xref: /haiku/src/apps/haikudepot/ui/FeaturedPackagesView.cpp (revision fa5c8097c9eddf48e05e70cd0dc6b389532fcaa3)
1f545fe6aSStephan Aßmus /*
2f545fe6aSStephan Aßmus  * Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>.
3d45104b1SJulian Harnath  * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
41f3909adSAndrew Lindesay  * Copyright 2020, Andrew Lindesay <apl@lindesay.co.nz>.
5f545fe6aSStephan Aßmus  * All rights reserved. Distributed under the terms of the MIT License.
6f545fe6aSStephan Aßmus  */
7f545fe6aSStephan Aßmus 
8f545fe6aSStephan Aßmus #include "FeaturedPackagesView.h"
9f545fe6aSStephan Aßmus 
109883929bSAndrew Lindesay #include <algorithm>
119883929bSAndrew Lindesay #include <vector>
12f545fe6aSStephan Aßmus 
139883929bSAndrew Lindesay #include <Bitmap.h>
14f545fe6aSStephan Aßmus #include <Catalog.h>
15f545fe6aSStephan Aßmus #include <Font.h>
16f545fe6aSStephan Aßmus #include <LayoutBuilder.h>
179883929bSAndrew Lindesay #include <LayoutItem.h>
18f545fe6aSStephan Aßmus #include <Message.h>
19f545fe6aSStephan Aßmus #include <ScrollView.h>
20f545fe6aSStephan Aßmus #include <StringView.h>
21f545fe6aSStephan Aßmus #include <SpaceLayoutItem.h>
22f545fe6aSStephan Aßmus 
23f545fe6aSStephan Aßmus #include "BitmapView.h"
24a9edb9bfSAndrew Lindesay #include "HaikuDepotConstants.h"
25f96d1f4dSAndrew Lindesay #include "Logger.h"
261f3c1ef1SStephan Aßmus #include "MainWindow.h"
276f6ad915SStephan Aßmus #include "MarkupTextView.h"
28f545fe6aSStephan Aßmus #include "MessagePackageListener.h"
299883929bSAndrew Lindesay #include "RatingUtils.h"
30f545fe6aSStephan Aßmus #include "RatingView.h"
31f545fe6aSStephan Aßmus #include "ScrollableGroupView.h"
329883929bSAndrew Lindesay #include "SharedBitmap.h"
33f545fe6aSStephan Aßmus 
34f545fe6aSStephan Aßmus 
35f545fe6aSStephan Aßmus #undef B_TRANSLATION_CONTEXT
36f545fe6aSStephan Aßmus #define B_TRANSLATION_CONTEXT "FeaturedPackagesView"
37f545fe6aSStephan Aßmus 
38f545fe6aSStephan Aßmus 
399883929bSAndrew Lindesay #define HEIGHT_PACKAGE 84.0f
409883929bSAndrew Lindesay #define SIZE_ICON 64.0f
419883929bSAndrew Lindesay #define X_POSITION_RATING 350.0f
429883929bSAndrew Lindesay #define X_POSITION_SUMMARY 500.0f
439883929bSAndrew Lindesay #define WIDTH_RATING 100.0f
449883929bSAndrew Lindesay #define Y_PROPORTION_TITLE 0.4f
459883929bSAndrew Lindesay #define Y_PROPORTION_PUBLISHER 0.7f
469883929bSAndrew Lindesay #define PADDING 8.0f
479883929bSAndrew Lindesay 
489883929bSAndrew Lindesay 
49a9edb9bfSAndrew Lindesay static BitmapRef sInstalledIcon(new(std::nothrow)
50a9edb9bfSAndrew Lindesay 	SharedBitmap(RSRC_INSTALLED), true);
51056d423cSStephan Aßmus 
52f545fe6aSStephan Aßmus 
53f545fe6aSStephan Aßmus // #pragma mark - PackageView
54f545fe6aSStephan Aßmus 
55f545fe6aSStephan Aßmus 
569883929bSAndrew Lindesay class StackedFeaturedPackagesView : public BView {
57f545fe6aSStephan Aßmus public:
589883929bSAndrew Lindesay 	StackedFeaturedPackagesView()
59f545fe6aSStephan Aßmus 		:
609883929bSAndrew Lindesay 		BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS),
613d869af5SStephan Aßmus 		fPackageListener(
62664372abSStephan Aßmus 			new(std::nothrow) OnePackageMessagePackageListener(this)),
639883929bSAndrew Lindesay 		fSelectedIndex(-1)
64f545fe6aSStephan Aßmus 	{
651f3c1ef1SStephan Aßmus 		SetEventMask(B_POINTER_EVENTS);
66f545fe6aSStephan Aßmus 		Clear();
67f545fe6aSStephan Aßmus 	}
68f545fe6aSStephan Aßmus 
699883929bSAndrew Lindesay 
709883929bSAndrew Lindesay 	virtual ~StackedFeaturedPackagesView()
71f545fe6aSStephan Aßmus 	{
72f545fe6aSStephan Aßmus 		fPackageListener->SetPackage(PackageInfoRef(NULL));
731f3909adSAndrew Lindesay 		fPackageListener->ReleaseReference();
74f545fe6aSStephan Aßmus 	}
75f545fe6aSStephan Aßmus 
769883929bSAndrew Lindesay // #pragma mark - message handling and events
77fa19dd44Slooncraz 
78f545fe6aSStephan Aßmus 	virtual void MessageReceived(BMessage* message)
79f545fe6aSStephan Aßmus 	{
80f545fe6aSStephan Aßmus 		switch (message->what) {
81f545fe6aSStephan Aßmus 			case MSG_UPDATE_PACKAGE:
82d45104b1SJulian Harnath 			{
839883929bSAndrew Lindesay 				BString name;
84*fa5c8097SAndrew Lindesay 				if (message->FindString("name", &name) != B_OK)
85*fa5c8097SAndrew Lindesay 					HDINFO("expected 'name' key on package update message");
869883929bSAndrew Lindesay 				else
879883929bSAndrew Lindesay 					_HandleUpdatePackage(name);
88d45104b1SJulian Harnath 				break;
89d45104b1SJulian Harnath 			}
90fa19dd44Slooncraz 
91fa19dd44Slooncraz 			case B_COLORS_UPDATED:
92fa19dd44Slooncraz 			{
939883929bSAndrew Lindesay 				Invalidate();
94fa19dd44Slooncraz 				break;
95fa19dd44Slooncraz 			}
961187e806SAndrew Lindesay 
971187e806SAndrew Lindesay 			default:
981187e806SAndrew Lindesay 				BView::MessageReceived(message);
991187e806SAndrew Lindesay 				break;
100f545fe6aSStephan Aßmus 		}
101f545fe6aSStephan Aßmus 	}
102f545fe6aSStephan Aßmus 
1039883929bSAndrew Lindesay 
1041f3c1ef1SStephan Aßmus 	virtual void MouseDown(BPoint where)
1051f3c1ef1SStephan Aßmus 	{
1069883929bSAndrew Lindesay 		if (Window()->IsActive() && !IsHidden()) {
107dfbf4d38SStephan Aßmus 			BRect bounds = Bounds();
108dfbf4d38SStephan Aßmus 			BRect parentBounds = Parent()->Bounds();
109dfbf4d38SStephan Aßmus 			ConvertFromParent(&parentBounds);
110dfbf4d38SStephan Aßmus 			bounds = bounds & parentBounds;
1119883929bSAndrew Lindesay 			if (bounds.Contains(where)) {
1129883929bSAndrew Lindesay 				_MessageSelectIndex(_IndexOfY(where.y));
1139883929bSAndrew Lindesay 				MakeFocus();
1149883929bSAndrew Lindesay 			}
1159883929bSAndrew Lindesay 		}
1169883929bSAndrew Lindesay 	}
117dfbf4d38SStephan Aßmus 
1189883929bSAndrew Lindesay 
1199883929bSAndrew Lindesay 	virtual void KeyDown(const char* bytes, int32 numBytes)
1209883929bSAndrew Lindesay 	{
1219883929bSAndrew Lindesay 		char key = bytes[0];
1229883929bSAndrew Lindesay 
1239883929bSAndrew Lindesay 		switch (key) {
1249883929bSAndrew Lindesay 			case B_RIGHT_ARROW:
1259883929bSAndrew Lindesay 			case B_DOWN_ARROW:
1269883929bSAndrew Lindesay 				if (!IsEmpty() && fSelectedIndex != -1
1279883929bSAndrew Lindesay 						&& fSelectedIndex < fPackages.size() - 1) {
1289883929bSAndrew Lindesay 					_MessageSelectIndex(fSelectedIndex + 1);
1299883929bSAndrew Lindesay 				}
1309883929bSAndrew Lindesay 				break;
1319883929bSAndrew Lindesay 			case B_LEFT_ARROW:
1329883929bSAndrew Lindesay 			case B_UP_ARROW:
1339883929bSAndrew Lindesay 				if (fSelectedIndex > 0)
1349883929bSAndrew Lindesay 					_MessageSelectIndex( fSelectedIndex - 1);
1359883929bSAndrew Lindesay 				break;
1369883929bSAndrew Lindesay 			case B_PAGE_UP:
1379883929bSAndrew Lindesay 			{
1389883929bSAndrew Lindesay 				BRect bounds = Bounds();
1399883929bSAndrew Lindesay 				ScrollTo(0, fmaxf(0, bounds.top - bounds.Height()));
1409883929bSAndrew Lindesay 				break;
1419883929bSAndrew Lindesay 			}
1429883929bSAndrew Lindesay 			case B_PAGE_DOWN:
1439883929bSAndrew Lindesay 			{
1449883929bSAndrew Lindesay 				BRect bounds = Bounds();
1459883929bSAndrew Lindesay 				float height = fPackages.size() * HEIGHT_PACKAGE;
1469883929bSAndrew Lindesay 				float maxScrollY = height - bounds.Height();
1479883929bSAndrew Lindesay 				float pageDownScrollY = bounds.top + bounds.Height();
1489883929bSAndrew Lindesay 				ScrollTo(0, fminf(maxScrollY, pageDownScrollY));
1499883929bSAndrew Lindesay 				break;
1509883929bSAndrew Lindesay 			}
1519883929bSAndrew Lindesay 			default:
1529883929bSAndrew Lindesay 				BView::KeyDown(bytes, numBytes);
1539883929bSAndrew Lindesay 				break;
1549883929bSAndrew Lindesay 		}
1559883929bSAndrew Lindesay 	}
1569883929bSAndrew Lindesay 
1579883929bSAndrew Lindesay 
1589883929bSAndrew Lindesay 	/*!	This method will send a message to the Window so that it can signal
1599883929bSAndrew Lindesay 		back to this and other views that a package has been selected.  This
1609883929bSAndrew Lindesay 		method won't actually change the state of this view directly.
1619883929bSAndrew Lindesay 	*/
1629883929bSAndrew Lindesay 
1639883929bSAndrew Lindesay 	void _MessageSelectIndex(int32 index) const
1649883929bSAndrew Lindesay 	{
1659883929bSAndrew Lindesay 		if (index != -1) {
1661f3c1ef1SStephan Aßmus 			BMessage message(MSG_PACKAGE_SELECTED);
1679883929bSAndrew Lindesay 			message.AddString("name", fPackages[index]->Name());
1681f3c1ef1SStephan Aßmus 			Window()->PostMessage(&message);
1691f3c1ef1SStephan Aßmus 		}
1701f3c1ef1SStephan Aßmus 	}
1711f3c1ef1SStephan Aßmus 
1729883929bSAndrew Lindesay 
1739883929bSAndrew Lindesay 	virtual void FrameResized(float width, float height)
174f545fe6aSStephan Aßmus 	{
1759883929bSAndrew Lindesay 		BView::FrameResized(width, height);
176f545fe6aSStephan Aßmus 
1779883929bSAndrew Lindesay 		// because the summary text will wrap, a resize of the frame will
1789883929bSAndrew Lindesay 		// result in all of the summary area needing to be redrawn.
179056d423cSStephan Aßmus 
1809883929bSAndrew Lindesay 		BRect rectToInvalidate = Bounds();
1819883929bSAndrew Lindesay 		rectToInvalidate.left = X_POSITION_SUMMARY;
1829883929bSAndrew Lindesay 		Invalidate(rectToInvalidate);
183f545fe6aSStephan Aßmus 	}
184f545fe6aSStephan Aßmus 
1859883929bSAndrew Lindesay 
1869883929bSAndrew Lindesay // #pragma mark - update / add / remove / clear data
1879883929bSAndrew Lindesay 
1889883929bSAndrew Lindesay 
189d45104b1SJulian Harnath 	void UpdatePackage(uint32 changeMask, const PackageInfoRef& package)
190d45104b1SJulian Harnath 	{
1919883929bSAndrew Lindesay 		// TODO; could optimize the invalidation?
1929883929bSAndrew Lindesay 		int32 index = _IndexOfPackage(package);
1939883929bSAndrew Lindesay 		if (index >= 0) {
1949883929bSAndrew Lindesay 			fPackages[index] = package;
1959883929bSAndrew Lindesay 			Invalidate(_RectOfIndex(index));
196d45104b1SJulian Harnath 		}
1979883929bSAndrew Lindesay 	}
1989883929bSAndrew Lindesay 
199d45104b1SJulian Harnath 
200f545fe6aSStephan Aßmus 	void Clear()
201f545fe6aSStephan Aßmus 	{
202c4199192SAndrew Lindesay 		for (std::vector<PackageInfoRef>::iterator it = fPackages.begin();
203c4199192SAndrew Lindesay 				it != fPackages.end(); it++) {
204c4199192SAndrew Lindesay 			(*it)->RemoveListener(fPackageListener);
205c4199192SAndrew Lindesay 		}
2069883929bSAndrew Lindesay 		fPackages.clear();
2079883929bSAndrew Lindesay 		fSelectedIndex = -1;
2089883929bSAndrew Lindesay 		Invalidate();
209f545fe6aSStephan Aßmus 	}
210f545fe6aSStephan Aßmus 
2119883929bSAndrew Lindesay 
2129883929bSAndrew Lindesay 	bool IsEmpty() const
2134b930ccaSStephan Aßmus 	{
2149883929bSAndrew Lindesay 		return fPackages.size() == 0;
2154b930ccaSStephan Aßmus 	}
2164b930ccaSStephan Aßmus 
2179883929bSAndrew Lindesay 
2189883929bSAndrew Lindesay 	void _HandleUpdatePackage(const BString& name)
21972992391SStephan Aßmus 	{
2209883929bSAndrew Lindesay 		int32 index = _IndexOfName(name);
2219883929bSAndrew Lindesay 		if (index != -1)
2229883929bSAndrew Lindesay 			Invalidate(_RectOfIndex(index));
22372992391SStephan Aßmus 	}
22472992391SStephan Aßmus 
2259883929bSAndrew Lindesay 
2269883929bSAndrew Lindesay 	/*! This method will return true if the packageA is ordered before
2279883929bSAndrew Lindesay 		packageB.
2289883929bSAndrew Lindesay 	*/
2299883929bSAndrew Lindesay 
2309883929bSAndrew Lindesay 	static bool _IsPackageBefore(const PackageInfoRef& packageA,
2319883929bSAndrew Lindesay 		const PackageInfoRef& packageB)
232664372abSStephan Aßmus 	{
2339883929bSAndrew Lindesay 		if (packageA.Get() == NULL || packageB.Get() == NULL)
2349883929bSAndrew Lindesay 			debugger("unexpected NULL reference in a referencable");
2359883929bSAndrew Lindesay 		int c = packageA->Title().ICompare(packageB->Title());
2369883929bSAndrew Lindesay 		if (c == 0)
2379883929bSAndrew Lindesay 			c = packageA->Name().Compare(packageB->Name());
2389883929bSAndrew Lindesay 		return c < 0;
239fa19dd44Slooncraz 	}
240fa19dd44Slooncraz 
2419883929bSAndrew Lindesay 	void AddPackage(const PackageInfoRef& package)
242fa19dd44Slooncraz 	{
2439883929bSAndrew Lindesay 		// fPackages is sorted and for this reason it is possible to find the
2449883929bSAndrew Lindesay 		// insertion point by identifying the first item in fPackages that does
2459883929bSAndrew Lindesay 		// not return true from the method '_IsPackageBefore'.
246fa19dd44Slooncraz 
2474d2de4eaSAugustin Cavalier 		std::vector<PackageInfoRef>::iterator itInsertionPt
2489883929bSAndrew Lindesay 			= std::lower_bound(fPackages.begin(), fPackages.end(), package,
2499883929bSAndrew Lindesay 				&_IsPackageBefore);
2509883929bSAndrew Lindesay 
2519883929bSAndrew Lindesay 		if (itInsertionPt == fPackages.end()
2529883929bSAndrew Lindesay 				|| package->Name() != (*itInsertionPt)->Name()) {
2539883929bSAndrew Lindesay 			int32 insertionIndex =
2549883929bSAndrew Lindesay 				std::distance<std::vector<PackageInfoRef>::const_iterator>(
2559883929bSAndrew Lindesay 					fPackages.begin(), itInsertionPt);
2569883929bSAndrew Lindesay 			if (fSelectedIndex >= insertionIndex)
2579883929bSAndrew Lindesay 				fSelectedIndex++;
2589883929bSAndrew Lindesay 			fPackages.insert(itInsertionPt, package);
2599883929bSAndrew Lindesay 			Invalidate(_RectOfIndex(insertionIndex)
2609883929bSAndrew Lindesay 				| _RectOfIndex(fPackages.size() - 1));
261c4199192SAndrew Lindesay 			package->AddListener(fPackageListener);
2629883929bSAndrew Lindesay 		}
263fa19dd44Slooncraz 	}
264664372abSStephan Aßmus 
265664372abSStephan Aßmus 
2669883929bSAndrew Lindesay 	void RemovePackage(const PackageInfoRef& package)
267d45104b1SJulian Harnath 	{
2689883929bSAndrew Lindesay 		int32 index = _IndexOfPackage(package);
2699883929bSAndrew Lindesay 		if (index >= 0) {
2709883929bSAndrew Lindesay 			if (fSelectedIndex == index)
2719883929bSAndrew Lindesay 				fSelectedIndex = -1;
2729883929bSAndrew Lindesay 			if (fSelectedIndex > index)
2739883929bSAndrew Lindesay 				fSelectedIndex--;
274c4199192SAndrew Lindesay 			fPackages[index]->RemoveListener(fPackageListener);
2759883929bSAndrew Lindesay 			fPackages.erase(fPackages.begin() + index);
2769883929bSAndrew Lindesay 			if (fPackages.empty())
2779883929bSAndrew Lindesay 				Invalidate();
2789883929bSAndrew Lindesay 			else {
2799883929bSAndrew Lindesay 				Invalidate(_RectOfIndex(index)
2809883929bSAndrew Lindesay 					| _RectOfIndex(fPackages.size() - 1));
2819883929bSAndrew Lindesay 			}
2829883929bSAndrew Lindesay 		}
2839883929bSAndrew Lindesay 	}
284d45104b1SJulian Harnath 
285d45104b1SJulian Harnath 
2869883929bSAndrew Lindesay // #pragma mark - selection and index handling
287d45104b1SJulian Harnath 
288d45104b1SJulian Harnath 
2899883929bSAndrew Lindesay 	void SelectPackage(const PackageInfoRef& package)
2909883929bSAndrew Lindesay 	{
2919883929bSAndrew Lindesay 		_SelectIndex(_IndexOfPackage(package));
2929883929bSAndrew Lindesay 	}
2939883929bSAndrew Lindesay 
2949883929bSAndrew Lindesay 
2959883929bSAndrew Lindesay 	void _SelectIndex(int32 index)
2969883929bSAndrew Lindesay 	{
2979883929bSAndrew Lindesay 		if (index != fSelectedIndex) {
2989883929bSAndrew Lindesay 			int32 previousSelectedIndex = fSelectedIndex;
2999883929bSAndrew Lindesay 			fSelectedIndex = index;
3009883929bSAndrew Lindesay 			if (fSelectedIndex >= 0)
3019883929bSAndrew Lindesay 				Invalidate(_RectOfIndex(fSelectedIndex));
3029883929bSAndrew Lindesay 			if (previousSelectedIndex >= 0)
3039883929bSAndrew Lindesay 				Invalidate(_RectOfIndex(previousSelectedIndex));
3049883929bSAndrew Lindesay 			_EnsureIndexVisible(index);
3059883929bSAndrew Lindesay 		}
3069883929bSAndrew Lindesay 	}
3079883929bSAndrew Lindesay 
3089883929bSAndrew Lindesay 
3099883929bSAndrew Lindesay 	int32 _IndexOfPackage(PackageInfoRef package) const
3109883929bSAndrew Lindesay 	{
311c4199192SAndrew Lindesay 		std::vector<PackageInfoRef>::const_iterator it
312c4199192SAndrew Lindesay 			= std::lower_bound(fPackages.begin(), fPackages.end(), package,
313c4199192SAndrew Lindesay 				&_IsPackageBefore);
314c4199192SAndrew Lindesay 
315c4199192SAndrew Lindesay 		return (it == fPackages.end() || (*it)->Name() != package->Name())
316c4199192SAndrew Lindesay 			? -1 : it - fPackages.begin();
3179883929bSAndrew Lindesay 	}
3189883929bSAndrew Lindesay 
3199883929bSAndrew Lindesay 
3209883929bSAndrew Lindesay 	int32 _IndexOfName(const BString& name) const
3219883929bSAndrew Lindesay 	{
3229883929bSAndrew Lindesay 		// TODO; slow linear search.
3239883929bSAndrew Lindesay 		// the fPackages is not sorted on name and for this reason it is not
3249883929bSAndrew Lindesay 		// possible to do a binary search.
3259883929bSAndrew Lindesay 		for (uint32 i = 0; i < fPackages.size(); i++) {
3269883929bSAndrew Lindesay 			if (fPackages[i]->Name() == name)
3279883929bSAndrew Lindesay 				return i;
3289883929bSAndrew Lindesay 		}
3299883929bSAndrew Lindesay 		return -1;
3309883929bSAndrew Lindesay 	}
3319883929bSAndrew Lindesay 
3329883929bSAndrew Lindesay 
3339883929bSAndrew Lindesay // #pragma mark - drawing and rendering
3349883929bSAndrew Lindesay 
3359883929bSAndrew Lindesay 
3369883929bSAndrew Lindesay 	virtual void Draw(BRect updateRect)
3379883929bSAndrew Lindesay 	{
3389883929bSAndrew Lindesay 		SetHighUIColor(B_LIST_BACKGROUND_COLOR);
3399883929bSAndrew Lindesay 		FillRect(updateRect);
3409883929bSAndrew Lindesay 
3419883929bSAndrew Lindesay 		int32 iStart = _IndexRoundedOfY(updateRect.top);
3429883929bSAndrew Lindesay 
3439883929bSAndrew Lindesay 		if (iStart != -1) {
3449883929bSAndrew Lindesay 			int32 iEnd = _IndexRoundedOfY(updateRect.bottom);
3459883929bSAndrew Lindesay 			for (int32 i = iStart; i <= iEnd; i++)
3469883929bSAndrew Lindesay 				_DrawPackageAtIndex(updateRect, i);
3479883929bSAndrew Lindesay 		}
3489883929bSAndrew Lindesay 	}
3499883929bSAndrew Lindesay 
3509883929bSAndrew Lindesay 
3519883929bSAndrew Lindesay 	void _DrawPackageAtIndex(BRect updateRect, int32 index)
3529883929bSAndrew Lindesay 	{
3539883929bSAndrew Lindesay 		_DrawPackage(updateRect, fPackages[index], index, _YOfIndex(index),
3549883929bSAndrew Lindesay 			index == fSelectedIndex);
3559883929bSAndrew Lindesay 	}
3569883929bSAndrew Lindesay 
3579883929bSAndrew Lindesay 
3589883929bSAndrew Lindesay 	void _DrawPackage(BRect updateRect, PackageInfoRef pkg, int index, float y,
3599883929bSAndrew Lindesay 		bool selected)
3609883929bSAndrew Lindesay 	{
3619883929bSAndrew Lindesay 		if (selected) {
3629883929bSAndrew Lindesay 			SetLowUIColor(B_LIST_SELECTED_BACKGROUND_COLOR);
3639883929bSAndrew Lindesay 			FillRect(_RectOfY(y), B_SOLID_LOW);
364d45104b1SJulian Harnath 		} else {
3659883929bSAndrew Lindesay 			SetLowUIColor(B_LIST_BACKGROUND_COLOR);
366d45104b1SJulian Harnath 		}
3679883929bSAndrew Lindesay 		// TODO; optimization; the updateRect may only cover some of this?
3689883929bSAndrew Lindesay 		_DrawPackageIcon(updateRect, pkg, y, selected);
3699883929bSAndrew Lindesay 		_DrawPackageTitle(updateRect, pkg, y, selected);
3709883929bSAndrew Lindesay 		_DrawPackagePublisher(updateRect, pkg, y, selected);
3719883929bSAndrew Lindesay 		_DrawPackageRating(updateRect, pkg, y, selected);
3729883929bSAndrew Lindesay 		_DrawPackageSummary(updateRect, pkg, y, selected);
373d45104b1SJulian Harnath 	}
374d45104b1SJulian Harnath 
375d45104b1SJulian Harnath 
3769883929bSAndrew Lindesay 	void _DrawPackageIcon(BRect updateRect, PackageInfoRef pkg, float y,
3779883929bSAndrew Lindesay 		bool selected)
378d45104b1SJulian Harnath 	{
3799883929bSAndrew Lindesay 		BitmapRef icon = pkg->Icon();
3809883929bSAndrew Lindesay 
381d45104b1SJulian Harnath 		if (icon.Get() != NULL) {
3829883929bSAndrew Lindesay 			float inset = (HEIGHT_PACKAGE - SIZE_ICON) / 2.0;
3839883929bSAndrew Lindesay 			BRect sourceRect = BRect(0, 0, SIZE_ICON, SIZE_ICON);
3849883929bSAndrew Lindesay 			BRect targetRect = BRect(inset, y + inset, SIZE_ICON + inset,
3859883929bSAndrew Lindesay 				y + SIZE_ICON + inset);
3869883929bSAndrew Lindesay 			const BBitmap* bitmap = icon->Bitmap(SharedBitmap::SIZE_64);
3879883929bSAndrew Lindesay 			SetDrawingMode(B_OP_ALPHA);
3889883929bSAndrew Lindesay 			DrawBitmap(bitmap, bitmap->Bounds(), targetRect,
3899883929bSAndrew Lindesay 				B_FILTER_BITMAP_BILINEAR);
390d45104b1SJulian Harnath 		}
3919883929bSAndrew Lindesay 	}
3929883929bSAndrew Lindesay 
3939883929bSAndrew Lindesay 
3949883929bSAndrew Lindesay 	void _DrawPackageTitle(BRect updateRect, PackageInfoRef pkg, float y,
3959883929bSAndrew Lindesay 		bool selected)
3969883929bSAndrew Lindesay 	{
3979883929bSAndrew Lindesay 		static BFont* sFont = NULL;
3989883929bSAndrew Lindesay 
3999883929bSAndrew Lindesay 		if (sFont == NULL) {
4009883929bSAndrew Lindesay 			sFont = new BFont(be_plain_font);
4019883929bSAndrew Lindesay 			GetFont(sFont);
4029883929bSAndrew Lindesay   			font_family family;
4039883929bSAndrew Lindesay 			font_style style;
4049883929bSAndrew Lindesay 			sFont->SetSize(ceilf(sFont->Size() * 1.8f));
4059883929bSAndrew Lindesay 			sFont->GetFamilyAndStyle(&family, &style);
4069883929bSAndrew Lindesay 			sFont->SetFamilyAndStyle(family, "Bold");
4079883929bSAndrew Lindesay 		}
4089883929bSAndrew Lindesay 
4099883929bSAndrew Lindesay 		SetDrawingMode(B_OP_COPY);
4109883929bSAndrew Lindesay 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
4119883929bSAndrew Lindesay 			: B_LIST_ITEM_TEXT_COLOR);
4129883929bSAndrew Lindesay 		SetFont(sFont);
4139883929bSAndrew Lindesay 		BPoint pt(HEIGHT_PACKAGE, y + (HEIGHT_PACKAGE * Y_PROPORTION_TITLE));
4149883929bSAndrew Lindesay 		DrawString(pkg->Title(), pt);
4159883929bSAndrew Lindesay 
4169883929bSAndrew Lindesay 		if (pkg->State() == ACTIVATED) {
4179883929bSAndrew Lindesay 			const BBitmap* bitmap = sInstalledIcon->Bitmap(
4189883929bSAndrew Lindesay 				SharedBitmap::SIZE_16);
4199883929bSAndrew Lindesay 			float stringWidth = StringWidth(pkg->Title());
4209883929bSAndrew Lindesay 			float offsetX = pt.x + stringWidth + PADDING;
4219883929bSAndrew Lindesay 			BRect targetRect(offsetX, pt.y - 16, offsetX + 16, pt.y);
4229883929bSAndrew Lindesay 			SetDrawingMode(B_OP_ALPHA);
4239883929bSAndrew Lindesay 			DrawBitmap(bitmap, bitmap->Bounds(), targetRect,
4249883929bSAndrew Lindesay 				B_FILTER_BITMAP_BILINEAR);
4259883929bSAndrew Lindesay 		}
4269883929bSAndrew Lindesay 	}
4279883929bSAndrew Lindesay 
4289883929bSAndrew Lindesay 
4299883929bSAndrew Lindesay 	void _DrawPackagePublisher(BRect updateRect, PackageInfoRef pkg, float y,
4309883929bSAndrew Lindesay 		bool selected)
4319883929bSAndrew Lindesay 	{
4329883929bSAndrew Lindesay 		static BFont* sFont = NULL;
4339883929bSAndrew Lindesay 
4349883929bSAndrew Lindesay 		if (sFont == NULL) {
4359883929bSAndrew Lindesay 			sFont = new BFont(be_plain_font);
4369883929bSAndrew Lindesay 			font_family family;
4379883929bSAndrew Lindesay 			font_style style;
4389883929bSAndrew Lindesay 			sFont->SetSize(std::max(9.0f, floorf(sFont->Size() * 0.92f)));
4399883929bSAndrew Lindesay 			sFont->GetFamilyAndStyle(&family, &style);
4409883929bSAndrew Lindesay 			sFont->SetFamilyAndStyle(family, "Italic");
4419883929bSAndrew Lindesay 		}
4429883929bSAndrew Lindesay 
4439883929bSAndrew Lindesay 		SetDrawingMode(B_OP_COPY);
4449883929bSAndrew Lindesay 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
4459883929bSAndrew Lindesay 			: B_LIST_ITEM_TEXT_COLOR);
4469883929bSAndrew Lindesay 		SetFont(sFont);
4479883929bSAndrew Lindesay 
4489883929bSAndrew Lindesay 		float maxTextWidth = (X_POSITION_RATING - HEIGHT_PACKAGE) - PADDING;
4499883929bSAndrew Lindesay 		BString publisherName(pkg->Publisher().Name());
4509883929bSAndrew Lindesay 		TruncateString(&publisherName, B_TRUNCATE_END, maxTextWidth);
4519883929bSAndrew Lindesay 
4529883929bSAndrew Lindesay 		DrawString(publisherName, BPoint(HEIGHT_PACKAGE,
4539883929bSAndrew Lindesay 			y + (HEIGHT_PACKAGE * Y_PROPORTION_PUBLISHER)));
4549883929bSAndrew Lindesay 	}
4559883929bSAndrew Lindesay 
4569883929bSAndrew Lindesay 
4579883929bSAndrew Lindesay 	// TODO; show the sample size
4589883929bSAndrew Lindesay 	void _DrawPackageRating(BRect updateRect, PackageInfoRef pkg, float y,
4599883929bSAndrew Lindesay 		bool selected)
4609883929bSAndrew Lindesay 	{
4619883929bSAndrew Lindesay 		BPoint at(X_POSITION_RATING,
4629883929bSAndrew Lindesay 			y + (HEIGHT_PACKAGE - SIZE_RATING_STAR) / 2.0f);
4639883929bSAndrew Lindesay 		RatingUtils::Draw(this, at,
4649883929bSAndrew Lindesay 			pkg->CalculateRatingSummary().averageRating);
4659883929bSAndrew Lindesay 	}
4669883929bSAndrew Lindesay 
4679883929bSAndrew Lindesay 
4689883929bSAndrew Lindesay 	// TODO; handle multi-line rendering of the text
4699883929bSAndrew Lindesay 	void _DrawPackageSummary(BRect updateRect, PackageInfoRef pkg, float y,
4709883929bSAndrew Lindesay 		bool selected)
4719883929bSAndrew Lindesay 	{
4729883929bSAndrew Lindesay 		BRect bounds = Bounds();
4739883929bSAndrew Lindesay 
4749883929bSAndrew Lindesay 		SetDrawingMode(B_OP_COPY);
4759883929bSAndrew Lindesay 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
4769883929bSAndrew Lindesay 			: B_LIST_ITEM_TEXT_COLOR);
4779883929bSAndrew Lindesay 		SetFont(be_plain_font);
4789883929bSAndrew Lindesay 
4799883929bSAndrew Lindesay 		float maxTextWidth = bounds.Width() - X_POSITION_SUMMARY - PADDING;
4809883929bSAndrew Lindesay 		BString summary(pkg->ShortDescription());
4819883929bSAndrew Lindesay 		TruncateString(&summary, B_TRUNCATE_END, maxTextWidth);
4829883929bSAndrew Lindesay 
4839883929bSAndrew Lindesay 		DrawString(summary, BPoint(X_POSITION_SUMMARY,
4849883929bSAndrew Lindesay 			y + (HEIGHT_PACKAGE * 0.5)));
4859883929bSAndrew Lindesay 	}
4869883929bSAndrew Lindesay 
4879883929bSAndrew Lindesay 
4889883929bSAndrew Lindesay // #pragma mark - geometry and scrolling
4899883929bSAndrew Lindesay 
4909883929bSAndrew Lindesay 
4919883929bSAndrew Lindesay 	/*!	This method will make sure that the package at the given index is
4929883929bSAndrew Lindesay 		visible.  If the whole of the package can be seen already then it will
4939883929bSAndrew Lindesay 		do nothing.  If the package is located above the visible region then it
4949883929bSAndrew Lindesay 		will scroll up to it.  If the package is located below the visible
4959883929bSAndrew Lindesay 		region then it will scroll down to it.
4969883929bSAndrew Lindesay 	*/
4979883929bSAndrew Lindesay 
4989883929bSAndrew Lindesay 	void _EnsureIndexVisible(int32 index)
4999883929bSAndrew Lindesay 	{
5009883929bSAndrew Lindesay 		if (!_IsIndexEntirelyVisible(index)) {
5019883929bSAndrew Lindesay 			BRect bounds = Bounds();
5029883929bSAndrew Lindesay 			int32 indexOfCentreVisible = _IndexOfY(
5039883929bSAndrew Lindesay 				bounds.top + bounds.Height() / 2);
5049883929bSAndrew Lindesay 			if (index < indexOfCentreVisible)
5059883929bSAndrew Lindesay 				ScrollTo(0, _YOfIndex(index));
5069883929bSAndrew Lindesay 			else {
5079883929bSAndrew Lindesay 				float scrollPointY = (_YOfIndex(index) + HEIGHT_PACKAGE)
5089883929bSAndrew Lindesay 					- bounds.Height();
5099883929bSAndrew Lindesay 				ScrollTo(0, scrollPointY);
5109883929bSAndrew Lindesay 			}
5119883929bSAndrew Lindesay 		}
5129883929bSAndrew Lindesay 	}
5139883929bSAndrew Lindesay 
5149883929bSAndrew Lindesay 
5159883929bSAndrew Lindesay 	/*!	This method will return true if the package at the supplied index is
5169883929bSAndrew Lindesay 		entirely visible.
5179883929bSAndrew Lindesay 	*/
5189883929bSAndrew Lindesay 
5199883929bSAndrew Lindesay 	bool _IsIndexEntirelyVisible(int32 index)
5209883929bSAndrew Lindesay 	{
5219883929bSAndrew Lindesay 		BRect bounds = Bounds();
5229883929bSAndrew Lindesay 		return bounds == (bounds | _RectOfIndex(index));
5239883929bSAndrew Lindesay 	}
5249883929bSAndrew Lindesay 
5259883929bSAndrew Lindesay 
5269883929bSAndrew Lindesay 	BRect _RectOfIndex(int32 index) const
5279883929bSAndrew Lindesay 	{
5289883929bSAndrew Lindesay 		if (index < 0)
5299883929bSAndrew Lindesay 			return BRect(0, 0, 0, 0);
5309883929bSAndrew Lindesay 		return _RectOfY(_YOfIndex(index));
5319883929bSAndrew Lindesay 	}
5329883929bSAndrew Lindesay 
5339883929bSAndrew Lindesay 
5349883929bSAndrew Lindesay 	/*!	Provides the top coordinate (offset from the top of view) of the package
5359883929bSAndrew Lindesay 		supplied.  If the package does not exist in the view then the coordinate
5369883929bSAndrew Lindesay 		returned will be B_SIZE_UNSET.
5379883929bSAndrew Lindesay 	*/
5389883929bSAndrew Lindesay 
5399883929bSAndrew Lindesay 	float TopOfPackage(const PackageInfoRef& package)
5409883929bSAndrew Lindesay 	{
5419883929bSAndrew Lindesay 		if (package.Get() != NULL) {
5429883929bSAndrew Lindesay 			int index = _IndexOfPackage(package);
5439883929bSAndrew Lindesay 			if (-1 != index)
5449883929bSAndrew Lindesay 				return _YOfIndex(index);
5459883929bSAndrew Lindesay 		}
5469883929bSAndrew Lindesay 		return B_SIZE_UNSET;
5479883929bSAndrew Lindesay 	}
5489883929bSAndrew Lindesay 
5499883929bSAndrew Lindesay 
5509883929bSAndrew Lindesay 	BRect _RectOfY(float y) const
5519883929bSAndrew Lindesay 	{
5529883929bSAndrew Lindesay 		return BRect(0, y, Bounds().Width(), y + HEIGHT_PACKAGE);
5539883929bSAndrew Lindesay 	}
5549883929bSAndrew Lindesay 
5559883929bSAndrew Lindesay 
5569883929bSAndrew Lindesay 	float _YOfIndex(int32 i) const
5579883929bSAndrew Lindesay 	{
5589883929bSAndrew Lindesay 		return i * HEIGHT_PACKAGE;
5599883929bSAndrew Lindesay 	}
5609883929bSAndrew Lindesay 
5619883929bSAndrew Lindesay 
5629883929bSAndrew Lindesay 	/*! Finds the offset into the list of packages for the y-coord in the view's
5639883929bSAndrew Lindesay 		coordinate space.  If the y is above or below the list of packages then
5649883929bSAndrew Lindesay 		this will return -1 to signal this.
5659883929bSAndrew Lindesay 	*/
5669883929bSAndrew Lindesay 
5679883929bSAndrew Lindesay 	int32 _IndexOfY(float y) const
5689883929bSAndrew Lindesay 	{
5699883929bSAndrew Lindesay 		if (fPackages.empty())
5709883929bSAndrew Lindesay 			return -1;
5719883929bSAndrew Lindesay 		int32 i = y / HEIGHT_PACKAGE;
5729883929bSAndrew Lindesay 		if (i < 0 || i >= fPackages.size())
5739883929bSAndrew Lindesay 			return -1;
5749883929bSAndrew Lindesay 		return i;
5759883929bSAndrew Lindesay 	}
5769883929bSAndrew Lindesay 
5779883929bSAndrew Lindesay 
5789883929bSAndrew Lindesay 	/*! Find the offset into the list of packages for the y-coord in the view's
5799883929bSAndrew Lindesay 		coordinate space.  If the y is above or below the list of packages then
5809883929bSAndrew Lindesay 		this will return the first or last package index respectively.  If there
5819883929bSAndrew Lindesay 		are no packages then this will return -1;
5829883929bSAndrew Lindesay 	*/
5839883929bSAndrew Lindesay 
5849883929bSAndrew Lindesay 	int32 _IndexRoundedOfY(float y) const
5859883929bSAndrew Lindesay 	{
5869883929bSAndrew Lindesay 		if (fPackages.empty())
5879883929bSAndrew Lindesay 			return -1;
5889883929bSAndrew Lindesay 		int32 i = y / HEIGHT_PACKAGE;
5899883929bSAndrew Lindesay 		if (i < 0)
5909883929bSAndrew Lindesay 			return 0;
5919883929bSAndrew Lindesay 		return std::min(i, (int32) (fPackages.size() - 1));
5929883929bSAndrew Lindesay 	}
5939883929bSAndrew Lindesay 
5949883929bSAndrew Lindesay 
5959883929bSAndrew Lindesay 	virtual BSize PreferredSize()
5969883929bSAndrew Lindesay 	{
5979883929bSAndrew Lindesay 		return BSize(B_SIZE_UNLIMITED, HEIGHT_PACKAGE * fPackages.size());
5989883929bSAndrew Lindesay 	}
5999883929bSAndrew Lindesay 
600d45104b1SJulian Harnath 
601f545fe6aSStephan Aßmus private:
6029883929bSAndrew Lindesay 			std::vector<PackageInfoRef>
6039883929bSAndrew Lindesay 								fPackages;
6049883929bSAndrew Lindesay 			int32				fSelectedIndex;
605f545fe6aSStephan Aßmus 
6069883929bSAndrew Lindesay 			OnePackageMessagePackageListener*
6079883929bSAndrew Lindesay 								fPackageListener;
608f545fe6aSStephan Aßmus };
609f545fe6aSStephan Aßmus 
610f545fe6aSStephan Aßmus 
611f545fe6aSStephan Aßmus // #pragma mark - FeaturedPackagesView
612f545fe6aSStephan Aßmus 
613f545fe6aSStephan Aßmus 
614f545fe6aSStephan Aßmus FeaturedPackagesView::FeaturedPackagesView()
615f545fe6aSStephan Aßmus 	:
616e00a489bSAugustin Cavalier 	BView(B_TRANSLATE("Featured packages"), 0)
617f545fe6aSStephan Aßmus {
6189883929bSAndrew Lindesay 	fPackagesView = new StackedFeaturedPackagesView();
619f545fe6aSStephan Aßmus 
6209883929bSAndrew Lindesay 	fScrollView = new BScrollView("featured packages scroll view",
621494bd147SAugustin Cavalier 		fPackagesView, 0, false, true, B_FANCY_BORDER);
6228507d262SStephan Aßmus 
623f545fe6aSStephan Aßmus 	BLayoutBuilder::Group<>(this)
6249883929bSAndrew Lindesay 		.Add(fScrollView, 1.0f);
625f545fe6aSStephan Aßmus }
626f545fe6aSStephan Aßmus 
627f545fe6aSStephan Aßmus 
628f545fe6aSStephan Aßmus FeaturedPackagesView::~FeaturedPackagesView()
629f545fe6aSStephan Aßmus {
630f545fe6aSStephan Aßmus }
631f545fe6aSStephan Aßmus 
632f545fe6aSStephan Aßmus 
633ccf707d0SAndrew Lindesay /*! This method will add the package into the list to be displayed.  The
634ccf707d0SAndrew Lindesay     insertion will occur in alphabetical order.
635ccf707d0SAndrew Lindesay */
636ccf707d0SAndrew Lindesay 
637f545fe6aSStephan Aßmus void
638f545fe6aSStephan Aßmus FeaturedPackagesView::AddPackage(const PackageInfoRef& package)
639f545fe6aSStephan Aßmus {
6409883929bSAndrew Lindesay 	fPackagesView->AddPackage(package);
6419883929bSAndrew Lindesay 	_AdjustViews();
642ccf707d0SAndrew Lindesay }
643f545fe6aSStephan Aßmus 
644f545fe6aSStephan Aßmus 
645f545fe6aSStephan Aßmus void
64618b941b4SStephan Aßmus FeaturedPackagesView::RemovePackage(const PackageInfoRef& package)
64718b941b4SStephan Aßmus {
6489883929bSAndrew Lindesay 	fPackagesView->RemovePackage(package);
6499883929bSAndrew Lindesay 	_AdjustViews();
65018b941b4SStephan Aßmus }
65118b941b4SStephan Aßmus 
65218b941b4SStephan Aßmus 
65318b941b4SStephan Aßmus void
654f545fe6aSStephan Aßmus FeaturedPackagesView::Clear()
655f545fe6aSStephan Aßmus {
656*fa5c8097SAndrew Lindesay 	HDINFO("did clear the featured packages view");
6579883929bSAndrew Lindesay 	fPackagesView->Clear();
6589883929bSAndrew Lindesay 	_AdjustViews();
659f545fe6aSStephan Aßmus }
660f545fe6aSStephan Aßmus 
661664372abSStephan Aßmus 
662664372abSStephan Aßmus void
66372fff6d3SJulian Harnath FeaturedPackagesView::SelectPackage(const PackageInfoRef& package,
66472fff6d3SJulian Harnath 	bool scrollToEntry)
665664372abSStephan Aßmus {
6669883929bSAndrew Lindesay 	fPackagesView->SelectPackage(package);
667ede65a8fSStephan Aßmus 
6689883929bSAndrew Lindesay 	if (scrollToEntry) {
6699883929bSAndrew Lindesay 		float offset = fPackagesView->TopOfPackage(package);
6709883929bSAndrew Lindesay 		if (offset != B_SIZE_UNSET)
6719883929bSAndrew Lindesay 			fPackagesView->ScrollTo(0, offset);
67272fff6d3SJulian Harnath 	}
673664372abSStephan Aßmus }
6749883929bSAndrew Lindesay 
6759883929bSAndrew Lindesay 
6769883929bSAndrew Lindesay void
6779883929bSAndrew Lindesay FeaturedPackagesView::DoLayout()
6789883929bSAndrew Lindesay {
6799883929bSAndrew Lindesay 	BView::DoLayout();
6809883929bSAndrew Lindesay 	_AdjustViews();
6819883929bSAndrew Lindesay }
6829883929bSAndrew Lindesay 
6839883929bSAndrew Lindesay 
6849883929bSAndrew Lindesay void
6859883929bSAndrew Lindesay FeaturedPackagesView::_AdjustViews()
6869883929bSAndrew Lindesay {
687494bd147SAugustin Cavalier 	fScrollView->FrameResized(fScrollView->Frame().Width(),
688494bd147SAugustin Cavalier 		fScrollView->Frame().Height());
689664372abSStephan Aßmus }
690664372abSStephan Aßmus 
691056d423cSStephan Aßmus 
692056d423cSStephan Aßmus void
693056d423cSStephan Aßmus FeaturedPackagesView::CleanupIcons()
694056d423cSStephan Aßmus {
695056d423cSStephan Aßmus 	sInstalledIcon.Unset();
696056d423cSStephan Aßmus }
697