1 /* 2 * Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 #include "FeaturedPackagesView.h" 7 8 #include <stdio.h> 9 10 #include <Catalog.h> 11 #include <Font.h> 12 #include <LayoutBuilder.h> 13 #include <Message.h> 14 #include <ScrollView.h> 15 #include <StringView.h> 16 #include <SpaceLayoutItem.h> 17 18 #include "BitmapView.h" 19 #include "MainWindow.h" 20 #include "MarkupTextView.h" 21 #include "MessagePackageListener.h" 22 #include "RatingView.h" 23 #include "ScrollableGroupView.h" 24 25 26 #undef B_TRANSLATION_CONTEXT 27 #define B_TRANSLATION_CONTEXT "FeaturedPackagesView" 28 29 30 static const rgb_color kLightBlack = (rgb_color){ 60, 60, 60, 255 }; 31 32 static BitmapRef sInstalledIcon(new(std::nothrow) SharedBitmap(504), true); 33 34 35 // #pragma mark - PackageView 36 37 38 class PackageView : public BGroupView { 39 public: 40 PackageView() 41 : 42 BGroupView("package view", B_HORIZONTAL), 43 fPackageListener( 44 new(std::nothrow) OnePackageMessagePackageListener(this)), 45 fSelected(false) 46 { 47 SetViewColor(255, 255, 255); 48 SetEventMask(B_POINTER_EVENTS); 49 50 fIconView = new BitmapView("package icon view"); 51 fInstalledIconView = new BitmapView("installed icon view"); 52 fTitleView = new BStringView("package title view", ""); 53 fPublisherView = new BStringView("package publisher view", ""); 54 55 // Title font 56 BFont font; 57 GetFont(&font); 58 font_family family; 59 font_style style; 60 font.SetSize(ceilf(font.Size() * 1.8f)); 61 font.GetFamilyAndStyle(&family, &style); 62 font.SetFamilyAndStyle(family, "Bold"); 63 fTitleView->SetFont(&font); 64 65 // Publisher font 66 GetFont(&font); 67 font.SetSize(std::max(9.0f, floorf(font.Size() * 0.92f))); 68 font.SetFamilyAndStyle(family, "Italic"); 69 fPublisherView->SetFont(&font); 70 fPublisherView->SetHighColor(kLightBlack); 71 72 // Summary text view 73 fSummaryView = new MarkupTextView("package summary"); 74 fSummaryView->SetSelectionEnabled(false); 75 76 // Rating view 77 fRatingView = new RatingView("package rating view"); 78 79 fAvgRating = new BStringView("package average rating", ""); 80 fAvgRating->SetFont(&font); 81 fAvgRating->SetHighColor(kLightBlack); 82 83 fVoteInfo = new BStringView("package vote info", ""); 84 // small font 85 GetFont(&font); 86 font.SetSize(std::max(9.0f, floorf(font.Size() * 0.85f))); 87 fVoteInfo->SetFont(&font); 88 fVoteInfo->SetHighColor(kLightBlack); 89 90 BLayoutBuilder::Group<>(this) 91 .Add(fIconView) 92 .AddGroup(B_VERTICAL, 1.0f, 2.2f) 93 .AddGroup(B_HORIZONTAL) 94 .Add(fTitleView) 95 .Add(fInstalledIconView) 96 .AddGlue() 97 .End() 98 .Add(fPublisherView) 99 .SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)) 100 .End() 101 .AddGlue(0.1f) 102 .AddGroup(B_HORIZONTAL, 0.8f) 103 .Add(fRatingView) 104 .Add(fAvgRating) 105 .Add(fVoteInfo) 106 .End() 107 .AddGlue(0.2f) 108 .Add(fSummaryView, 2.0f) 109 110 .SetInsets(B_USE_WINDOW_INSETS) 111 ; 112 113 Clear(); 114 } 115 116 virtual ~PackageView() 117 { 118 fPackageListener->SetPackage(PackageInfoRef(NULL)); 119 delete fPackageListener; 120 } 121 122 virtual void MessageReceived(BMessage* message) 123 { 124 switch (message->what) { 125 case B_MOUSE_WHEEL_CHANGED: 126 Window()->PostMessage(message, Parent()); 127 break; 128 129 case MSG_UPDATE_PACKAGE: 130 SetPackage(fPackageListener->Package()); 131 break; 132 } 133 } 134 135 virtual void MouseDown(BPoint where) 136 { 137 BRect bounds = Bounds(); 138 BRect parentBounds = Parent()->Bounds(); 139 ConvertFromParent(&parentBounds); 140 bounds = bounds & parentBounds; 141 142 if (bounds.Contains(where) && Window()->IsActive()) { 143 BMessage message(MSG_PACKAGE_SELECTED); 144 message.AddString("name", PackageName()); 145 Window()->PostMessage(&message); 146 } 147 } 148 149 void SetPackage(const PackageInfoRef& package) 150 { 151 fPackageListener->SetPackage(package); 152 153 if (package->Icon().Get() != NULL) { 154 fIconView->SetBitmap(package->Icon(), SharedBitmap::SIZE_64); 155 } else 156 fIconView->UnsetBitmap(); 157 158 if (package->State() == ACTIVATED) { 159 fInstalledIconView->SetBitmap(sInstalledIcon, 160 SharedBitmap::SIZE_16); 161 } else 162 fInstalledIconView->UnsetBitmap(); 163 164 fTitleView->SetText(package->Title()); 165 166 BString publisher = package->Publisher().Name(); 167 fPublisherView->SetText(publisher); 168 169 BString summary = package->ShortDescription(); 170 fSummaryView->SetText(summary); 171 172 RatingSummary ratingSummary = package->CalculateRatingSummary(); 173 174 fRatingView->SetRating(ratingSummary.averageRating); 175 176 if (ratingSummary.ratingCount > 0) { 177 BString avgRating; 178 avgRating.SetToFormat("%.1f", ratingSummary.averageRating); 179 fAvgRating->SetText(avgRating); 180 181 BString votes; 182 votes.SetToFormat("%d", ratingSummary.ratingCount); 183 184 BString voteInfo(B_TRANSLATE("(%Votes%)")); 185 voteInfo.ReplaceAll("%Votes%", votes); 186 187 fVoteInfo->SetText(voteInfo); 188 } else { 189 fAvgRating->SetText(""); 190 fVoteInfo->SetText(""); 191 } 192 193 InvalidateLayout(); 194 Invalidate(); 195 } 196 197 void Clear() 198 { 199 fPackageListener->SetPackage(PackageInfoRef(NULL)); 200 201 fIconView->UnsetBitmap(); 202 fInstalledIconView->UnsetBitmap(); 203 fTitleView->SetText(""); 204 fPublisherView->SetText(""); 205 fSummaryView->SetText(""); 206 fRatingView->SetRating(-1.0f); 207 fAvgRating->SetText(""); 208 fVoteInfo->SetText(""); 209 } 210 211 const char* PackageTitle() const 212 { 213 return fTitleView->Text(); 214 } 215 216 const char* PackageName() const 217 { 218 if (fPackageListener->Package().Get() != NULL) 219 return fPackageListener->Package()->Name(); 220 else 221 return ""; 222 } 223 224 void SetSelected(bool selected) 225 { 226 if (fSelected == selected) 227 return; 228 fSelected = selected; 229 230 rgb_color bgColor; 231 if (fSelected) 232 bgColor = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR); 233 else 234 bgColor = (rgb_color){ 255, 255, 255, 255 }; 235 236 List<BView*, true> views; 237 238 views.Add(this); 239 views.Add(fIconView); 240 views.Add(fInstalledIconView); 241 views.Add(fTitleView); 242 views.Add(fPublisherView); 243 views.Add(fSummaryView); 244 views.Add(fRatingView); 245 views.Add(fAvgRating); 246 views.Add(fVoteInfo); 247 248 for (int32 i = 0; i < views.CountItems(); i++) { 249 BView* view = views.ItemAtFast(i); 250 251 view->SetViewColor(bgColor); 252 view->SetLowColor(bgColor); 253 view->Invalidate(); 254 } 255 } 256 257 private: 258 OnePackageMessagePackageListener* fPackageListener; 259 260 BitmapView* fIconView; 261 BitmapView* fInstalledIconView; 262 263 BStringView* fTitleView; 264 BStringView* fPublisherView; 265 266 MarkupTextView* fSummaryView; 267 268 RatingView* fRatingView; 269 BStringView* fAvgRating; 270 BStringView* fVoteInfo; 271 272 bool fSelected; 273 274 BString fPackageName; 275 }; 276 277 278 // #pragma mark - FeaturedPackagesView 279 280 281 FeaturedPackagesView::FeaturedPackagesView() 282 : 283 BView("featured package view", 0) 284 { 285 BGroupLayout* layout = new BGroupLayout(B_VERTICAL); 286 SetLayout(layout); 287 288 ScrollableGroupView* containerView = new ScrollableGroupView(); 289 containerView->SetViewColor(255, 255, 255); 290 fPackageListLayout = containerView->GroupLayout(); 291 292 BScrollView* scrollView = new BScrollView( 293 "featured packages scroll view", containerView, 294 0, false, true, B_FANCY_BORDER); 295 296 BScrollBar* scrollBar = scrollView->ScrollBar(B_VERTICAL); 297 if (scrollBar != NULL) 298 scrollBar->SetSteps(10.0f, 20.0f); 299 300 BLayoutBuilder::Group<>(this) 301 .Add(scrollView, 1.0f) 302 ; 303 } 304 305 306 FeaturedPackagesView::~FeaturedPackagesView() 307 { 308 } 309 310 311 void 312 FeaturedPackagesView::AddPackage(const PackageInfoRef& package) 313 { 314 // Find insertion index (alphabetical) 315 int32 index = 0; 316 for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) { 317 PackageView* view = dynamic_cast<PackageView*>(item->View()); 318 if (view == NULL) 319 break; 320 321 BString name = view->PackageName(); 322 if (name == package->Name()) { 323 // Don't add packages more than once 324 return; 325 } 326 327 BString title = view->PackageTitle(); 328 if (title.Compare(package->Title()) < 0) 329 index++; 330 } 331 332 PackageView* view = new PackageView(); 333 view->SetPackage(package); 334 335 fPackageListLayout->AddView(index, view); 336 } 337 338 339 void 340 FeaturedPackagesView::RemovePackage(const PackageInfoRef& package) 341 { 342 // Find the package 343 for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) { 344 PackageView* view = dynamic_cast<PackageView*>(item->View()); 345 if (view == NULL) 346 break; 347 348 BString name = view->PackageName(); 349 if (name == package->Name()) { 350 view->RemoveSelf(); 351 delete view; 352 break; 353 } 354 } 355 } 356 357 358 void 359 FeaturedPackagesView::Clear() 360 { 361 for (int32 i = fPackageListLayout->CountItems() - 1; 362 BLayoutItem* item = fPackageListLayout->ItemAt(i); i--) { 363 BView* view = dynamic_cast<PackageView*>(item->View()); 364 if (view != NULL) { 365 view->RemoveSelf(); 366 delete view; 367 } 368 } 369 } 370 371 372 void 373 FeaturedPackagesView::SelectPackage(const PackageInfoRef& package) 374 { 375 BString selectedName; 376 if (package.Get() != NULL) 377 selectedName = package->Name(); 378 379 for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) { 380 PackageView* view = dynamic_cast<PackageView*>(item->View()); 381 if (view == NULL) 382 break; 383 384 BString name = view->PackageName(); 385 view->SetSelected(name == selectedName); 386 } 387 } 388 389 390 void 391 FeaturedPackagesView::CleanupIcons() 392 { 393 sInstalledIcon.Unset(); 394 } 395