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>.
466ee6532SAndrew Lindesay * Copyright 2020-2024, 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>
15c3cad236SAndrew Lindesay #include <ControlLook.h>
16f545fe6aSStephan Aßmus #include <Font.h>
17f545fe6aSStephan Aßmus #include <LayoutBuilder.h>
189883929bSAndrew Lindesay #include <LayoutItem.h>
19f545fe6aSStephan Aßmus #include <Message.h>
20f545fe6aSStephan Aßmus #include <ScrollView.h>
21f545fe6aSStephan Aßmus #include <StringView.h>
22f545fe6aSStephan Aßmus
23f545fe6aSStephan Aßmus #include "BitmapView.h"
24a9edb9bfSAndrew Lindesay #include "HaikuDepotConstants.h"
250d4fa3e5SAndrew Lindesay #include "LocaleUtils.h"
26f96d1f4dSAndrew Lindesay #include "Logger.h"
271f3c1ef1SStephan Aßmus #include "MainWindow.h"
286f6ad915SStephan Aßmus #include "MarkupTextView.h"
29f545fe6aSStephan Aßmus #include "MessagePackageListener.h"
30c65ff9f1SAndrew Lindesay #include "PackageUtils.h"
319883929bSAndrew Lindesay #include "RatingUtils.h"
32f545fe6aSStephan Aßmus #include "RatingView.h"
3366ee6532SAndrew Lindesay #include "SharedIcons.h"
34f545fe6aSStephan Aßmus
35f545fe6aSStephan Aßmus
36f545fe6aSStephan Aßmus #undef B_TRANSLATION_CONTEXT
37f545fe6aSStephan Aßmus #define B_TRANSLATION_CONTEXT "FeaturedPackagesView"
38f545fe6aSStephan Aßmus
399883929bSAndrew Lindesay #define SIZE_ICON 64.0f
40c3cad236SAndrew Lindesay
41c3cad236SAndrew Lindesay // If the space for the summary has less than this many "M" characters then the summary will not be
42c3cad236SAndrew Lindesay // displayed.
43c3cad236SAndrew Lindesay #define MINIMUM_M_COUNT_SUMMARY 10.0f
44c3cad236SAndrew Lindesay
45c3cad236SAndrew Lindesay // The title area will be this many times the width of an "M".
46c3cad236SAndrew Lindesay #define M_COUNT_TITLE 10
479883929bSAndrew Lindesay
48*6afa94a0SAndrew Lindesay // The fraction of the icon width that is left of the trailing icon
49*6afa94a0SAndrew Lindesay #define TRAILING_ICON_PADDING_LEFT_FACTOR 0.1
50*6afa94a0SAndrew Lindesay
51*6afa94a0SAndrew Lindesay // The faction of an M-space that is left between the title and the first trailing icon.
52*6afa94a0SAndrew Lindesay #define TITLE_RIGHT_TRAILING_ICON_PADDING_M_FACTOR 0.1
53*6afa94a0SAndrew Lindesay
549883929bSAndrew Lindesay
55f545fe6aSStephan Aßmus // #pragma mark - PackageView
56f545fe6aSStephan Aßmus
57f545fe6aSStephan Aßmus
58c3cad236SAndrew Lindesay class StackedFeaturesPackageBandMetrics
59c3cad236SAndrew Lindesay {
60c3cad236SAndrew Lindesay public:
StackedFeaturesPackageBandMetrics(float width,BFont * titleFont,BFont * metadataFont)61c3cad236SAndrew Lindesay StackedFeaturesPackageBandMetrics(float width, BFont* titleFont, BFont* metadataFont)
62c3cad236SAndrew Lindesay {
63c3cad236SAndrew Lindesay float padding = be_control_look->DefaultItemSpacing();
64c3cad236SAndrew Lindesay BSize iconSize = BControlLook::ComposeIconSize(SIZE_ICON);
65c3cad236SAndrew Lindesay
66c3cad236SAndrew Lindesay font_height titleFontHeight;
67c3cad236SAndrew Lindesay font_height metadataFontHeight;
68c3cad236SAndrew Lindesay titleFont->GetHeight(&titleFontHeight);
69c3cad236SAndrew Lindesay metadataFont->GetHeight(&metadataFontHeight);
70c3cad236SAndrew Lindesay
71c3cad236SAndrew Lindesay float totalTitleAndMetadataHeight = titleFontHeight.ascent + titleFontHeight.descent
72c3cad236SAndrew Lindesay + titleFontHeight.leading
73c3cad236SAndrew Lindesay + metadataFontHeight.leading
74c3cad236SAndrew Lindesay + 2.0 * (metadataFontHeight.ascent + metadataFontHeight.descent);
75c3cad236SAndrew Lindesay
76c3cad236SAndrew Lindesay fHeight = fmaxf(totalTitleAndMetadataHeight, iconSize.Height()) + 2.0 * padding;
77c3cad236SAndrew Lindesay
78c3cad236SAndrew Lindesay {
79c3cad236SAndrew Lindesay float iconInset = (fHeight - iconSize.Width()) / 2.0;
80c3cad236SAndrew Lindesay fIconRect = BRect(padding, iconInset, iconSize.Width() + padding,
81c3cad236SAndrew Lindesay iconSize.Height() + iconInset);
82c3cad236SAndrew Lindesay }
83c3cad236SAndrew Lindesay
84c3cad236SAndrew Lindesay {
85c3cad236SAndrew Lindesay float titleWidthM = titleFont->StringWidth("M");
86c3cad236SAndrew Lindesay
87c3cad236SAndrew Lindesay float leftTitlePublisherAndChronologicalInfo = fIconRect.right + padding;
88c3cad236SAndrew Lindesay float rightTitlePublisherAndChronologicalInfo = fminf(width, fIconRect.Size().Width()
89c3cad236SAndrew Lindesay + (2.0 * padding) + (titleWidthM * M_COUNT_TITLE));
90c3cad236SAndrew Lindesay
91c3cad236SAndrew Lindesay // left, top, right bottom
92c3cad236SAndrew Lindesay fTitleRect = BRect(leftTitlePublisherAndChronologicalInfo,
93c3cad236SAndrew Lindesay (fHeight - totalTitleAndMetadataHeight) / 2.0,
94c3cad236SAndrew Lindesay rightTitlePublisherAndChronologicalInfo,
95c3cad236SAndrew Lindesay ((fHeight - totalTitleAndMetadataHeight) / 2.0)
96c3cad236SAndrew Lindesay + titleFontHeight.ascent + titleFontHeight.descent);
97c3cad236SAndrew Lindesay
98c3cad236SAndrew Lindesay fPublisherRect = BRect(leftTitlePublisherAndChronologicalInfo,
99c3cad236SAndrew Lindesay fTitleRect.bottom + titleFontHeight.leading,
100c3cad236SAndrew Lindesay rightTitlePublisherAndChronologicalInfo,
101c3cad236SAndrew Lindesay fTitleRect.bottom + titleFontHeight.leading
102c3cad236SAndrew Lindesay + metadataFontHeight.ascent + metadataFontHeight.descent);
103c3cad236SAndrew Lindesay
104c3cad236SAndrew Lindesay fChronologicalInfoRect = BRect(leftTitlePublisherAndChronologicalInfo,
105c3cad236SAndrew Lindesay fPublisherRect.bottom + metadataFontHeight.leading,
106c3cad236SAndrew Lindesay rightTitlePublisherAndChronologicalInfo,
107c3cad236SAndrew Lindesay fPublisherRect.bottom + metadataFontHeight.leading
108c3cad236SAndrew Lindesay + metadataFontHeight.ascent + metadataFontHeight.descent);
109c3cad236SAndrew Lindesay }
110c3cad236SAndrew Lindesay
111c3cad236SAndrew Lindesay // sort out the ratings display
112c3cad236SAndrew Lindesay
113c3cad236SAndrew Lindesay {
114c3cad236SAndrew Lindesay BSize ratingStarSize = SharedIcons::IconStarBlue16Scaled()->Bitmap()->Bounds().Size();
115c3cad236SAndrew Lindesay RatingStarsMetrics ratingStarsMetrics(ratingStarSize);
116c3cad236SAndrew Lindesay
117c3cad236SAndrew Lindesay fRatingStarsRect = BRect(BPoint(fTitleRect.right + padding,
118c3cad236SAndrew Lindesay (fHeight - ratingStarsMetrics.Size().Height()) / 2), ratingStarsMetrics.Size());
119c3cad236SAndrew Lindesay
120c3cad236SAndrew Lindesay if (fRatingStarsRect.right > width)
121c3cad236SAndrew Lindesay fRatingStarsRect = BRect();
122c3cad236SAndrew Lindesay else {
123c3cad236SAndrew Lindesay // Now sort out the position for the summary. This is reckoned as a container
124c3cad236SAndrew Lindesay // rect because it would be nice to layout the text with newlines and not just a
125c3cad236SAndrew Lindesay // single line.
126c3cad236SAndrew Lindesay
127c3cad236SAndrew Lindesay fSummaryContainerRect = BRect(fRatingStarsRect.right + (padding * 2.0), padding,
128c3cad236SAndrew Lindesay width - padding, fHeight - (padding * 2.0));
129c3cad236SAndrew Lindesay
130c3cad236SAndrew Lindesay float metadataWidthM = metadataFont->StringWidth("M");
131c3cad236SAndrew Lindesay
132c3cad236SAndrew Lindesay if (fSummaryContainerRect.Size().Width() < MINIMUM_M_COUNT_SUMMARY * metadataWidthM)
133c3cad236SAndrew Lindesay fSummaryContainerRect = BRect();
134c3cad236SAndrew Lindesay }
135c3cad236SAndrew Lindesay }
136c3cad236SAndrew Lindesay }
137c3cad236SAndrew Lindesay
Height()138c3cad236SAndrew Lindesay float Height()
139c3cad236SAndrew Lindesay {
140c3cad236SAndrew Lindesay return fHeight;
141c3cad236SAndrew Lindesay }
142c3cad236SAndrew Lindesay
IconRect()143c3cad236SAndrew Lindesay BRect IconRect()
144c3cad236SAndrew Lindesay {
145c3cad236SAndrew Lindesay return fIconRect;
146c3cad236SAndrew Lindesay }
147c3cad236SAndrew Lindesay
TitleRect()148c3cad236SAndrew Lindesay BRect TitleRect()
149c3cad236SAndrew Lindesay {
150c3cad236SAndrew Lindesay return fTitleRect;
151c3cad236SAndrew Lindesay }
152c3cad236SAndrew Lindesay
PublisherRect()153c3cad236SAndrew Lindesay BRect PublisherRect()
154c3cad236SAndrew Lindesay {
155c3cad236SAndrew Lindesay return fPublisherRect;
156c3cad236SAndrew Lindesay }
157c3cad236SAndrew Lindesay
ChronologicalInfoRect()158c3cad236SAndrew Lindesay BRect ChronologicalInfoRect()
159c3cad236SAndrew Lindesay {
160c3cad236SAndrew Lindesay return fChronologicalInfoRect;
161c3cad236SAndrew Lindesay }
162c3cad236SAndrew Lindesay
RatingStarsRect()163c3cad236SAndrew Lindesay BRect RatingStarsRect()
164c3cad236SAndrew Lindesay {
165c3cad236SAndrew Lindesay return fRatingStarsRect;
166c3cad236SAndrew Lindesay }
167c3cad236SAndrew Lindesay
SummaryContainerRect()168c3cad236SAndrew Lindesay BRect SummaryContainerRect()
169c3cad236SAndrew Lindesay {
170c3cad236SAndrew Lindesay return fSummaryContainerRect;
171c3cad236SAndrew Lindesay }
172c3cad236SAndrew Lindesay
173c3cad236SAndrew Lindesay private:
174c3cad236SAndrew Lindesay float fHeight;
175c3cad236SAndrew Lindesay
176c3cad236SAndrew Lindesay BRect fIconRect;
177c3cad236SAndrew Lindesay BRect fTitleRect;
178c3cad236SAndrew Lindesay BRect fPublisherRect;
179c3cad236SAndrew Lindesay BRect fChronologicalInfoRect;
180c3cad236SAndrew Lindesay
181c3cad236SAndrew Lindesay BRect fRatingStarsRect;
182c3cad236SAndrew Lindesay
183c3cad236SAndrew Lindesay BRect fSummaryContainerRect;
184c3cad236SAndrew Lindesay };
185c3cad236SAndrew Lindesay
186c3cad236SAndrew Lindesay
1879883929bSAndrew Lindesay class StackedFeaturedPackagesView : public BView {
188f545fe6aSStephan Aßmus public:
StackedFeaturedPackagesView(Model & model)189f0e491d3SAndrew Lindesay StackedFeaturedPackagesView(Model& model)
190f545fe6aSStephan Aßmus :
1919883929bSAndrew Lindesay BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS),
192f0e491d3SAndrew Lindesay fModel(model),
193f0e491d3SAndrew Lindesay fSelectedIndex(-1),
1943d869af5SStephan Aßmus fPackageListener(
1950b69420bSAndrew Lindesay new(std::nothrow) OnePackageMessagePackageListener(this)),
1960b69420bSAndrew Lindesay fLowestIndexAddedOrRemoved(-1)
197f545fe6aSStephan Aßmus {
1981f3c1ef1SStephan Aßmus SetEventMask(B_POINTER_EVENTS);
199c3cad236SAndrew Lindesay
200c3cad236SAndrew Lindesay fTitleFont = StackedFeaturedPackagesView::CreateTitleFont();
201c3cad236SAndrew Lindesay fMetadataFont = StackedFeaturedPackagesView::CreateMetadataFont();
202c3cad236SAndrew Lindesay fSummaryFont = StackedFeaturedPackagesView::CreateSummaryFont();
203c3cad236SAndrew Lindesay fBandMetrics = CreateBandMetrics();
204c3cad236SAndrew Lindesay
205f545fe6aSStephan Aßmus Clear();
206f545fe6aSStephan Aßmus }
207f545fe6aSStephan Aßmus
2089883929bSAndrew Lindesay
~StackedFeaturedPackagesView()2099883929bSAndrew Lindesay virtual ~StackedFeaturedPackagesView()
210f545fe6aSStephan Aßmus {
211f545fe6aSStephan Aßmus fPackageListener->SetPackage(PackageInfoRef(NULL));
2121f3909adSAndrew Lindesay fPackageListener->ReleaseReference();
213c3cad236SAndrew Lindesay delete fBandMetrics;
214c3cad236SAndrew Lindesay delete fTitleFont;
215c3cad236SAndrew Lindesay delete fMetadataFont;
216c3cad236SAndrew Lindesay delete fSummaryFont;
217f545fe6aSStephan Aßmus }
218f545fe6aSStephan Aßmus
2199883929bSAndrew Lindesay // #pragma mark - message handling and events
220fa19dd44Slooncraz
MessageReceived(BMessage * message)221f545fe6aSStephan Aßmus virtual void MessageReceived(BMessage* message)
222f545fe6aSStephan Aßmus {
223f545fe6aSStephan Aßmus switch (message->what) {
224f545fe6aSStephan Aßmus case MSG_UPDATE_PACKAGE:
225d45104b1SJulian Harnath {
2269883929bSAndrew Lindesay BString name;
227fa5c8097SAndrew Lindesay if (message->FindString("name", &name) != B_OK)
228fa5c8097SAndrew Lindesay HDINFO("expected 'name' key on package update message");
2299883929bSAndrew Lindesay else
2309883929bSAndrew Lindesay _HandleUpdatePackage(name);
231d45104b1SJulian Harnath break;
232d45104b1SJulian Harnath }
233fa19dd44Slooncraz
234fa19dd44Slooncraz case B_COLORS_UPDATED:
235fa19dd44Slooncraz {
2369883929bSAndrew Lindesay Invalidate();
237fa19dd44Slooncraz break;
238fa19dd44Slooncraz }
2391187e806SAndrew Lindesay
2401187e806SAndrew Lindesay default:
2411187e806SAndrew Lindesay BView::MessageReceived(message);
2421187e806SAndrew Lindesay break;
243f545fe6aSStephan Aßmus }
244f545fe6aSStephan Aßmus }
245f545fe6aSStephan Aßmus
2469883929bSAndrew Lindesay
MouseDown(BPoint where)2471f3c1ef1SStephan Aßmus virtual void MouseDown(BPoint where)
2481f3c1ef1SStephan Aßmus {
2499883929bSAndrew Lindesay if (Window()->IsActive() && !IsHidden()) {
250dfbf4d38SStephan Aßmus BRect bounds = Bounds();
251dfbf4d38SStephan Aßmus BRect parentBounds = Parent()->Bounds();
252dfbf4d38SStephan Aßmus ConvertFromParent(&parentBounds);
253dfbf4d38SStephan Aßmus bounds = bounds & parentBounds;
2549883929bSAndrew Lindesay if (bounds.Contains(where)) {
2559883929bSAndrew Lindesay _MessageSelectIndex(_IndexOfY(where.y));
2569883929bSAndrew Lindesay MakeFocus();
2579883929bSAndrew Lindesay }
2589883929bSAndrew Lindesay }
2599883929bSAndrew Lindesay }
260dfbf4d38SStephan Aßmus
2619883929bSAndrew Lindesay
KeyDown(const char * bytes,int32 numBytes)2629883929bSAndrew Lindesay virtual void KeyDown(const char* bytes, int32 numBytes)
2639883929bSAndrew Lindesay {
2649883929bSAndrew Lindesay char key = bytes[0];
2659883929bSAndrew Lindesay
2669883929bSAndrew Lindesay switch (key) {
2679883929bSAndrew Lindesay case B_RIGHT_ARROW:
2689883929bSAndrew Lindesay case B_DOWN_ARROW:
269d75b4d61SAndrew Lindesay {
270d75b4d61SAndrew Lindesay int32 lastIndex = static_cast<int32>(fPackages.size()) - 1;
2719883929bSAndrew Lindesay if (!IsEmpty() && fSelectedIndex != -1
272d75b4d61SAndrew Lindesay && fSelectedIndex < lastIndex) {
2739883929bSAndrew Lindesay _MessageSelectIndex(fSelectedIndex + 1);
2749883929bSAndrew Lindesay }
2759883929bSAndrew Lindesay break;
276d75b4d61SAndrew Lindesay }
2779883929bSAndrew Lindesay case B_LEFT_ARROW:
2789883929bSAndrew Lindesay case B_UP_ARROW:
2799883929bSAndrew Lindesay if (fSelectedIndex > 0)
2809883929bSAndrew Lindesay _MessageSelectIndex( fSelectedIndex - 1);
2819883929bSAndrew Lindesay break;
2829883929bSAndrew Lindesay case B_PAGE_UP:
2839883929bSAndrew Lindesay {
2849883929bSAndrew Lindesay BRect bounds = Bounds();
2859883929bSAndrew Lindesay ScrollTo(0, fmaxf(0, bounds.top - bounds.Height()));
2869883929bSAndrew Lindesay break;
2879883929bSAndrew Lindesay }
2889883929bSAndrew Lindesay case B_PAGE_DOWN:
2899883929bSAndrew Lindesay {
2909883929bSAndrew Lindesay BRect bounds = Bounds();
291c3cad236SAndrew Lindesay float height = fPackages.size() * fBandMetrics->Height();
2929883929bSAndrew Lindesay float maxScrollY = height - bounds.Height();
2939883929bSAndrew Lindesay float pageDownScrollY = bounds.top + bounds.Height();
2949883929bSAndrew Lindesay ScrollTo(0, fminf(maxScrollY, pageDownScrollY));
2959883929bSAndrew Lindesay break;
2969883929bSAndrew Lindesay }
2979883929bSAndrew Lindesay default:
2989883929bSAndrew Lindesay BView::KeyDown(bytes, numBytes);
2999883929bSAndrew Lindesay break;
3009883929bSAndrew Lindesay }
3019883929bSAndrew Lindesay }
3029883929bSAndrew Lindesay
3039883929bSAndrew Lindesay
3049883929bSAndrew Lindesay /*! This method will send a message to the Window so that it can signal
3059883929bSAndrew Lindesay back to this and other views that a package has been selected. This
3069883929bSAndrew Lindesay method won't actually change the state of this view directly.
3079883929bSAndrew Lindesay */
3089883929bSAndrew Lindesay
_MessageSelectIndex(int32 index) const3099883929bSAndrew Lindesay void _MessageSelectIndex(int32 index) const
3109883929bSAndrew Lindesay {
3119883929bSAndrew Lindesay if (index != -1) {
3121f3c1ef1SStephan Aßmus BMessage message(MSG_PACKAGE_SELECTED);
313d75b4d61SAndrew Lindesay BString packageName = fPackages[index]->Name();
314d75b4d61SAndrew Lindesay message.AddString("name", packageName);
3151f3c1ef1SStephan Aßmus Window()->PostMessage(&message);
3161f3c1ef1SStephan Aßmus }
3171f3c1ef1SStephan Aßmus }
3181f3c1ef1SStephan Aßmus
3199883929bSAndrew Lindesay
FrameResized(float width,float height)3209883929bSAndrew Lindesay virtual void FrameResized(float width, float height)
321f545fe6aSStephan Aßmus {
3229883929bSAndrew Lindesay BView::FrameResized(width, height);
323f545fe6aSStephan Aßmus
324c3cad236SAndrew Lindesay delete fBandMetrics;
325c3cad236SAndrew Lindesay fBandMetrics = CreateBandMetrics();
326056d423cSStephan Aßmus
327c3cad236SAndrew Lindesay Invalidate();
328f545fe6aSStephan Aßmus }
329f545fe6aSStephan Aßmus
3309883929bSAndrew Lindesay
3319883929bSAndrew Lindesay // #pragma mark - update / add / remove / clear data
3329883929bSAndrew Lindesay
3339883929bSAndrew Lindesay
UpdatePackage(uint32 changeMask,const PackageInfoRef & package)334d45104b1SJulian Harnath void UpdatePackage(uint32 changeMask, const PackageInfoRef& package)
335d45104b1SJulian Harnath {
3369883929bSAndrew Lindesay // TODO; could optimize the invalidation?
3379883929bSAndrew Lindesay int32 index = _IndexOfPackage(package);
3389883929bSAndrew Lindesay if (index >= 0) {
3399883929bSAndrew Lindesay fPackages[index] = package;
3409883929bSAndrew Lindesay Invalidate(_RectOfIndex(index));
341d45104b1SJulian Harnath }
3429883929bSAndrew Lindesay }
3439883929bSAndrew Lindesay
344d45104b1SJulian Harnath
Clear()345f545fe6aSStephan Aßmus void Clear()
346f545fe6aSStephan Aßmus {
347c4199192SAndrew Lindesay for (std::vector<PackageInfoRef>::iterator it = fPackages.begin();
348c4199192SAndrew Lindesay it != fPackages.end(); it++) {
349c4199192SAndrew Lindesay (*it)->RemoveListener(fPackageListener);
350c4199192SAndrew Lindesay }
3519883929bSAndrew Lindesay fPackages.clear();
3529883929bSAndrew Lindesay fSelectedIndex = -1;
353c3cad236SAndrew Lindesay
3549883929bSAndrew Lindesay Invalidate();
355f545fe6aSStephan Aßmus }
356f545fe6aSStephan Aßmus
3579883929bSAndrew Lindesay
CreateTitleFont()358c3cad236SAndrew Lindesay static BFont* CreateTitleFont()
359c3cad236SAndrew Lindesay {
360c3cad236SAndrew Lindesay BFont* font = new BFont(be_plain_font);
361c3cad236SAndrew Lindesay font_family family;
362c3cad236SAndrew Lindesay font_style style;
363c3cad236SAndrew Lindesay font->SetSize(ceilf(font->Size() * 1.8f));
364c3cad236SAndrew Lindesay font->GetFamilyAndStyle(&family, &style);
365c3cad236SAndrew Lindesay font->SetFamilyAndStyle(family, "Bold");
366c3cad236SAndrew Lindesay return font;
367c3cad236SAndrew Lindesay }
368c3cad236SAndrew Lindesay
369c3cad236SAndrew Lindesay
CreateMetadataFont()370c3cad236SAndrew Lindesay static BFont* CreateMetadataFont()
371c3cad236SAndrew Lindesay {
372c3cad236SAndrew Lindesay BFont* font = new BFont(be_plain_font);
373c3cad236SAndrew Lindesay font_family family;
374c3cad236SAndrew Lindesay font_style style;
375c3cad236SAndrew Lindesay font->GetFamilyAndStyle(&family, &style);
376c3cad236SAndrew Lindesay font->SetFamilyAndStyle(family, "Italic");
377c3cad236SAndrew Lindesay return font;
378c3cad236SAndrew Lindesay }
379c3cad236SAndrew Lindesay
380c3cad236SAndrew Lindesay
CreateSummaryFont()381c3cad236SAndrew Lindesay static BFont* CreateSummaryFont()
382c3cad236SAndrew Lindesay {
383c3cad236SAndrew Lindesay return new BFont(be_plain_font);
384c3cad236SAndrew Lindesay }
385c3cad236SAndrew Lindesay
386c3cad236SAndrew Lindesay
CreateBandMetrics()387c3cad236SAndrew Lindesay StackedFeaturesPackageBandMetrics* CreateBandMetrics()
388c3cad236SAndrew Lindesay {
389c3cad236SAndrew Lindesay return new StackedFeaturesPackageBandMetrics(Bounds().Width(), fTitleFont, fMetadataFont);
390c3cad236SAndrew Lindesay }
391c3cad236SAndrew Lindesay
392c3cad236SAndrew Lindesay
IsEmpty() const3939883929bSAndrew Lindesay bool IsEmpty() const
3944b930ccaSStephan Aßmus {
3959883929bSAndrew Lindesay return fPackages.size() == 0;
3964b930ccaSStephan Aßmus }
3974b930ccaSStephan Aßmus
3989883929bSAndrew Lindesay
_HandleUpdatePackage(const BString & name)3999883929bSAndrew Lindesay void _HandleUpdatePackage(const BString& name)
40072992391SStephan Aßmus {
4019883929bSAndrew Lindesay int32 index = _IndexOfName(name);
4029883929bSAndrew Lindesay if (index != -1)
4039883929bSAndrew Lindesay Invalidate(_RectOfIndex(index));
40472992391SStephan Aßmus }
40572992391SStephan Aßmus
4069883929bSAndrew Lindesay
_CmpProminences(int64 a,int64 b)4074af3fbf9SAndrew Lindesay static int _CmpProminences(int64 a, int64 b)
4084af3fbf9SAndrew Lindesay {
4094af3fbf9SAndrew Lindesay if (a <= 0)
4104af3fbf9SAndrew Lindesay a = PROMINANCE_ORDERING_MAX;
4114af3fbf9SAndrew Lindesay if (b <= 0)
4124af3fbf9SAndrew Lindesay b = PROMINANCE_ORDERING_MAX;
4134af3fbf9SAndrew Lindesay if (a == b)
4144af3fbf9SAndrew Lindesay return 0;
4154af3fbf9SAndrew Lindesay if (a > b)
4164af3fbf9SAndrew Lindesay return 1;
4174af3fbf9SAndrew Lindesay return -1;
4184af3fbf9SAndrew Lindesay }
4194af3fbf9SAndrew Lindesay
4204af3fbf9SAndrew Lindesay
4219883929bSAndrew Lindesay /*! This method will return true if the packageA is ordered before
4229883929bSAndrew Lindesay packageB.
4239883929bSAndrew Lindesay */
4249883929bSAndrew Lindesay
_IsPackageBefore(const PackageInfoRef & packageA,const PackageInfoRef & packageB)4258cc3ed34SAndrew Lindesay static bool _IsPackageBefore(const PackageInfoRef& packageA, const PackageInfoRef& packageB)
426664372abSStephan Aßmus {
427779ab335SX512 if (!packageA.IsSet() || !packageB.IsSet())
428a5e4976dSAndrew Lindesay HDFATAL("unexpected NULL reference in a referencable");
4298cc3ed34SAndrew Lindesay
4308cc3ed34SAndrew Lindesay uint32 prominenceA = 0;
4318cc3ed34SAndrew Lindesay uint32 prominenceB = 0;
4328cc3ed34SAndrew Lindesay
4338cc3ed34SAndrew Lindesay PackageClassificationInfoRef classificationInfoA = packageA->PackageClassificationInfo();
4348cc3ed34SAndrew Lindesay PackageClassificationInfoRef classificationInfoB = packageB->PackageClassificationInfo();
4358cc3ed34SAndrew Lindesay
4368cc3ed34SAndrew Lindesay if (classificationInfoA.IsSet())
4378cc3ed34SAndrew Lindesay prominenceA = classificationInfoA->Prominence();
4388cc3ed34SAndrew Lindesay
4398cc3ed34SAndrew Lindesay if (classificationInfoB.IsSet())
4408cc3ed34SAndrew Lindesay prominenceB = classificationInfoB->Prominence();
4418cc3ed34SAndrew Lindesay
4428cc3ed34SAndrew Lindesay int c = static_cast<int>(prominenceA) - static_cast<int>(prominenceB);
4438cc3ed34SAndrew Lindesay
444c65ff9f1SAndrew Lindesay if (c == 0) {
445c65ff9f1SAndrew Lindesay BString titleA;
446c65ff9f1SAndrew Lindesay BString titleB;
447c65ff9f1SAndrew Lindesay PackageUtils::Title(packageA, titleA);
448c65ff9f1SAndrew Lindesay PackageUtils::Title(packageB, titleB);
449c65ff9f1SAndrew Lindesay c = titleA.ICompare(titleB);
450c65ff9f1SAndrew Lindesay }
4518cc3ed34SAndrew Lindesay
4529883929bSAndrew Lindesay if (c == 0)
4539883929bSAndrew Lindesay c = packageA->Name().Compare(packageB->Name());
4548cc3ed34SAndrew Lindesay
4559883929bSAndrew Lindesay return c < 0;
456fa19dd44Slooncraz }
457fa19dd44Slooncraz
4584af3fbf9SAndrew Lindesay
BeginAddRemove()4590b69420bSAndrew Lindesay void BeginAddRemove()
4600b69420bSAndrew Lindesay {
4610b69420bSAndrew Lindesay fLowestIndexAddedOrRemoved = INT32_MAX;
4620b69420bSAndrew Lindesay }
4630b69420bSAndrew Lindesay
4640b69420bSAndrew Lindesay
EndAddRemove()4650b69420bSAndrew Lindesay void EndAddRemove()
4660b69420bSAndrew Lindesay {
4670b69420bSAndrew Lindesay if (fLowestIndexAddedOrRemoved < INT32_MAX) {
4680b69420bSAndrew Lindesay if (fPackages.empty())
4690b69420bSAndrew Lindesay Invalidate();
4700b69420bSAndrew Lindesay else {
4710b69420bSAndrew Lindesay BRect invalidRect = Bounds();
4720b69420bSAndrew Lindesay invalidRect.top = _YOfIndex(fLowestIndexAddedOrRemoved);
4730b69420bSAndrew Lindesay Invalidate(invalidRect);
4740b69420bSAndrew Lindesay }
4750b69420bSAndrew Lindesay }
4760b69420bSAndrew Lindesay }
4770b69420bSAndrew Lindesay
4780b69420bSAndrew Lindesay
AddPackage(const PackageInfoRef & package)4799883929bSAndrew Lindesay void AddPackage(const PackageInfoRef& package)
480fa19dd44Slooncraz {
4819883929bSAndrew Lindesay // fPackages is sorted and for this reason it is possible to find the
4829883929bSAndrew Lindesay // insertion point by identifying the first item in fPackages that does
4839883929bSAndrew Lindesay // not return true from the method '_IsPackageBefore'.
484fa19dd44Slooncraz
4854d2de4eaSAugustin Cavalier std::vector<PackageInfoRef>::iterator itInsertionPt
4869883929bSAndrew Lindesay = std::lower_bound(fPackages.begin(), fPackages.end(), package,
4879883929bSAndrew Lindesay &_IsPackageBefore);
4889883929bSAndrew Lindesay
4899883929bSAndrew Lindesay if (itInsertionPt == fPackages.end()
4909883929bSAndrew Lindesay || package->Name() != (*itInsertionPt)->Name()) {
4919883929bSAndrew Lindesay int32 insertionIndex =
4929883929bSAndrew Lindesay std::distance<std::vector<PackageInfoRef>::const_iterator>(
4939883929bSAndrew Lindesay fPackages.begin(), itInsertionPt);
4949883929bSAndrew Lindesay if (fSelectedIndex >= insertionIndex)
4959883929bSAndrew Lindesay fSelectedIndex++;
4969883929bSAndrew Lindesay fPackages.insert(itInsertionPt, package);
497c4199192SAndrew Lindesay package->AddListener(fPackageListener);
4980b69420bSAndrew Lindesay if (insertionIndex < fLowestIndexAddedOrRemoved)
4990b69420bSAndrew Lindesay fLowestIndexAddedOrRemoved = insertionIndex;
5009883929bSAndrew Lindesay }
501fa19dd44Slooncraz }
502664372abSStephan Aßmus
503664372abSStephan Aßmus
RemovePackage(const PackageInfoRef & package)5049883929bSAndrew Lindesay void RemovePackage(const PackageInfoRef& package)
505d45104b1SJulian Harnath {
5069883929bSAndrew Lindesay int32 index = _IndexOfPackage(package);
5079883929bSAndrew Lindesay if (index >= 0) {
5089883929bSAndrew Lindesay if (fSelectedIndex == index)
5099883929bSAndrew Lindesay fSelectedIndex = -1;
5109883929bSAndrew Lindesay if (fSelectedIndex > index)
5119883929bSAndrew Lindesay fSelectedIndex--;
512c4199192SAndrew Lindesay fPackages[index]->RemoveListener(fPackageListener);
5139883929bSAndrew Lindesay fPackages.erase(fPackages.begin() + index);
5140b69420bSAndrew Lindesay if (index < fLowestIndexAddedOrRemoved)
5150b69420bSAndrew Lindesay fLowestIndexAddedOrRemoved = index;
5169883929bSAndrew Lindesay }
5179883929bSAndrew Lindesay }
518d45104b1SJulian Harnath
519d45104b1SJulian Harnath
5209883929bSAndrew Lindesay // #pragma mark - selection and index handling
521d45104b1SJulian Harnath
522d45104b1SJulian Harnath
SelectPackage(const PackageInfoRef & package)5239883929bSAndrew Lindesay void SelectPackage(const PackageInfoRef& package)
5249883929bSAndrew Lindesay {
5259883929bSAndrew Lindesay _SelectIndex(_IndexOfPackage(package));
5269883929bSAndrew Lindesay }
5279883929bSAndrew Lindesay
5289883929bSAndrew Lindesay
_SelectIndex(int32 index)5299883929bSAndrew Lindesay void _SelectIndex(int32 index)
5309883929bSAndrew Lindesay {
5319883929bSAndrew Lindesay if (index != fSelectedIndex) {
5329883929bSAndrew Lindesay int32 previousSelectedIndex = fSelectedIndex;
5339883929bSAndrew Lindesay fSelectedIndex = index;
5349883929bSAndrew Lindesay if (fSelectedIndex >= 0)
5359883929bSAndrew Lindesay Invalidate(_RectOfIndex(fSelectedIndex));
5369883929bSAndrew Lindesay if (previousSelectedIndex >= 0)
5379883929bSAndrew Lindesay Invalidate(_RectOfIndex(previousSelectedIndex));
5389883929bSAndrew Lindesay _EnsureIndexVisible(index);
5399883929bSAndrew Lindesay }
5409883929bSAndrew Lindesay }
5419883929bSAndrew Lindesay
5429883929bSAndrew Lindesay
_IndexOfPackage(PackageInfoRef package) const5439883929bSAndrew Lindesay int32 _IndexOfPackage(PackageInfoRef package) const
5449883929bSAndrew Lindesay {
545c4199192SAndrew Lindesay std::vector<PackageInfoRef>::const_iterator it
546c4199192SAndrew Lindesay = std::lower_bound(fPackages.begin(), fPackages.end(), package,
547c4199192SAndrew Lindesay &_IsPackageBefore);
548c4199192SAndrew Lindesay
549c4199192SAndrew Lindesay return (it == fPackages.end() || (*it)->Name() != package->Name())
550c4199192SAndrew Lindesay ? -1 : it - fPackages.begin();
5519883929bSAndrew Lindesay }
5529883929bSAndrew Lindesay
5539883929bSAndrew Lindesay
_IndexOfName(const BString & name) const5549883929bSAndrew Lindesay int32 _IndexOfName(const BString& name) const
5559883929bSAndrew Lindesay {
5569883929bSAndrew Lindesay // TODO; slow linear search.
5579883929bSAndrew Lindesay // the fPackages is not sorted on name and for this reason it is not
5589883929bSAndrew Lindesay // possible to do a binary search.
5599883929bSAndrew Lindesay for (uint32 i = 0; i < fPackages.size(); i++) {
5609883929bSAndrew Lindesay if (fPackages[i]->Name() == name)
5619883929bSAndrew Lindesay return i;
5629883929bSAndrew Lindesay }
5639883929bSAndrew Lindesay return -1;
5649883929bSAndrew Lindesay }
5659883929bSAndrew Lindesay
5669883929bSAndrew Lindesay
5679883929bSAndrew Lindesay // #pragma mark - drawing and rendering
5689883929bSAndrew Lindesay
5699883929bSAndrew Lindesay
Draw(BRect updateRect)5709883929bSAndrew Lindesay virtual void Draw(BRect updateRect)
5719883929bSAndrew Lindesay {
5729883929bSAndrew Lindesay SetHighUIColor(B_LIST_BACKGROUND_COLOR);
5739883929bSAndrew Lindesay FillRect(updateRect);
5749883929bSAndrew Lindesay
5759883929bSAndrew Lindesay int32 iStart = _IndexRoundedOfY(updateRect.top);
5769883929bSAndrew Lindesay
5779883929bSAndrew Lindesay if (iStart != -1) {
5789883929bSAndrew Lindesay int32 iEnd = _IndexRoundedOfY(updateRect.bottom);
5799883929bSAndrew Lindesay for (int32 i = iStart; i <= iEnd; i++)
5809883929bSAndrew Lindesay _DrawPackageAtIndex(updateRect, i);
5819883929bSAndrew Lindesay }
5829883929bSAndrew Lindesay }
5839883929bSAndrew Lindesay
5849883929bSAndrew Lindesay
_DrawPackageAtIndex(BRect updateRect,int32 index)5859883929bSAndrew Lindesay void _DrawPackageAtIndex(BRect updateRect, int32 index)
5869883929bSAndrew Lindesay {
587c3cad236SAndrew Lindesay _DrawPackage(updateRect, fPackages[index], _YOfIndex(index), index == fSelectedIndex);
5889883929bSAndrew Lindesay }
5899883929bSAndrew Lindesay
5909883929bSAndrew Lindesay
_DrawPackage(BRect updateRect,PackageInfoRef pkg,float y,bool selected)591c3cad236SAndrew Lindesay void _DrawPackage(BRect updateRect, PackageInfoRef pkg, float y, bool selected)
5929883929bSAndrew Lindesay {
5939883929bSAndrew Lindesay if (selected) {
5949883929bSAndrew Lindesay SetLowUIColor(B_LIST_SELECTED_BACKGROUND_COLOR);
5959883929bSAndrew Lindesay FillRect(_RectOfY(y), B_SOLID_LOW);
596d45104b1SJulian Harnath } else {
5979883929bSAndrew Lindesay SetLowUIColor(B_LIST_BACKGROUND_COLOR);
598d45104b1SJulian Harnath }
599c3cad236SAndrew Lindesay
600c3cad236SAndrew Lindesay BRect iconRect = fBandMetrics->IconRect();
601c3cad236SAndrew Lindesay BRect titleRect = fBandMetrics->TitleRect();
602c3cad236SAndrew Lindesay BRect publisherRect = fBandMetrics->PublisherRect();
603c3cad236SAndrew Lindesay BRect chronologicalInfoRect = fBandMetrics->ChronologicalInfoRect();
604c3cad236SAndrew Lindesay BRect ratingStarsRect = fBandMetrics->RatingStarsRect();
605c3cad236SAndrew Lindesay BRect summaryContainerRect = fBandMetrics->SummaryContainerRect();
606c3cad236SAndrew Lindesay
607c3cad236SAndrew Lindesay iconRect.OffsetBy(0.0, y);
608c3cad236SAndrew Lindesay titleRect.OffsetBy(0.0, y);
609c3cad236SAndrew Lindesay publisherRect.OffsetBy(0.0, y);
610c3cad236SAndrew Lindesay chronologicalInfoRect.OffsetBy(0.0, y);
611c3cad236SAndrew Lindesay ratingStarsRect.OffsetBy(0.0, y);
612c3cad236SAndrew Lindesay summaryContainerRect.OffsetBy(0.0, y);
613c3cad236SAndrew Lindesay
6149883929bSAndrew Lindesay // TODO; optimization; the updateRect may only cover some of this?
615c3cad236SAndrew Lindesay _DrawPackageIcon(iconRect, pkg, selected);
616c3cad236SAndrew Lindesay _DrawPackageTitle(titleRect, pkg, selected);
617c3cad236SAndrew Lindesay _DrawPackagePublisher(publisherRect, pkg, selected);
618c3cad236SAndrew Lindesay _DrawPackageChronologicalInfo(chronologicalInfoRect, pkg, selected);
619c3cad236SAndrew Lindesay _DrawPackageRating(ratingStarsRect, pkg);
620c3cad236SAndrew Lindesay _DrawPackageSummary(summaryContainerRect, pkg, selected);
621d45104b1SJulian Harnath }
622d45104b1SJulian Harnath
623d45104b1SJulian Harnath
_DrawPackageIcon(BRect iconRect,PackageInfoRef pkg,bool selected)624c3cad236SAndrew Lindesay void _DrawPackageIcon(BRect iconRect, PackageInfoRef pkg, bool selected)
625d45104b1SJulian Harnath {
626c3cad236SAndrew Lindesay if (!iconRect.IsValid())
627c3cad236SAndrew Lindesay return;
628c3cad236SAndrew Lindesay
62966ee6532SAndrew Lindesay BitmapHolderRef icon;
630c3cad236SAndrew Lindesay status_t iconResult = fModel.GetPackageIconRepository().GetIcon(pkg->Name(),
631c3cad236SAndrew Lindesay iconRect.Width(), icon);
6329883929bSAndrew Lindesay
633f0e491d3SAndrew Lindesay if (iconResult == B_OK) {
634779ab335SX512 if (icon.IsSet()) {
63566ee6532SAndrew Lindesay const BBitmap* bitmap = icon->Bitmap();
6368d18358cSAndrew Lindesay
6378d18358cSAndrew Lindesay if (bitmap != NULL && bitmap->IsValid()) {
6389883929bSAndrew Lindesay SetDrawingMode(B_OP_ALPHA);
639c3cad236SAndrew Lindesay DrawBitmap(bitmap, bitmap->Bounds(), iconRect, B_FILTER_BITMAP_BILINEAR);
640d45104b1SJulian Harnath }
6419883929bSAndrew Lindesay }
642f0e491d3SAndrew Lindesay }
6438d18358cSAndrew Lindesay }
6449883929bSAndrew Lindesay
6459883929bSAndrew Lindesay
_DrawPackageTitle(BRect textRect,PackageInfoRef pkg,bool selected)646c3cad236SAndrew Lindesay void _DrawPackageTitle(BRect textRect, PackageInfoRef pkg, bool selected)
6479883929bSAndrew Lindesay {
648c3cad236SAndrew Lindesay if (!textRect.IsValid())
649c3cad236SAndrew Lindesay return;
6509883929bSAndrew Lindesay
651*6afa94a0SAndrew Lindesay std::vector<BitmapHolderRef> trailingIconBitmaps;
652*6afa94a0SAndrew Lindesay
653*6afa94a0SAndrew Lindesay if (PackageUtils::State(pkg) == ACTIVATED)
654*6afa94a0SAndrew Lindesay trailingIconBitmaps.push_back(SharedIcons::IconInstalled16Scaled());
655*6afa94a0SAndrew Lindesay
656*6afa94a0SAndrew Lindesay if (PackageUtils::IsNativeDesktop(pkg))
657*6afa94a0SAndrew Lindesay trailingIconBitmaps.push_back(SharedIcons::IconNative16Scaled());
658*6afa94a0SAndrew Lindesay
659*6afa94a0SAndrew Lindesay float trailingIconsWidth = 0.0f;
660*6afa94a0SAndrew Lindesay
661*6afa94a0SAndrew Lindesay for (std::vector<BitmapHolderRef>::iterator it = trailingIconBitmaps.begin();
662*6afa94a0SAndrew Lindesay it != trailingIconBitmaps.end(); it++) {
663*6afa94a0SAndrew Lindesay trailingIconsWidth += (*it)->Bitmap()->Bounds().Width()
664*6afa94a0SAndrew Lindesay * (1.0 + TRAILING_ICON_PADDING_LEFT_FACTOR);
665*6afa94a0SAndrew Lindesay }
6669883929bSAndrew Lindesay
6679883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY);
668c3cad236SAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR : B_LIST_ITEM_TEXT_COLOR);
669c3cad236SAndrew Lindesay SetFont(fTitleFont);
670c3cad236SAndrew Lindesay
671*6afa94a0SAndrew Lindesay float titleRightTrailingIconsPadding = 0.0f;
672*6afa94a0SAndrew Lindesay
673*6afa94a0SAndrew Lindesay if (!trailingIconBitmaps.empty()) {
674*6afa94a0SAndrew Lindesay titleRightTrailingIconsPadding = StringWidth("M")
675*6afa94a0SAndrew Lindesay * TITLE_RIGHT_TRAILING_ICON_PADDING_M_FACTOR;
676*6afa94a0SAndrew Lindesay }
677*6afa94a0SAndrew Lindesay
678c3cad236SAndrew Lindesay font_height fontHeight;
679c3cad236SAndrew Lindesay fTitleFont->GetHeight(&fontHeight);
680c3cad236SAndrew Lindesay BPoint pt = textRect.LeftTop() + BPoint(0.0, + fontHeight.ascent);
681c3cad236SAndrew Lindesay
682c65ff9f1SAndrew Lindesay BString title;
683c65ff9f1SAndrew Lindesay PackageUtils::TitleOrName(pkg, title);
684c65ff9f1SAndrew Lindesay
685c65ff9f1SAndrew Lindesay BString renderedText = title;
686*6afa94a0SAndrew Lindesay TruncateString(&renderedText, B_TRUNCATE_END, textRect.Width()
687*6afa94a0SAndrew Lindesay - (titleRightTrailingIconsPadding + trailingIconsWidth));
688c3cad236SAndrew Lindesay
689c3cad236SAndrew Lindesay DrawString(renderedText, pt);
6909883929bSAndrew Lindesay
691*6afa94a0SAndrew Lindesay // now draw the trailing icons.
692*6afa94a0SAndrew Lindesay
693c65ff9f1SAndrew Lindesay float stringWidth = StringWidth(title);
694*6afa94a0SAndrew Lindesay float trailingIconX = textRect.left + stringWidth + titleRightTrailingIconsPadding;
695*6afa94a0SAndrew Lindesay float trailingIconMidY = textRect.top + (textRect.Height() / 2.0);
696*6afa94a0SAndrew Lindesay
6979883929bSAndrew Lindesay SetDrawingMode(B_OP_ALPHA);
698*6afa94a0SAndrew Lindesay
699*6afa94a0SAndrew Lindesay for (std::vector<BitmapHolderRef>::iterator it = trailingIconBitmaps.begin();
700*6afa94a0SAndrew Lindesay it != trailingIconBitmaps.end(); it++) {
701*6afa94a0SAndrew Lindesay const BBitmap* bitmap = (*it)->Bitmap();
702*6afa94a0SAndrew Lindesay BRect bitmapBounds = bitmap->Bounds();
703*6afa94a0SAndrew Lindesay
704*6afa94a0SAndrew Lindesay float trailingIconTopLeftPtX = ceilf(
705*6afa94a0SAndrew Lindesay trailingIconX + (bitmapBounds.Width() * TRAILING_ICON_PADDING_LEFT_FACTOR)) + 0.5;
706*6afa94a0SAndrew Lindesay float trailingIconTopLeftPtY = ceilf(
707*6afa94a0SAndrew Lindesay trailingIconMidY - (bitmapBounds.Height() / 2.0)) + 0.5;
708*6afa94a0SAndrew Lindesay
709*6afa94a0SAndrew Lindesay BRect trailingIconRect(BPoint(trailingIconTopLeftPtX, trailingIconTopLeftPtY),
710*6afa94a0SAndrew Lindesay bitmap->Bounds().Size());
711*6afa94a0SAndrew Lindesay
712*6afa94a0SAndrew Lindesay DrawBitmap(bitmap, bitmapBounds, trailingIconRect, B_FILTER_BITMAP_BILINEAR);
713*6afa94a0SAndrew Lindesay
714*6afa94a0SAndrew Lindesay trailingIconX = trailingIconRect.right;
7159883929bSAndrew Lindesay }
7169883929bSAndrew Lindesay }
7179883929bSAndrew Lindesay
7189883929bSAndrew Lindesay
_DrawPackageGenericTextSlug(BRect textRect,const BString & text,bool selected)719c3cad236SAndrew Lindesay void _DrawPackageGenericTextSlug(BRect textRect, const BString& text, bool selected)
7209883929bSAndrew Lindesay {
721c3cad236SAndrew Lindesay if (!textRect.IsValid())
722c3cad236SAndrew Lindesay return;
7239883929bSAndrew Lindesay
7249883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY);
725c3cad236SAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR : B_LIST_ITEM_TEXT_COLOR);
726c3cad236SAndrew Lindesay SetFont(fMetadataFont);
7279883929bSAndrew Lindesay
728c3cad236SAndrew Lindesay font_height fontHeight;
729c3cad236SAndrew Lindesay fMetadataFont->GetHeight(&fontHeight);
730c3cad236SAndrew Lindesay BPoint pt = textRect.LeftTop() + BPoint(0.0, + fontHeight.ascent);
731c3cad236SAndrew Lindesay
7320d4fa3e5SAndrew Lindesay BString renderedText(text);
733c3cad236SAndrew Lindesay TruncateString(&renderedText, B_TRUNCATE_END, textRect.Width());
7349883929bSAndrew Lindesay
735c3cad236SAndrew Lindesay DrawString(renderedText, pt);
7360d4fa3e5SAndrew Lindesay }
7370d4fa3e5SAndrew Lindesay
7380d4fa3e5SAndrew Lindesay
_DrawPackagePublisher(BRect textRect,PackageInfoRef pkg,bool selected)739c3cad236SAndrew Lindesay void _DrawPackagePublisher(BRect textRect, PackageInfoRef pkg, bool selected)
7400d4fa3e5SAndrew Lindesay {
741c3cad236SAndrew Lindesay _DrawPackageGenericTextSlug(textRect, pkg->Publisher().Name(), selected);
7420d4fa3e5SAndrew Lindesay }
7430d4fa3e5SAndrew Lindesay
7440d4fa3e5SAndrew Lindesay
_DrawPackageChronologicalInfo(BRect textRect,PackageInfoRef pkg,bool selected)745c3cad236SAndrew Lindesay void _DrawPackageChronologicalInfo(BRect textRect, PackageInfoRef pkg, bool selected)
7460d4fa3e5SAndrew Lindesay {
7470d4fa3e5SAndrew Lindesay BString versionCreateTimestampPresentation
7487dd47034SHumdinger = LocaleUtils::TimestampToDateString(pkg->VersionCreateTimestamp());
749c3cad236SAndrew Lindesay _DrawPackageGenericTextSlug(textRect, versionCreateTimestampPresentation, selected);
7509883929bSAndrew Lindesay }
7519883929bSAndrew Lindesay
7529883929bSAndrew Lindesay
7539883929bSAndrew Lindesay // TODO; show the sample size
_DrawPackageRating(BRect ratingRect,PackageInfoRef pkg)754c3cad236SAndrew Lindesay void _DrawPackageRating(BRect ratingRect, PackageInfoRef pkg)
7559883929bSAndrew Lindesay {
756c3cad236SAndrew Lindesay if (!ratingRect.IsValid())
757c3cad236SAndrew Lindesay return;
7584b3c808eSAndrew Lindesay
7594b3c808eSAndrew Lindesay UserRatingInfoRef userRatingInfo = pkg->UserRatingInfo();
7604b3c808eSAndrew Lindesay
7614b3c808eSAndrew Lindesay if (userRatingInfo.IsSet()) {
7624b3c808eSAndrew Lindesay UserRatingSummaryRef userRatingSummary = userRatingInfo->Summary();
7634b3c808eSAndrew Lindesay
7644b3c808eSAndrew Lindesay if (userRatingSummary.IsSet())
7654b3c808eSAndrew Lindesay RatingUtils::Draw(this, ratingRect.LeftTop(), userRatingSummary->AverageRating());
7664b3c808eSAndrew Lindesay }
7679883929bSAndrew Lindesay }
7689883929bSAndrew Lindesay
7699883929bSAndrew Lindesay
7709883929bSAndrew Lindesay // TODO; handle multi-line rendering of the text
_DrawPackageSummary(BRect textRect,PackageInfoRef pkg,bool selected)771c3cad236SAndrew Lindesay void _DrawPackageSummary(BRect textRect, PackageInfoRef pkg, bool selected)
7729883929bSAndrew Lindesay {
773c3cad236SAndrew Lindesay if (!textRect.IsValid())
774c3cad236SAndrew Lindesay return;
7759883929bSAndrew Lindesay
7769883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY);
777c3cad236SAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR : B_LIST_ITEM_TEXT_COLOR);
778c3cad236SAndrew Lindesay SetFont(fSummaryFont);
7799883929bSAndrew Lindesay
780c3cad236SAndrew Lindesay font_height fontHeight;
781c3cad236SAndrew Lindesay fSummaryFont->GetHeight(&fontHeight);
782c3cad236SAndrew Lindesay
783c3cad236SAndrew Lindesay // The text rect is a container into which later text can be made to flow multi-line. For
784c3cad236SAndrew Lindesay // now just draw one line of the summary.
785c3cad236SAndrew Lindesay
786c3cad236SAndrew Lindesay BPoint pt = textRect.LeftTop() + BPoint(0.0,
787c3cad236SAndrew Lindesay (textRect.Size().Height() / 2.0) - ((fontHeight.ascent + fontHeight.descent) / 2.0)
788c3cad236SAndrew Lindesay + fontHeight.ascent);
789c3cad236SAndrew Lindesay
790c65ff9f1SAndrew Lindesay BString summary;
791c65ff9f1SAndrew Lindesay PackageUtils::Summary(pkg, summary);
792c3cad236SAndrew Lindesay TruncateString(&summary, B_TRUNCATE_END, textRect.Width());
7939883929bSAndrew Lindesay
794c3cad236SAndrew Lindesay DrawString(summary, pt);
7959883929bSAndrew Lindesay }
7969883929bSAndrew Lindesay
7979883929bSAndrew Lindesay
7989883929bSAndrew Lindesay // #pragma mark - geometry and scrolling
7999883929bSAndrew Lindesay
8009883929bSAndrew Lindesay
8019883929bSAndrew Lindesay /*! This method will make sure that the package at the given index is
8029883929bSAndrew Lindesay visible. If the whole of the package can be seen already then it will
8039883929bSAndrew Lindesay do nothing. If the package is located above the visible region then it
8049883929bSAndrew Lindesay will scroll up to it. If the package is located below the visible
8059883929bSAndrew Lindesay region then it will scroll down to it.
8069883929bSAndrew Lindesay */
8079883929bSAndrew Lindesay
_EnsureIndexVisible(int32 index)8089883929bSAndrew Lindesay void _EnsureIndexVisible(int32 index)
8099883929bSAndrew Lindesay {
8109883929bSAndrew Lindesay if (!_IsIndexEntirelyVisible(index)) {
8119883929bSAndrew Lindesay BRect bounds = Bounds();
812c3cad236SAndrew Lindesay int32 indexOfCentreVisible = _IndexOfY(bounds.top + bounds.Height() / 2);
8139883929bSAndrew Lindesay if (index < indexOfCentreVisible)
8149883929bSAndrew Lindesay ScrollTo(0, _YOfIndex(index));
8159883929bSAndrew Lindesay else {
816c3cad236SAndrew Lindesay float scrollPointY = (_YOfIndex(index) + fBandMetrics->Height()) - bounds.Height();
8179883929bSAndrew Lindesay ScrollTo(0, scrollPointY);
8189883929bSAndrew Lindesay }
8199883929bSAndrew Lindesay }
8209883929bSAndrew Lindesay }
8219883929bSAndrew Lindesay
8229883929bSAndrew Lindesay
8239883929bSAndrew Lindesay /*! This method will return true if the package at the supplied index is
8249883929bSAndrew Lindesay entirely visible.
8259883929bSAndrew Lindesay */
8269883929bSAndrew Lindesay
_IsIndexEntirelyVisible(int32 index)8279883929bSAndrew Lindesay bool _IsIndexEntirelyVisible(int32 index)
8289883929bSAndrew Lindesay {
8299883929bSAndrew Lindesay BRect bounds = Bounds();
8309883929bSAndrew Lindesay return bounds == (bounds | _RectOfIndex(index));
8319883929bSAndrew Lindesay }
8329883929bSAndrew Lindesay
8339883929bSAndrew Lindesay
_RectOfIndex(int32 index) const8349883929bSAndrew Lindesay BRect _RectOfIndex(int32 index) const
8359883929bSAndrew Lindesay {
8369883929bSAndrew Lindesay if (index < 0)
8379883929bSAndrew Lindesay return BRect(0, 0, 0, 0);
8389883929bSAndrew Lindesay return _RectOfY(_YOfIndex(index));
8399883929bSAndrew Lindesay }
8409883929bSAndrew Lindesay
8419883929bSAndrew Lindesay
8429883929bSAndrew Lindesay /*! Provides the top coordinate (offset from the top of view) of the package
8439883929bSAndrew Lindesay supplied. If the package does not exist in the view then the coordinate
8449883929bSAndrew Lindesay returned will be B_SIZE_UNSET.
8459883929bSAndrew Lindesay */
8469883929bSAndrew Lindesay
TopOfPackage(const PackageInfoRef & package)8479883929bSAndrew Lindesay float TopOfPackage(const PackageInfoRef& package)
8489883929bSAndrew Lindesay {
849779ab335SX512 if (package.IsSet()) {
8509883929bSAndrew Lindesay int index = _IndexOfPackage(package);
8519883929bSAndrew Lindesay if (-1 != index)
8529883929bSAndrew Lindesay return _YOfIndex(index);
8539883929bSAndrew Lindesay }
8549883929bSAndrew Lindesay return B_SIZE_UNSET;
8559883929bSAndrew Lindesay }
8569883929bSAndrew Lindesay
8579883929bSAndrew Lindesay
_RectOfY(float y) const8589883929bSAndrew Lindesay BRect _RectOfY(float y) const
8599883929bSAndrew Lindesay {
860c3cad236SAndrew Lindesay return BRect(0, y, Bounds().Width(), y + fBandMetrics->Height());
8619883929bSAndrew Lindesay }
8629883929bSAndrew Lindesay
8639883929bSAndrew Lindesay
_YOfIndex(int32 i) const8649883929bSAndrew Lindesay float _YOfIndex(int32 i) const
8659883929bSAndrew Lindesay {
866c3cad236SAndrew Lindesay return i * fBandMetrics->Height();
8679883929bSAndrew Lindesay }
8689883929bSAndrew Lindesay
8699883929bSAndrew Lindesay
8709883929bSAndrew Lindesay /*! Finds the offset into the list of packages for the y-coord in the view's
8719883929bSAndrew Lindesay coordinate space. If the y is above or below the list of packages then
8729883929bSAndrew Lindesay this will return -1 to signal this.
8739883929bSAndrew Lindesay */
8749883929bSAndrew Lindesay
_IndexOfY(float y) const8759883929bSAndrew Lindesay int32 _IndexOfY(float y) const
8769883929bSAndrew Lindesay {
8779883929bSAndrew Lindesay if (fPackages.empty())
8789883929bSAndrew Lindesay return -1;
879c3cad236SAndrew Lindesay int32 i = static_cast<int32>(y / fBandMetrics->Height());
880d75b4d61SAndrew Lindesay if (i < 0 || i >= static_cast<int32>(fPackages.size()))
8819883929bSAndrew Lindesay return -1;
8829883929bSAndrew Lindesay return i;
8839883929bSAndrew Lindesay }
8849883929bSAndrew Lindesay
8859883929bSAndrew Lindesay
8869883929bSAndrew Lindesay /*! Find the offset into the list of packages for the y-coord in the view's
8879883929bSAndrew Lindesay coordinate space. If the y is above or below the list of packages then
8889883929bSAndrew Lindesay this will return the first or last package index respectively. If there
8899883929bSAndrew Lindesay are no packages then this will return -1;
8909883929bSAndrew Lindesay */
8919883929bSAndrew Lindesay
_IndexRoundedOfY(float y) const8929883929bSAndrew Lindesay int32 _IndexRoundedOfY(float y) const
8939883929bSAndrew Lindesay {
8949883929bSAndrew Lindesay if (fPackages.empty())
8959883929bSAndrew Lindesay return -1;
896c3cad236SAndrew Lindesay int32 i = static_cast<int32>(y / fBandMetrics->Height());
8979883929bSAndrew Lindesay if (i < 0)
8989883929bSAndrew Lindesay return 0;
8999883929bSAndrew Lindesay return std::min(i, (int32) (fPackages.size() - 1));
9009883929bSAndrew Lindesay }
9019883929bSAndrew Lindesay
9029883929bSAndrew Lindesay
PreferredSize()9039883929bSAndrew Lindesay virtual BSize PreferredSize()
9049883929bSAndrew Lindesay {
905c3cad236SAndrew Lindesay return BSize(B_SIZE_UNLIMITED,
906c3cad236SAndrew Lindesay fBandMetrics->Height() * static_cast<float>(fPackages.size()));
9079883929bSAndrew Lindesay }
9089883929bSAndrew Lindesay
909d45104b1SJulian Harnath
910f545fe6aSStephan Aßmus private:
911f0e491d3SAndrew Lindesay Model& fModel;
9129883929bSAndrew Lindesay std::vector<PackageInfoRef>
9139883929bSAndrew Lindesay fPackages;
9149883929bSAndrew Lindesay int32 fSelectedIndex;
9159883929bSAndrew Lindesay OnePackageMessagePackageListener*
9169883929bSAndrew Lindesay fPackageListener;
9170b69420bSAndrew Lindesay int32 fLowestIndexAddedOrRemoved;
918c3cad236SAndrew Lindesay StackedFeaturesPackageBandMetrics*
919c3cad236SAndrew Lindesay fBandMetrics;
920c3cad236SAndrew Lindesay
921c3cad236SAndrew Lindesay BFont* fTitleFont;
922c3cad236SAndrew Lindesay BFont* fMetadataFont;
923c3cad236SAndrew Lindesay BFont* fSummaryFont;
924f545fe6aSStephan Aßmus };
925f545fe6aSStephan Aßmus
926f545fe6aSStephan Aßmus
927f545fe6aSStephan Aßmus // #pragma mark - FeaturedPackagesView
928f545fe6aSStephan Aßmus
929f545fe6aSStephan Aßmus
FeaturedPackagesView(Model & model)930f0e491d3SAndrew Lindesay FeaturedPackagesView::FeaturedPackagesView(Model& model)
931f545fe6aSStephan Aßmus :
932f0e491d3SAndrew Lindesay BView(B_TRANSLATE("Featured packages"), 0),
933f0e491d3SAndrew Lindesay fModel(model)
934f545fe6aSStephan Aßmus {
935f0e491d3SAndrew Lindesay fPackagesView = new StackedFeaturedPackagesView(fModel);
936f545fe6aSStephan Aßmus
9379883929bSAndrew Lindesay fScrollView = new BScrollView("featured packages scroll view",
938494bd147SAugustin Cavalier fPackagesView, 0, false, true, B_FANCY_BORDER);
9398507d262SStephan Aßmus
940f545fe6aSStephan Aßmus BLayoutBuilder::Group<>(this)
9419883929bSAndrew Lindesay .Add(fScrollView, 1.0f);
942f545fe6aSStephan Aßmus }
943f545fe6aSStephan Aßmus
944f545fe6aSStephan Aßmus
~FeaturedPackagesView()945f545fe6aSStephan Aßmus FeaturedPackagesView::~FeaturedPackagesView()
946f545fe6aSStephan Aßmus {
947f545fe6aSStephan Aßmus }
948f545fe6aSStephan Aßmus
949f545fe6aSStephan Aßmus
9500b69420bSAndrew Lindesay void
BeginAddRemove()9510b69420bSAndrew Lindesay FeaturedPackagesView::BeginAddRemove()
9520b69420bSAndrew Lindesay {
9530b69420bSAndrew Lindesay fPackagesView->BeginAddRemove();
9540b69420bSAndrew Lindesay }
9550b69420bSAndrew Lindesay
9560b69420bSAndrew Lindesay
9570b69420bSAndrew Lindesay void
EndAddRemove()9580b69420bSAndrew Lindesay FeaturedPackagesView::EndAddRemove()
9590b69420bSAndrew Lindesay {
9600b69420bSAndrew Lindesay fPackagesView->EndAddRemove();
9610b69420bSAndrew Lindesay _AdjustViews();
9620b69420bSAndrew Lindesay }
9630b69420bSAndrew Lindesay
9640b69420bSAndrew Lindesay
965ccf707d0SAndrew Lindesay /*! This method will add the package into the list to be displayed. The
966ccf707d0SAndrew Lindesay insertion will occur in alphabetical order.
967ccf707d0SAndrew Lindesay */
968ccf707d0SAndrew Lindesay
969f545fe6aSStephan Aßmus void
AddPackage(const PackageInfoRef & package)970f545fe6aSStephan Aßmus FeaturedPackagesView::AddPackage(const PackageInfoRef& package)
971f545fe6aSStephan Aßmus {
9729883929bSAndrew Lindesay fPackagesView->AddPackage(package);
973ccf707d0SAndrew Lindesay }
974f545fe6aSStephan Aßmus
975f545fe6aSStephan Aßmus
976f545fe6aSStephan Aßmus void
RemovePackage(const PackageInfoRef & package)97718b941b4SStephan Aßmus FeaturedPackagesView::RemovePackage(const PackageInfoRef& package)
97818b941b4SStephan Aßmus {
9799883929bSAndrew Lindesay fPackagesView->RemovePackage(package);
98018b941b4SStephan Aßmus }
98118b941b4SStephan Aßmus
98218b941b4SStephan Aßmus
98318b941b4SStephan Aßmus void
Clear()984f545fe6aSStephan Aßmus FeaturedPackagesView::Clear()
985f545fe6aSStephan Aßmus {
986fa5c8097SAndrew Lindesay HDINFO("did clear the featured packages view");
9879883929bSAndrew Lindesay fPackagesView->Clear();
9889883929bSAndrew Lindesay _AdjustViews();
989f545fe6aSStephan Aßmus }
990f545fe6aSStephan Aßmus
991664372abSStephan Aßmus
992664372abSStephan Aßmus void
SelectPackage(const PackageInfoRef & package,bool scrollToEntry)99372fff6d3SJulian Harnath FeaturedPackagesView::SelectPackage(const PackageInfoRef& package,
99472fff6d3SJulian Harnath bool scrollToEntry)
995664372abSStephan Aßmus {
9969883929bSAndrew Lindesay fPackagesView->SelectPackage(package);
997ede65a8fSStephan Aßmus
9989883929bSAndrew Lindesay if (scrollToEntry) {
9999883929bSAndrew Lindesay float offset = fPackagesView->TopOfPackage(package);
10009883929bSAndrew Lindesay if (offset != B_SIZE_UNSET)
10019883929bSAndrew Lindesay fPackagesView->ScrollTo(0, offset);
100272fff6d3SJulian Harnath }
1003664372abSStephan Aßmus }
10049883929bSAndrew Lindesay
10059883929bSAndrew Lindesay
10069883929bSAndrew Lindesay void
DoLayout()10079883929bSAndrew Lindesay FeaturedPackagesView::DoLayout()
10089883929bSAndrew Lindesay {
10099883929bSAndrew Lindesay BView::DoLayout();
10109883929bSAndrew Lindesay _AdjustViews();
10119883929bSAndrew Lindesay }
10129883929bSAndrew Lindesay
10139883929bSAndrew Lindesay
10149883929bSAndrew Lindesay void
_AdjustViews()10159883929bSAndrew Lindesay FeaturedPackagesView::_AdjustViews()
10169883929bSAndrew Lindesay {
1017494bd147SAugustin Cavalier fScrollView->FrameResized(fScrollView->Frame().Width(),
1018494bd147SAugustin Cavalier fScrollView->Frame().Height());
1019664372abSStephan Aßmus }
1020