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>. 4d75b4d61SAndrew Lindesay * Copyright 2020-2021, 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: 58f0e491d3SAndrew Lindesay StackedFeaturedPackagesView(Model& model) 59f545fe6aSStephan Aßmus : 609883929bSAndrew Lindesay BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS), 61f0e491d3SAndrew Lindesay fModel(model), 62f0e491d3SAndrew Lindesay fSelectedIndex(-1), 633d869af5SStephan Aßmus fPackageListener( 64*0b69420bSAndrew Lindesay new(std::nothrow) OnePackageMessagePackageListener(this)), 65*0b69420bSAndrew Lindesay fLowestIndexAddedOrRemoved(-1) 66f545fe6aSStephan Aßmus { 671f3c1ef1SStephan Aßmus SetEventMask(B_POINTER_EVENTS); 68f545fe6aSStephan Aßmus Clear(); 69f545fe6aSStephan Aßmus } 70f545fe6aSStephan Aßmus 719883929bSAndrew Lindesay 729883929bSAndrew Lindesay virtual ~StackedFeaturedPackagesView() 73f545fe6aSStephan Aßmus { 74f545fe6aSStephan Aßmus fPackageListener->SetPackage(PackageInfoRef(NULL)); 751f3909adSAndrew Lindesay fPackageListener->ReleaseReference(); 76f545fe6aSStephan Aßmus } 77f545fe6aSStephan Aßmus 789883929bSAndrew Lindesay // #pragma mark - message handling and events 79fa19dd44Slooncraz 80f545fe6aSStephan Aßmus virtual void MessageReceived(BMessage* message) 81f545fe6aSStephan Aßmus { 82f545fe6aSStephan Aßmus switch (message->what) { 83f545fe6aSStephan Aßmus case MSG_UPDATE_PACKAGE: 84d45104b1SJulian Harnath { 859883929bSAndrew Lindesay BString name; 86fa5c8097SAndrew Lindesay if (message->FindString("name", &name) != B_OK) 87fa5c8097SAndrew Lindesay HDINFO("expected 'name' key on package update message"); 889883929bSAndrew Lindesay else 899883929bSAndrew Lindesay _HandleUpdatePackage(name); 90d45104b1SJulian Harnath break; 91d45104b1SJulian Harnath } 92fa19dd44Slooncraz 93fa19dd44Slooncraz case B_COLORS_UPDATED: 94fa19dd44Slooncraz { 959883929bSAndrew Lindesay Invalidate(); 96fa19dd44Slooncraz break; 97fa19dd44Slooncraz } 981187e806SAndrew Lindesay 991187e806SAndrew Lindesay default: 1001187e806SAndrew Lindesay BView::MessageReceived(message); 1011187e806SAndrew Lindesay break; 102f545fe6aSStephan Aßmus } 103f545fe6aSStephan Aßmus } 104f545fe6aSStephan Aßmus 1059883929bSAndrew Lindesay 1061f3c1ef1SStephan Aßmus virtual void MouseDown(BPoint where) 1071f3c1ef1SStephan Aßmus { 1089883929bSAndrew Lindesay if (Window()->IsActive() && !IsHidden()) { 109dfbf4d38SStephan Aßmus BRect bounds = Bounds(); 110dfbf4d38SStephan Aßmus BRect parentBounds = Parent()->Bounds(); 111dfbf4d38SStephan Aßmus ConvertFromParent(&parentBounds); 112dfbf4d38SStephan Aßmus bounds = bounds & parentBounds; 1139883929bSAndrew Lindesay if (bounds.Contains(where)) { 1149883929bSAndrew Lindesay _MessageSelectIndex(_IndexOfY(where.y)); 1159883929bSAndrew Lindesay MakeFocus(); 1169883929bSAndrew Lindesay } 1179883929bSAndrew Lindesay } 1189883929bSAndrew Lindesay } 119dfbf4d38SStephan Aßmus 1209883929bSAndrew Lindesay 1219883929bSAndrew Lindesay virtual void KeyDown(const char* bytes, int32 numBytes) 1229883929bSAndrew Lindesay { 1239883929bSAndrew Lindesay char key = bytes[0]; 1249883929bSAndrew Lindesay 1259883929bSAndrew Lindesay switch (key) { 1269883929bSAndrew Lindesay case B_RIGHT_ARROW: 1279883929bSAndrew Lindesay case B_DOWN_ARROW: 128d75b4d61SAndrew Lindesay { 129d75b4d61SAndrew Lindesay int32 lastIndex = static_cast<int32>(fPackages.size()) - 1; 1309883929bSAndrew Lindesay if (!IsEmpty() && fSelectedIndex != -1 131d75b4d61SAndrew Lindesay && fSelectedIndex < lastIndex) { 1329883929bSAndrew Lindesay _MessageSelectIndex(fSelectedIndex + 1); 1339883929bSAndrew Lindesay } 1349883929bSAndrew Lindesay break; 135d75b4d61SAndrew Lindesay } 1369883929bSAndrew Lindesay case B_LEFT_ARROW: 1379883929bSAndrew Lindesay case B_UP_ARROW: 1389883929bSAndrew Lindesay if (fSelectedIndex > 0) 1399883929bSAndrew Lindesay _MessageSelectIndex( fSelectedIndex - 1); 1409883929bSAndrew Lindesay break; 1419883929bSAndrew Lindesay case B_PAGE_UP: 1429883929bSAndrew Lindesay { 1439883929bSAndrew Lindesay BRect bounds = Bounds(); 1449883929bSAndrew Lindesay ScrollTo(0, fmaxf(0, bounds.top - bounds.Height())); 1459883929bSAndrew Lindesay break; 1469883929bSAndrew Lindesay } 1479883929bSAndrew Lindesay case B_PAGE_DOWN: 1489883929bSAndrew Lindesay { 1499883929bSAndrew Lindesay BRect bounds = Bounds(); 1509883929bSAndrew Lindesay float height = fPackages.size() * HEIGHT_PACKAGE; 1519883929bSAndrew Lindesay float maxScrollY = height - bounds.Height(); 1529883929bSAndrew Lindesay float pageDownScrollY = bounds.top + bounds.Height(); 1539883929bSAndrew Lindesay ScrollTo(0, fminf(maxScrollY, pageDownScrollY)); 1549883929bSAndrew Lindesay break; 1559883929bSAndrew Lindesay } 1569883929bSAndrew Lindesay default: 1579883929bSAndrew Lindesay BView::KeyDown(bytes, numBytes); 1589883929bSAndrew Lindesay break; 1599883929bSAndrew Lindesay } 1609883929bSAndrew Lindesay } 1619883929bSAndrew Lindesay 1629883929bSAndrew Lindesay 1639883929bSAndrew Lindesay /*! This method will send a message to the Window so that it can signal 1649883929bSAndrew Lindesay back to this and other views that a package has been selected. This 1659883929bSAndrew Lindesay method won't actually change the state of this view directly. 1669883929bSAndrew Lindesay */ 1679883929bSAndrew Lindesay 1689883929bSAndrew Lindesay void _MessageSelectIndex(int32 index) const 1699883929bSAndrew Lindesay { 1709883929bSAndrew Lindesay if (index != -1) { 1711f3c1ef1SStephan Aßmus BMessage message(MSG_PACKAGE_SELECTED); 172d75b4d61SAndrew Lindesay BString packageName = fPackages[index]->Name(); 173d75b4d61SAndrew Lindesay message.AddString("name", packageName); 1741f3c1ef1SStephan Aßmus Window()->PostMessage(&message); 1751f3c1ef1SStephan Aßmus } 1761f3c1ef1SStephan Aßmus } 1771f3c1ef1SStephan Aßmus 1789883929bSAndrew Lindesay 1799883929bSAndrew Lindesay virtual void FrameResized(float width, float height) 180f545fe6aSStephan Aßmus { 1819883929bSAndrew Lindesay BView::FrameResized(width, height); 182f545fe6aSStephan Aßmus 1839883929bSAndrew Lindesay // because the summary text will wrap, a resize of the frame will 1849883929bSAndrew Lindesay // result in all of the summary area needing to be redrawn. 185056d423cSStephan Aßmus 1869883929bSAndrew Lindesay BRect rectToInvalidate = Bounds(); 1879883929bSAndrew Lindesay rectToInvalidate.left = X_POSITION_SUMMARY; 1889883929bSAndrew Lindesay Invalidate(rectToInvalidate); 189f545fe6aSStephan Aßmus } 190f545fe6aSStephan Aßmus 1919883929bSAndrew Lindesay 1929883929bSAndrew Lindesay // #pragma mark - update / add / remove / clear data 1939883929bSAndrew Lindesay 1949883929bSAndrew Lindesay 195d45104b1SJulian Harnath void UpdatePackage(uint32 changeMask, const PackageInfoRef& package) 196d45104b1SJulian Harnath { 1979883929bSAndrew Lindesay // TODO; could optimize the invalidation? 1989883929bSAndrew Lindesay int32 index = _IndexOfPackage(package); 1999883929bSAndrew Lindesay if (index >= 0) { 2009883929bSAndrew Lindesay fPackages[index] = package; 2019883929bSAndrew Lindesay Invalidate(_RectOfIndex(index)); 202d45104b1SJulian Harnath } 2039883929bSAndrew Lindesay } 2049883929bSAndrew Lindesay 205d45104b1SJulian Harnath 206f545fe6aSStephan Aßmus void Clear() 207f545fe6aSStephan Aßmus { 208c4199192SAndrew Lindesay for (std::vector<PackageInfoRef>::iterator it = fPackages.begin(); 209c4199192SAndrew Lindesay it != fPackages.end(); it++) { 210c4199192SAndrew Lindesay (*it)->RemoveListener(fPackageListener); 211c4199192SAndrew Lindesay } 2129883929bSAndrew Lindesay fPackages.clear(); 2139883929bSAndrew Lindesay fSelectedIndex = -1; 2149883929bSAndrew Lindesay Invalidate(); 215f545fe6aSStephan Aßmus } 216f545fe6aSStephan Aßmus 2179883929bSAndrew Lindesay 2189883929bSAndrew Lindesay bool IsEmpty() const 2194b930ccaSStephan Aßmus { 2209883929bSAndrew Lindesay return fPackages.size() == 0; 2214b930ccaSStephan Aßmus } 2224b930ccaSStephan Aßmus 2239883929bSAndrew Lindesay 2249883929bSAndrew Lindesay void _HandleUpdatePackage(const BString& name) 22572992391SStephan Aßmus { 2269883929bSAndrew Lindesay int32 index = _IndexOfName(name); 2279883929bSAndrew Lindesay if (index != -1) 2289883929bSAndrew Lindesay Invalidate(_RectOfIndex(index)); 22972992391SStephan Aßmus } 23072992391SStephan Aßmus 2319883929bSAndrew Lindesay 2324af3fbf9SAndrew Lindesay static int _CmpProminences(int64 a, int64 b) 2334af3fbf9SAndrew Lindesay { 2344af3fbf9SAndrew Lindesay if (a <= 0) 2354af3fbf9SAndrew Lindesay a = PROMINANCE_ORDERING_MAX; 2364af3fbf9SAndrew Lindesay if (b <= 0) 2374af3fbf9SAndrew Lindesay b = PROMINANCE_ORDERING_MAX; 2384af3fbf9SAndrew Lindesay if (a == b) 2394af3fbf9SAndrew Lindesay return 0; 2404af3fbf9SAndrew Lindesay if (a > b) 2414af3fbf9SAndrew Lindesay return 1; 2424af3fbf9SAndrew Lindesay return -1; 2434af3fbf9SAndrew Lindesay } 2444af3fbf9SAndrew Lindesay 2454af3fbf9SAndrew Lindesay 2469883929bSAndrew Lindesay /*! This method will return true if the packageA is ordered before 2479883929bSAndrew Lindesay packageB. 2489883929bSAndrew Lindesay */ 2499883929bSAndrew Lindesay 2509883929bSAndrew Lindesay static bool _IsPackageBefore(const PackageInfoRef& packageA, 2519883929bSAndrew Lindesay const PackageInfoRef& packageB) 252664372abSStephan Aßmus { 253779ab335SX512 if (!packageA.IsSet() || !packageB.IsSet()) 254a5e4976dSAndrew Lindesay HDFATAL("unexpected NULL reference in a referencable"); 2554af3fbf9SAndrew Lindesay int c = _CmpProminences(packageA->Prominence(), packageB->Prominence()); 2564af3fbf9SAndrew Lindesay if (c == 0) 2574af3fbf9SAndrew Lindesay c = packageA->Title().ICompare(packageB->Title()); 2589883929bSAndrew Lindesay if (c == 0) 2599883929bSAndrew Lindesay c = packageA->Name().Compare(packageB->Name()); 2609883929bSAndrew Lindesay return c < 0; 261fa19dd44Slooncraz } 262fa19dd44Slooncraz 2634af3fbf9SAndrew Lindesay 264*0b69420bSAndrew Lindesay void BeginAddRemove() 265*0b69420bSAndrew Lindesay { 266*0b69420bSAndrew Lindesay fLowestIndexAddedOrRemoved = INT32_MAX; 267*0b69420bSAndrew Lindesay } 268*0b69420bSAndrew Lindesay 269*0b69420bSAndrew Lindesay 270*0b69420bSAndrew Lindesay void EndAddRemove() 271*0b69420bSAndrew Lindesay { 272*0b69420bSAndrew Lindesay if (fLowestIndexAddedOrRemoved < INT32_MAX) { 273*0b69420bSAndrew Lindesay if (fPackages.empty()) 274*0b69420bSAndrew Lindesay Invalidate(); 275*0b69420bSAndrew Lindesay else { 276*0b69420bSAndrew Lindesay BRect invalidRect = Bounds(); 277*0b69420bSAndrew Lindesay invalidRect.top = _YOfIndex(fLowestIndexAddedOrRemoved); 278*0b69420bSAndrew Lindesay Invalidate(invalidRect); 279*0b69420bSAndrew Lindesay } 280*0b69420bSAndrew Lindesay } 281*0b69420bSAndrew Lindesay } 282*0b69420bSAndrew Lindesay 283*0b69420bSAndrew Lindesay 2849883929bSAndrew Lindesay void AddPackage(const PackageInfoRef& package) 285fa19dd44Slooncraz { 2869883929bSAndrew Lindesay // fPackages is sorted and for this reason it is possible to find the 2879883929bSAndrew Lindesay // insertion point by identifying the first item in fPackages that does 2889883929bSAndrew Lindesay // not return true from the method '_IsPackageBefore'. 289fa19dd44Slooncraz 2904d2de4eaSAugustin Cavalier std::vector<PackageInfoRef>::iterator itInsertionPt 2919883929bSAndrew Lindesay = std::lower_bound(fPackages.begin(), fPackages.end(), package, 2929883929bSAndrew Lindesay &_IsPackageBefore); 2939883929bSAndrew Lindesay 2949883929bSAndrew Lindesay if (itInsertionPt == fPackages.end() 2959883929bSAndrew Lindesay || package->Name() != (*itInsertionPt)->Name()) { 2969883929bSAndrew Lindesay int32 insertionIndex = 2979883929bSAndrew Lindesay std::distance<std::vector<PackageInfoRef>::const_iterator>( 2989883929bSAndrew Lindesay fPackages.begin(), itInsertionPt); 2999883929bSAndrew Lindesay if (fSelectedIndex >= insertionIndex) 3009883929bSAndrew Lindesay fSelectedIndex++; 3019883929bSAndrew Lindesay fPackages.insert(itInsertionPt, package); 302c4199192SAndrew Lindesay package->AddListener(fPackageListener); 303*0b69420bSAndrew Lindesay if (insertionIndex < fLowestIndexAddedOrRemoved) 304*0b69420bSAndrew Lindesay fLowestIndexAddedOrRemoved = insertionIndex; 3059883929bSAndrew Lindesay } 306fa19dd44Slooncraz } 307664372abSStephan Aßmus 308664372abSStephan Aßmus 3099883929bSAndrew Lindesay void RemovePackage(const PackageInfoRef& package) 310d45104b1SJulian Harnath { 3119883929bSAndrew Lindesay int32 index = _IndexOfPackage(package); 3129883929bSAndrew Lindesay if (index >= 0) { 3139883929bSAndrew Lindesay if (fSelectedIndex == index) 3149883929bSAndrew Lindesay fSelectedIndex = -1; 3159883929bSAndrew Lindesay if (fSelectedIndex > index) 3169883929bSAndrew Lindesay fSelectedIndex--; 317c4199192SAndrew Lindesay fPackages[index]->RemoveListener(fPackageListener); 3189883929bSAndrew Lindesay fPackages.erase(fPackages.begin() + index); 319*0b69420bSAndrew Lindesay if (index < fLowestIndexAddedOrRemoved) 320*0b69420bSAndrew Lindesay fLowestIndexAddedOrRemoved = index; 3219883929bSAndrew Lindesay } 3229883929bSAndrew Lindesay } 323d45104b1SJulian Harnath 324d45104b1SJulian Harnath 3259883929bSAndrew Lindesay // #pragma mark - selection and index handling 326d45104b1SJulian Harnath 327d45104b1SJulian Harnath 3289883929bSAndrew Lindesay void SelectPackage(const PackageInfoRef& package) 3299883929bSAndrew Lindesay { 3309883929bSAndrew Lindesay _SelectIndex(_IndexOfPackage(package)); 3319883929bSAndrew Lindesay } 3329883929bSAndrew Lindesay 3339883929bSAndrew Lindesay 3349883929bSAndrew Lindesay void _SelectIndex(int32 index) 3359883929bSAndrew Lindesay { 3369883929bSAndrew Lindesay if (index != fSelectedIndex) { 3379883929bSAndrew Lindesay int32 previousSelectedIndex = fSelectedIndex; 3389883929bSAndrew Lindesay fSelectedIndex = index; 3399883929bSAndrew Lindesay if (fSelectedIndex >= 0) 3409883929bSAndrew Lindesay Invalidate(_RectOfIndex(fSelectedIndex)); 3419883929bSAndrew Lindesay if (previousSelectedIndex >= 0) 3429883929bSAndrew Lindesay Invalidate(_RectOfIndex(previousSelectedIndex)); 3439883929bSAndrew Lindesay _EnsureIndexVisible(index); 3449883929bSAndrew Lindesay } 3459883929bSAndrew Lindesay } 3469883929bSAndrew Lindesay 3479883929bSAndrew Lindesay 3489883929bSAndrew Lindesay int32 _IndexOfPackage(PackageInfoRef package) const 3499883929bSAndrew Lindesay { 350c4199192SAndrew Lindesay std::vector<PackageInfoRef>::const_iterator it 351c4199192SAndrew Lindesay = std::lower_bound(fPackages.begin(), fPackages.end(), package, 352c4199192SAndrew Lindesay &_IsPackageBefore); 353c4199192SAndrew Lindesay 354c4199192SAndrew Lindesay return (it == fPackages.end() || (*it)->Name() != package->Name()) 355c4199192SAndrew Lindesay ? -1 : it - fPackages.begin(); 3569883929bSAndrew Lindesay } 3579883929bSAndrew Lindesay 3589883929bSAndrew Lindesay 3599883929bSAndrew Lindesay int32 _IndexOfName(const BString& name) const 3609883929bSAndrew Lindesay { 3619883929bSAndrew Lindesay // TODO; slow linear search. 3629883929bSAndrew Lindesay // the fPackages is not sorted on name and for this reason it is not 3639883929bSAndrew Lindesay // possible to do a binary search. 3649883929bSAndrew Lindesay for (uint32 i = 0; i < fPackages.size(); i++) { 3659883929bSAndrew Lindesay if (fPackages[i]->Name() == name) 3669883929bSAndrew Lindesay return i; 3679883929bSAndrew Lindesay } 3689883929bSAndrew Lindesay return -1; 3699883929bSAndrew Lindesay } 3709883929bSAndrew Lindesay 3719883929bSAndrew Lindesay 3729883929bSAndrew Lindesay // #pragma mark - drawing and rendering 3739883929bSAndrew Lindesay 3749883929bSAndrew Lindesay 3759883929bSAndrew Lindesay virtual void Draw(BRect updateRect) 3769883929bSAndrew Lindesay { 3779883929bSAndrew Lindesay SetHighUIColor(B_LIST_BACKGROUND_COLOR); 3789883929bSAndrew Lindesay FillRect(updateRect); 3799883929bSAndrew Lindesay 3809883929bSAndrew Lindesay int32 iStart = _IndexRoundedOfY(updateRect.top); 3819883929bSAndrew Lindesay 3829883929bSAndrew Lindesay if (iStart != -1) { 3839883929bSAndrew Lindesay int32 iEnd = _IndexRoundedOfY(updateRect.bottom); 3849883929bSAndrew Lindesay for (int32 i = iStart; i <= iEnd; i++) 3859883929bSAndrew Lindesay _DrawPackageAtIndex(updateRect, i); 3869883929bSAndrew Lindesay } 3879883929bSAndrew Lindesay } 3889883929bSAndrew Lindesay 3899883929bSAndrew Lindesay 3909883929bSAndrew Lindesay void _DrawPackageAtIndex(BRect updateRect, int32 index) 3919883929bSAndrew Lindesay { 3929883929bSAndrew Lindesay _DrawPackage(updateRect, fPackages[index], index, _YOfIndex(index), 3939883929bSAndrew Lindesay index == fSelectedIndex); 3949883929bSAndrew Lindesay } 3959883929bSAndrew Lindesay 3969883929bSAndrew Lindesay 3979883929bSAndrew Lindesay void _DrawPackage(BRect updateRect, PackageInfoRef pkg, int index, float y, 3989883929bSAndrew Lindesay bool selected) 3999883929bSAndrew Lindesay { 4009883929bSAndrew Lindesay if (selected) { 4019883929bSAndrew Lindesay SetLowUIColor(B_LIST_SELECTED_BACKGROUND_COLOR); 4029883929bSAndrew Lindesay FillRect(_RectOfY(y), B_SOLID_LOW); 403d45104b1SJulian Harnath } else { 4049883929bSAndrew Lindesay SetLowUIColor(B_LIST_BACKGROUND_COLOR); 405d45104b1SJulian Harnath } 4069883929bSAndrew Lindesay // TODO; optimization; the updateRect may only cover some of this? 4079883929bSAndrew Lindesay _DrawPackageIcon(updateRect, pkg, y, selected); 4089883929bSAndrew Lindesay _DrawPackageTitle(updateRect, pkg, y, selected); 4099883929bSAndrew Lindesay _DrawPackagePublisher(updateRect, pkg, y, selected); 4109883929bSAndrew Lindesay _DrawPackageRating(updateRect, pkg, y, selected); 4119883929bSAndrew Lindesay _DrawPackageSummary(updateRect, pkg, y, selected); 412d45104b1SJulian Harnath } 413d45104b1SJulian Harnath 414d45104b1SJulian Harnath 4159883929bSAndrew Lindesay void _DrawPackageIcon(BRect updateRect, PackageInfoRef pkg, float y, 4169883929bSAndrew Lindesay bool selected) 417d45104b1SJulian Harnath { 418f0e491d3SAndrew Lindesay BitmapRef icon; 419f0e491d3SAndrew Lindesay status_t iconResult = fModel.GetPackageIconRepository().GetIcon( 420f0e491d3SAndrew Lindesay pkg->Name(), BITMAP_SIZE_64, icon); 4219883929bSAndrew Lindesay 422f0e491d3SAndrew Lindesay if (iconResult == B_OK) { 423779ab335SX512 if (icon.IsSet()) { 4249883929bSAndrew Lindesay float inset = (HEIGHT_PACKAGE - SIZE_ICON) / 2.0; 4259883929bSAndrew Lindesay BRect targetRect = BRect(inset, y + inset, SIZE_ICON + inset, 4269883929bSAndrew Lindesay y + SIZE_ICON + inset); 427f0e491d3SAndrew Lindesay const BBitmap* bitmap = icon->Bitmap(BITMAP_SIZE_64); 4289883929bSAndrew Lindesay SetDrawingMode(B_OP_ALPHA); 4299883929bSAndrew Lindesay DrawBitmap(bitmap, bitmap->Bounds(), targetRect, 4309883929bSAndrew Lindesay B_FILTER_BITMAP_BILINEAR); 431d45104b1SJulian Harnath } 4329883929bSAndrew Lindesay } 433f0e491d3SAndrew Lindesay } 4349883929bSAndrew Lindesay 4359883929bSAndrew Lindesay 4369883929bSAndrew Lindesay void _DrawPackageTitle(BRect updateRect, PackageInfoRef pkg, float y, 4379883929bSAndrew Lindesay bool selected) 4389883929bSAndrew Lindesay { 4399883929bSAndrew Lindesay static BFont* sFont = NULL; 4409883929bSAndrew Lindesay 4419883929bSAndrew Lindesay if (sFont == NULL) { 4429883929bSAndrew Lindesay sFont = new BFont(be_plain_font); 4439883929bSAndrew Lindesay GetFont(sFont); 4449883929bSAndrew Lindesay font_family family; 4459883929bSAndrew Lindesay font_style style; 4469883929bSAndrew Lindesay sFont->SetSize(ceilf(sFont->Size() * 1.8f)); 4479883929bSAndrew Lindesay sFont->GetFamilyAndStyle(&family, &style); 4489883929bSAndrew Lindesay sFont->SetFamilyAndStyle(family, "Bold"); 4499883929bSAndrew Lindesay } 4509883929bSAndrew Lindesay 4519883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY); 4529883929bSAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR 4539883929bSAndrew Lindesay : B_LIST_ITEM_TEXT_COLOR); 4549883929bSAndrew Lindesay SetFont(sFont); 4559883929bSAndrew Lindesay BPoint pt(HEIGHT_PACKAGE, y + (HEIGHT_PACKAGE * Y_PROPORTION_TITLE)); 4569883929bSAndrew Lindesay DrawString(pkg->Title(), pt); 4579883929bSAndrew Lindesay 4589883929bSAndrew Lindesay if (pkg->State() == ACTIVATED) { 4599883929bSAndrew Lindesay const BBitmap* bitmap = sInstalledIcon->Bitmap( 460f0e491d3SAndrew Lindesay BITMAP_SIZE_16); 4619883929bSAndrew Lindesay float stringWidth = StringWidth(pkg->Title()); 4629883929bSAndrew Lindesay float offsetX = pt.x + stringWidth + PADDING; 4639883929bSAndrew Lindesay BRect targetRect(offsetX, pt.y - 16, offsetX + 16, pt.y); 4649883929bSAndrew Lindesay SetDrawingMode(B_OP_ALPHA); 4659883929bSAndrew Lindesay DrawBitmap(bitmap, bitmap->Bounds(), targetRect, 4669883929bSAndrew Lindesay B_FILTER_BITMAP_BILINEAR); 4679883929bSAndrew Lindesay } 4689883929bSAndrew Lindesay } 4699883929bSAndrew Lindesay 4709883929bSAndrew Lindesay 4719883929bSAndrew Lindesay void _DrawPackagePublisher(BRect updateRect, PackageInfoRef pkg, float y, 4729883929bSAndrew Lindesay bool selected) 4739883929bSAndrew Lindesay { 4749883929bSAndrew Lindesay static BFont* sFont = NULL; 4759883929bSAndrew Lindesay 4769883929bSAndrew Lindesay if (sFont == NULL) { 4779883929bSAndrew Lindesay sFont = new BFont(be_plain_font); 4789883929bSAndrew Lindesay font_family family; 4799883929bSAndrew Lindesay font_style style; 4809883929bSAndrew Lindesay sFont->SetSize(std::max(9.0f, floorf(sFont->Size() * 0.92f))); 4819883929bSAndrew Lindesay sFont->GetFamilyAndStyle(&family, &style); 4829883929bSAndrew Lindesay sFont->SetFamilyAndStyle(family, "Italic"); 4839883929bSAndrew Lindesay } 4849883929bSAndrew Lindesay 4859883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY); 4869883929bSAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR 4879883929bSAndrew Lindesay : B_LIST_ITEM_TEXT_COLOR); 4889883929bSAndrew Lindesay SetFont(sFont); 4899883929bSAndrew Lindesay 4909883929bSAndrew Lindesay float maxTextWidth = (X_POSITION_RATING - HEIGHT_PACKAGE) - PADDING; 4919883929bSAndrew Lindesay BString publisherName(pkg->Publisher().Name()); 4929883929bSAndrew Lindesay TruncateString(&publisherName, B_TRUNCATE_END, maxTextWidth); 4939883929bSAndrew Lindesay 4949883929bSAndrew Lindesay DrawString(publisherName, BPoint(HEIGHT_PACKAGE, 4959883929bSAndrew Lindesay y + (HEIGHT_PACKAGE * Y_PROPORTION_PUBLISHER))); 4969883929bSAndrew Lindesay } 4979883929bSAndrew Lindesay 4989883929bSAndrew Lindesay 4999883929bSAndrew Lindesay // TODO; show the sample size 5009883929bSAndrew Lindesay void _DrawPackageRating(BRect updateRect, PackageInfoRef pkg, float y, 5019883929bSAndrew Lindesay bool selected) 5029883929bSAndrew Lindesay { 5039883929bSAndrew Lindesay BPoint at(X_POSITION_RATING, 5049883929bSAndrew Lindesay y + (HEIGHT_PACKAGE - SIZE_RATING_STAR) / 2.0f); 5059883929bSAndrew Lindesay RatingUtils::Draw(this, at, 5069883929bSAndrew Lindesay pkg->CalculateRatingSummary().averageRating); 5079883929bSAndrew Lindesay } 5089883929bSAndrew Lindesay 5099883929bSAndrew Lindesay 5109883929bSAndrew Lindesay // TODO; handle multi-line rendering of the text 5119883929bSAndrew Lindesay void _DrawPackageSummary(BRect updateRect, PackageInfoRef pkg, float y, 5129883929bSAndrew Lindesay bool selected) 5139883929bSAndrew Lindesay { 5149883929bSAndrew Lindesay BRect bounds = Bounds(); 5159883929bSAndrew Lindesay 5169883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY); 5179883929bSAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR 5189883929bSAndrew Lindesay : B_LIST_ITEM_TEXT_COLOR); 5199883929bSAndrew Lindesay SetFont(be_plain_font); 5209883929bSAndrew Lindesay 5219883929bSAndrew Lindesay float maxTextWidth = bounds.Width() - X_POSITION_SUMMARY - PADDING; 5229883929bSAndrew Lindesay BString summary(pkg->ShortDescription()); 5239883929bSAndrew Lindesay TruncateString(&summary, B_TRUNCATE_END, maxTextWidth); 5249883929bSAndrew Lindesay 5259883929bSAndrew Lindesay DrawString(summary, BPoint(X_POSITION_SUMMARY, 5269883929bSAndrew Lindesay y + (HEIGHT_PACKAGE * 0.5))); 5279883929bSAndrew Lindesay } 5289883929bSAndrew Lindesay 5299883929bSAndrew Lindesay 5309883929bSAndrew Lindesay // #pragma mark - geometry and scrolling 5319883929bSAndrew Lindesay 5329883929bSAndrew Lindesay 5339883929bSAndrew Lindesay /*! This method will make sure that the package at the given index is 5349883929bSAndrew Lindesay visible. If the whole of the package can be seen already then it will 5359883929bSAndrew Lindesay do nothing. If the package is located above the visible region then it 5369883929bSAndrew Lindesay will scroll up to it. If the package is located below the visible 5379883929bSAndrew Lindesay region then it will scroll down to it. 5389883929bSAndrew Lindesay */ 5399883929bSAndrew Lindesay 5409883929bSAndrew Lindesay void _EnsureIndexVisible(int32 index) 5419883929bSAndrew Lindesay { 5429883929bSAndrew Lindesay if (!_IsIndexEntirelyVisible(index)) { 5439883929bSAndrew Lindesay BRect bounds = Bounds(); 5449883929bSAndrew Lindesay int32 indexOfCentreVisible = _IndexOfY( 5459883929bSAndrew Lindesay bounds.top + bounds.Height() / 2); 5469883929bSAndrew Lindesay if (index < indexOfCentreVisible) 5479883929bSAndrew Lindesay ScrollTo(0, _YOfIndex(index)); 5489883929bSAndrew Lindesay else { 5499883929bSAndrew Lindesay float scrollPointY = (_YOfIndex(index) + HEIGHT_PACKAGE) 5509883929bSAndrew Lindesay - bounds.Height(); 5519883929bSAndrew Lindesay ScrollTo(0, scrollPointY); 5529883929bSAndrew Lindesay } 5539883929bSAndrew Lindesay } 5549883929bSAndrew Lindesay } 5559883929bSAndrew Lindesay 5569883929bSAndrew Lindesay 5579883929bSAndrew Lindesay /*! This method will return true if the package at the supplied index is 5589883929bSAndrew Lindesay entirely visible. 5599883929bSAndrew Lindesay */ 5609883929bSAndrew Lindesay 5619883929bSAndrew Lindesay bool _IsIndexEntirelyVisible(int32 index) 5629883929bSAndrew Lindesay { 5639883929bSAndrew Lindesay BRect bounds = Bounds(); 5649883929bSAndrew Lindesay return bounds == (bounds | _RectOfIndex(index)); 5659883929bSAndrew Lindesay } 5669883929bSAndrew Lindesay 5679883929bSAndrew Lindesay 5689883929bSAndrew Lindesay BRect _RectOfIndex(int32 index) const 5699883929bSAndrew Lindesay { 5709883929bSAndrew Lindesay if (index < 0) 5719883929bSAndrew Lindesay return BRect(0, 0, 0, 0); 5729883929bSAndrew Lindesay return _RectOfY(_YOfIndex(index)); 5739883929bSAndrew Lindesay } 5749883929bSAndrew Lindesay 5759883929bSAndrew Lindesay 5769883929bSAndrew Lindesay /*! Provides the top coordinate (offset from the top of view) of the package 5779883929bSAndrew Lindesay supplied. If the package does not exist in the view then the coordinate 5789883929bSAndrew Lindesay returned will be B_SIZE_UNSET. 5799883929bSAndrew Lindesay */ 5809883929bSAndrew Lindesay 5819883929bSAndrew Lindesay float TopOfPackage(const PackageInfoRef& package) 5829883929bSAndrew Lindesay { 583779ab335SX512 if (package.IsSet()) { 5849883929bSAndrew Lindesay int index = _IndexOfPackage(package); 5859883929bSAndrew Lindesay if (-1 != index) 5869883929bSAndrew Lindesay return _YOfIndex(index); 5879883929bSAndrew Lindesay } 5889883929bSAndrew Lindesay return B_SIZE_UNSET; 5899883929bSAndrew Lindesay } 5909883929bSAndrew Lindesay 5919883929bSAndrew Lindesay 5929883929bSAndrew Lindesay BRect _RectOfY(float y) const 5939883929bSAndrew Lindesay { 5949883929bSAndrew Lindesay return BRect(0, y, Bounds().Width(), y + HEIGHT_PACKAGE); 5959883929bSAndrew Lindesay } 5969883929bSAndrew Lindesay 5979883929bSAndrew Lindesay 5989883929bSAndrew Lindesay float _YOfIndex(int32 i) const 5999883929bSAndrew Lindesay { 6009883929bSAndrew Lindesay return i * HEIGHT_PACKAGE; 6019883929bSAndrew Lindesay } 6029883929bSAndrew Lindesay 6039883929bSAndrew Lindesay 6049883929bSAndrew Lindesay /*! Finds the offset into the list of packages for the y-coord in the view's 6059883929bSAndrew Lindesay coordinate space. If the y is above or below the list of packages then 6069883929bSAndrew Lindesay this will return -1 to signal this. 6079883929bSAndrew Lindesay */ 6089883929bSAndrew Lindesay 6099883929bSAndrew Lindesay int32 _IndexOfY(float y) const 6109883929bSAndrew Lindesay { 6119883929bSAndrew Lindesay if (fPackages.empty()) 6129883929bSAndrew Lindesay return -1; 613d75b4d61SAndrew Lindesay int32 i = static_cast<int32>(y / HEIGHT_PACKAGE); 614d75b4d61SAndrew Lindesay if (i < 0 || i >= static_cast<int32>(fPackages.size())) 6159883929bSAndrew Lindesay return -1; 6169883929bSAndrew Lindesay return i; 6179883929bSAndrew Lindesay } 6189883929bSAndrew Lindesay 6199883929bSAndrew Lindesay 6209883929bSAndrew Lindesay /*! Find the offset into the list of packages for the y-coord in the view's 6219883929bSAndrew Lindesay coordinate space. If the y is above or below the list of packages then 6229883929bSAndrew Lindesay this will return the first or last package index respectively. If there 6239883929bSAndrew Lindesay are no packages then this will return -1; 6249883929bSAndrew Lindesay */ 6259883929bSAndrew Lindesay 6269883929bSAndrew Lindesay int32 _IndexRoundedOfY(float y) const 6279883929bSAndrew Lindesay { 6289883929bSAndrew Lindesay if (fPackages.empty()) 6299883929bSAndrew Lindesay return -1; 630d75b4d61SAndrew Lindesay int32 i = static_cast<int32>(y / HEIGHT_PACKAGE); 6319883929bSAndrew Lindesay if (i < 0) 6329883929bSAndrew Lindesay return 0; 6339883929bSAndrew Lindesay return std::min(i, (int32) (fPackages.size() - 1)); 6349883929bSAndrew Lindesay } 6359883929bSAndrew Lindesay 6369883929bSAndrew Lindesay 6379883929bSAndrew Lindesay virtual BSize PreferredSize() 6389883929bSAndrew Lindesay { 6399883929bSAndrew Lindesay return BSize(B_SIZE_UNLIMITED, HEIGHT_PACKAGE * fPackages.size()); 6409883929bSAndrew Lindesay } 6419883929bSAndrew Lindesay 642d45104b1SJulian Harnath 643f545fe6aSStephan Aßmus private: 644f0e491d3SAndrew Lindesay Model& fModel; 6459883929bSAndrew Lindesay std::vector<PackageInfoRef> 6469883929bSAndrew Lindesay fPackages; 6479883929bSAndrew Lindesay int32 fSelectedIndex; 6489883929bSAndrew Lindesay OnePackageMessagePackageListener* 6499883929bSAndrew Lindesay fPackageListener; 650*0b69420bSAndrew Lindesay int32 fLowestIndexAddedOrRemoved; 651f545fe6aSStephan Aßmus }; 652f545fe6aSStephan Aßmus 653f545fe6aSStephan Aßmus 654f545fe6aSStephan Aßmus // #pragma mark - FeaturedPackagesView 655f545fe6aSStephan Aßmus 656f545fe6aSStephan Aßmus 657f0e491d3SAndrew Lindesay FeaturedPackagesView::FeaturedPackagesView(Model& model) 658f545fe6aSStephan Aßmus : 659f0e491d3SAndrew Lindesay BView(B_TRANSLATE("Featured packages"), 0), 660f0e491d3SAndrew Lindesay fModel(model) 661f545fe6aSStephan Aßmus { 662f0e491d3SAndrew Lindesay fPackagesView = new StackedFeaturedPackagesView(fModel); 663f545fe6aSStephan Aßmus 6649883929bSAndrew Lindesay fScrollView = new BScrollView("featured packages scroll view", 665494bd147SAugustin Cavalier fPackagesView, 0, false, true, B_FANCY_BORDER); 6668507d262SStephan Aßmus 667f545fe6aSStephan Aßmus BLayoutBuilder::Group<>(this) 6689883929bSAndrew Lindesay .Add(fScrollView, 1.0f); 669f545fe6aSStephan Aßmus } 670f545fe6aSStephan Aßmus 671f545fe6aSStephan Aßmus 672f545fe6aSStephan Aßmus FeaturedPackagesView::~FeaturedPackagesView() 673f545fe6aSStephan Aßmus { 674f545fe6aSStephan Aßmus } 675f545fe6aSStephan Aßmus 676f545fe6aSStephan Aßmus 677*0b69420bSAndrew Lindesay void 678*0b69420bSAndrew Lindesay FeaturedPackagesView::BeginAddRemove() 679*0b69420bSAndrew Lindesay { 680*0b69420bSAndrew Lindesay fPackagesView->BeginAddRemove(); 681*0b69420bSAndrew Lindesay } 682*0b69420bSAndrew Lindesay 683*0b69420bSAndrew Lindesay 684*0b69420bSAndrew Lindesay void 685*0b69420bSAndrew Lindesay FeaturedPackagesView::EndAddRemove() 686*0b69420bSAndrew Lindesay { 687*0b69420bSAndrew Lindesay fPackagesView->EndAddRemove(); 688*0b69420bSAndrew Lindesay _AdjustViews(); 689*0b69420bSAndrew Lindesay } 690*0b69420bSAndrew Lindesay 691*0b69420bSAndrew Lindesay 692ccf707d0SAndrew Lindesay /*! This method will add the package into the list to be displayed. The 693ccf707d0SAndrew Lindesay insertion will occur in alphabetical order. 694ccf707d0SAndrew Lindesay */ 695ccf707d0SAndrew Lindesay 696f545fe6aSStephan Aßmus void 697f545fe6aSStephan Aßmus FeaturedPackagesView::AddPackage(const PackageInfoRef& package) 698f545fe6aSStephan Aßmus { 6999883929bSAndrew Lindesay fPackagesView->AddPackage(package); 700ccf707d0SAndrew Lindesay } 701f545fe6aSStephan Aßmus 702f545fe6aSStephan Aßmus 703f545fe6aSStephan Aßmus void 70418b941b4SStephan Aßmus FeaturedPackagesView::RemovePackage(const PackageInfoRef& package) 70518b941b4SStephan Aßmus { 7069883929bSAndrew Lindesay fPackagesView->RemovePackage(package); 70718b941b4SStephan Aßmus } 70818b941b4SStephan Aßmus 70918b941b4SStephan Aßmus 71018b941b4SStephan Aßmus void 711f545fe6aSStephan Aßmus FeaturedPackagesView::Clear() 712f545fe6aSStephan Aßmus { 713fa5c8097SAndrew Lindesay HDINFO("did clear the featured packages view"); 7149883929bSAndrew Lindesay fPackagesView->Clear(); 7159883929bSAndrew Lindesay _AdjustViews(); 716f545fe6aSStephan Aßmus } 717f545fe6aSStephan Aßmus 718664372abSStephan Aßmus 719664372abSStephan Aßmus void 72072fff6d3SJulian Harnath FeaturedPackagesView::SelectPackage(const PackageInfoRef& package, 72172fff6d3SJulian Harnath bool scrollToEntry) 722664372abSStephan Aßmus { 7239883929bSAndrew Lindesay fPackagesView->SelectPackage(package); 724ede65a8fSStephan Aßmus 7259883929bSAndrew Lindesay if (scrollToEntry) { 7269883929bSAndrew Lindesay float offset = fPackagesView->TopOfPackage(package); 7279883929bSAndrew Lindesay if (offset != B_SIZE_UNSET) 7289883929bSAndrew Lindesay fPackagesView->ScrollTo(0, offset); 72972fff6d3SJulian Harnath } 730664372abSStephan Aßmus } 7319883929bSAndrew Lindesay 7329883929bSAndrew Lindesay 7339883929bSAndrew Lindesay void 7349883929bSAndrew Lindesay FeaturedPackagesView::DoLayout() 7359883929bSAndrew Lindesay { 7369883929bSAndrew Lindesay BView::DoLayout(); 7379883929bSAndrew Lindesay _AdjustViews(); 7389883929bSAndrew Lindesay } 7399883929bSAndrew Lindesay 7409883929bSAndrew Lindesay 7419883929bSAndrew Lindesay void 7429883929bSAndrew Lindesay FeaturedPackagesView::_AdjustViews() 7439883929bSAndrew Lindesay { 744494bd147SAugustin Cavalier fScrollView->FrameResized(fScrollView->Frame().Width(), 745494bd147SAugustin Cavalier fScrollView->Frame().Height()); 746664372abSStephan Aßmus } 747664372abSStephan Aßmus 748056d423cSStephan Aßmus 749056d423cSStephan Aßmus void 750056d423cSStephan Aßmus FeaturedPackagesView::CleanupIcons() 751056d423cSStephan Aßmus { 752056d423cSStephan Aßmus sInstalledIcon.Unset(); 753056d423cSStephan Aßmus } 754