1 /* 2 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>. 3 * Copyright 2018-2024, Andrew Lindesay <apl@lindesay.co.nz>. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 */ 6 #include "PackageInfoView.h" 7 8 #include <algorithm> 9 10 #include <Alert.h> 11 #include <Autolock.h> 12 #include <Bitmap.h> 13 #include <Button.h> 14 #include <CardLayout.h> 15 #include <Catalog.h> 16 #include <ColumnListView.h> 17 #include <ControlLook.h> 18 #include <Font.h> 19 #include <GridView.h> 20 #include <LayoutBuilder.h> 21 #include <LayoutUtils.h> 22 #include <LocaleRoster.h> 23 #include <Message.h> 24 #include <OutlineListView.h> 25 #include <ScrollView.h> 26 #include <SpaceLayoutItem.h> 27 #include <StatusBar.h> 28 #include <StringView.h> 29 #include <TabView.h> 30 #include <Url.h> 31 32 #include <package/hpkg/PackageReader.h> 33 #include <package/hpkg/NoErrorOutput.h> 34 #include <package/hpkg/PackageContentHandler.h> 35 #include <package/hpkg/PackageEntry.h> 36 37 #include "BitmapView.h" 38 #include "GeneralContentScrollView.h" 39 #include "LinkView.h" 40 #include "LinkedBitmapView.h" 41 #include "LocaleUtils.h" 42 #include "Logger.h" 43 #include "MarkupTextView.h" 44 #include "MessagePackageListener.h" 45 #include "PackageContentsView.h" 46 #include "PackageInfo.h" 47 #include "PackageManager.h" 48 #include "ProcessCoordinatorFactory.h" 49 #include "RatingView.h" 50 #include "ScrollableGroupView.h" 51 #include "SharedIcons.h" 52 #include "TextView.h" 53 54 55 #undef B_TRANSLATION_CONTEXT 56 #define B_TRANSLATION_CONTEXT "PackageInfoView" 57 58 59 enum { 60 TAB_ABOUT = 0, 61 TAB_RATINGS = 1, 62 TAB_CHANGELOG = 2, 63 TAB_CONTENTS = 3 64 }; 65 66 67 static const float kContentTint = (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0f; 68 static const uint32 kScreenshotSize = 320; 69 70 71 class RatingsScrollView : public GeneralContentScrollView { 72 public: 73 RatingsScrollView(const char* name, BView* target) 74 : 75 GeneralContentScrollView(name, target) 76 { 77 } 78 79 virtual void DoLayout() 80 { 81 GeneralContentScrollView::DoLayout(); 82 83 BScrollBar* scrollBar = ScrollBar(B_VERTICAL); 84 BView* target = Target(); 85 if (target != NULL && scrollBar != NULL) { 86 // Set the scroll steps 87 BView* item = target->ChildAt(0); 88 if (item != NULL) { 89 scrollBar->SetSteps(item->MinSize().height + 1, 90 item->MinSize().height + 1); 91 } 92 } 93 } 94 }; 95 96 97 // #pragma mark - rating stats 98 99 100 class DiagramBarView : public BView { 101 public: 102 DiagramBarView() 103 : 104 BView("diagram bar view", B_WILL_DRAW), 105 fValue(0.0f) 106 { 107 SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint); 108 SetHighUIColor(B_CONTROL_MARK_COLOR); 109 } 110 111 virtual ~DiagramBarView() 112 { 113 } 114 115 virtual void AttachedToWindow() 116 { 117 } 118 119 virtual void Draw(BRect updateRect) 120 { 121 FillRect(updateRect, B_SOLID_LOW); 122 123 if (fValue <= 0.0f) 124 return; 125 126 BRect rect(Bounds()); 127 rect.right = ceilf(rect.left + fValue * rect.Width()); 128 129 FillRect(rect, B_SOLID_HIGH); 130 } 131 132 virtual BSize MinSize() 133 { 134 return BSize(64, 10); 135 } 136 137 virtual BSize PreferredSize() 138 { 139 return MinSize(); 140 } 141 142 virtual BSize MaxSize() 143 { 144 return BSize(64, 10); 145 } 146 147 void SetValue(float value) 148 { 149 if (fValue != value) { 150 fValue = value; 151 Invalidate(); 152 } 153 } 154 155 private: 156 float fValue; 157 }; 158 159 160 // #pragma mark - TitleView 161 162 163 enum { 164 MSG_MOUSE_ENTERED_RATING = 'menr', 165 MSG_MOUSE_EXITED_RATING = 'mexr', 166 }; 167 168 169 class TransitReportingButton : public BButton { 170 public: 171 TransitReportingButton(const char* name, const char* label, 172 BMessage* message) 173 : 174 BButton(name, label, message), 175 fTransitMessage(NULL) 176 { 177 } 178 179 virtual ~TransitReportingButton() 180 { 181 SetTransitMessage(NULL); 182 } 183 184 virtual void MouseMoved(BPoint point, uint32 transit, 185 const BMessage* dragMessage) 186 { 187 BButton::MouseMoved(point, transit, dragMessage); 188 189 if (fTransitMessage != NULL && transit == B_EXITED_VIEW) 190 Invoke(fTransitMessage); 191 } 192 193 void SetTransitMessage(BMessage* message) 194 { 195 if (fTransitMessage != message) { 196 delete fTransitMessage; 197 fTransitMessage = message; 198 } 199 } 200 201 private: 202 BMessage* fTransitMessage; 203 }; 204 205 206 class TransitReportingRatingView : public RatingView, public BInvoker { 207 public: 208 TransitReportingRatingView(BMessage* transitMessage) 209 : 210 RatingView("package rating view"), 211 fTransitMessage(transitMessage) 212 { 213 } 214 215 virtual ~TransitReportingRatingView() 216 { 217 delete fTransitMessage; 218 } 219 220 virtual void MouseMoved(BPoint point, uint32 transit, 221 const BMessage* dragMessage) 222 { 223 RatingView::MouseMoved(point, transit, dragMessage); 224 225 if (fTransitMessage != NULL && transit == B_ENTERED_VIEW) 226 Invoke(fTransitMessage); 227 } 228 229 private: 230 BMessage* fTransitMessage; 231 }; 232 233 234 class TitleView : public BGroupView { 235 public: 236 TitleView(PackageIconRepository& packageIconRepository) 237 : 238 BGroupView("title view", B_HORIZONTAL), 239 fPackageIconRepository(packageIconRepository) 240 { 241 fIconView = new BitmapView("package icon view"); 242 fTitleView = new BStringView("package title view", ""); 243 fPublisherView = new BStringView("package publisher view", ""); 244 245 // Title font 246 BFont font; 247 GetFont(&font); 248 font_family family; 249 font_style style; 250 font.SetSize(ceilf(font.Size() * 1.5f)); 251 font.GetFamilyAndStyle(&family, &style); 252 font.SetFamilyAndStyle(family, "Bold"); 253 fTitleView->SetFont(&font); 254 255 // Publisher font 256 GetFont(&font); 257 font.SetSize(std::max(9.0f, floorf(font.Size() * 0.92f))); 258 font.SetFamilyAndStyle(family, "Italic"); 259 fPublisherView->SetFont(&font); 260 fPublisherView->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT); 261 262 // slightly bigger font 263 GetFont(&font); 264 font.SetSize(ceilf(font.Size() * 1.2f)); 265 266 // Version info 267 fVersionInfo = new BStringView("package version info", ""); 268 fVersionInfo->SetFont(&font); 269 fVersionInfo->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT); 270 271 // Rating view 272 fRatingView = new TransitReportingRatingView( 273 new BMessage(MSG_MOUSE_ENTERED_RATING)); 274 275 fAvgRating = new BStringView("package average rating", ""); 276 fAvgRating->SetFont(&font); 277 fAvgRating->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT); 278 279 fVoteInfo = new BStringView("package vote info", ""); 280 // small font 281 GetFont(&font); 282 font.SetSize(std::max(9.0f, floorf(font.Size() * 0.85f))); 283 fVoteInfo->SetFont(&font); 284 fVoteInfo->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT); 285 286 // Rate button 287 fRateButton = new TransitReportingButton("rate", 288 B_TRANSLATE("Rate package" B_UTF8_ELLIPSIS), 289 new BMessage(MSG_RATE_PACKAGE)); 290 fRateButton->SetTransitMessage(new BMessage(MSG_MOUSE_EXITED_RATING)); 291 fRateButton->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 292 B_ALIGN_VERTICAL_CENTER)); 293 294 // Rating group 295 BView* ratingStack = new BView("rating stack", 0); 296 fRatingLayout = new BCardLayout(); 297 ratingStack->SetLayout(fRatingLayout); 298 ratingStack->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 299 ratingStack->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 300 301 BGroupView* ratingGroup = new BGroupView(B_HORIZONTAL, 302 B_USE_SMALL_SPACING); 303 BLayoutBuilder::Group<>(ratingGroup) 304 .Add(fRatingView) 305 .Add(fAvgRating) 306 .Add(fVoteInfo) 307 ; 308 309 ratingStack->AddChild(ratingGroup); 310 ratingStack->AddChild(fRateButton); 311 fRatingLayout->SetVisibleItem((int32)0); 312 313 BLayoutBuilder::Group<>(this) 314 .Add(fIconView) 315 .AddGroup(B_VERTICAL, 1.0f, 2.2f) 316 .Add(fTitleView) 317 .Add(fPublisherView) 318 .SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)) 319 .End() 320 .AddGlue(0.1f) 321 .Add(ratingStack, 0.8f) 322 .AddGlue(0.2f) 323 .AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING, 2.0f) 324 .Add(fVersionInfo) 325 .AddGlue() 326 .SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)) 327 .End() 328 ; 329 330 Clear(); 331 } 332 333 virtual ~TitleView() 334 { 335 } 336 337 virtual void AttachedToWindow() 338 { 339 fRateButton->SetTarget(this); 340 fRatingView->SetTarget(this); 341 } 342 343 virtual void MessageReceived(BMessage* message) 344 { 345 switch (message->what) { 346 case MSG_RATE_PACKAGE: 347 // Forward to window (The button has us as target so 348 // we receive the message below.) 349 Window()->PostMessage(MSG_RATE_PACKAGE); 350 break; 351 352 case MSG_MOUSE_ENTERED_RATING: 353 fRatingLayout->SetVisibleItem(1); 354 break; 355 356 case MSG_MOUSE_EXITED_RATING: 357 fRatingLayout->SetVisibleItem((int32)0); 358 break; 359 } 360 } 361 362 void SetPackage(const PackageInfoRef package) 363 { 364 BitmapHolderRef bitmapHolderRef; 365 BSize iconSize = BControlLook::ComposeIconSize(32.0); 366 status_t iconResult = fPackageIconRepository.GetIcon(package->Name(), iconSize.Width() + 1, 367 bitmapHolderRef); 368 369 if (iconResult == B_OK) 370 fIconView->SetBitmap(bitmapHolderRef); 371 else 372 fIconView->UnsetBitmap(); 373 374 fTitleView->SetText(package->Title()); 375 376 BString publisher = package->Publisher().Name(); 377 if (publisher.CountChars() > 45) { 378 fPublisherView->SetToolTip(publisher); 379 fPublisherView->SetText(publisher.TruncateChars(45) 380 .Append(B_UTF8_ELLIPSIS)); 381 } else { 382 fPublisherView->SetToolTip(""); 383 fPublisherView->SetText(publisher); 384 } 385 386 fVersionInfo->SetText(package->Version().ToString()); 387 388 RatingSummary ratingSummary = package->CalculateRatingSummary(); 389 390 fRatingView->SetRating(ratingSummary.averageRating); 391 392 if (ratingSummary.ratingCount > 0) { 393 BString avgRating; 394 avgRating.SetToFormat("%.1f", ratingSummary.averageRating); 395 fAvgRating->SetText(avgRating); 396 397 BString votes; 398 votes.SetToFormat("%d", ratingSummary.ratingCount); 399 400 BString voteInfo(B_TRANSLATE("(%Votes%)")); 401 voteInfo.ReplaceAll("%Votes%", votes); 402 403 fVoteInfo->SetText(voteInfo); 404 } else { 405 fAvgRating->SetText(""); 406 fVoteInfo->SetText(B_TRANSLATE("n/a")); 407 } 408 409 InvalidateLayout(); 410 Invalidate(); 411 } 412 413 void Clear() 414 { 415 fIconView->UnsetBitmap(); 416 fTitleView->SetText(""); 417 fPublisherView->SetText(""); 418 fVersionInfo->SetText(""); 419 fRatingView->SetRating(-1.0f); 420 fAvgRating->SetText(""); 421 fVoteInfo->SetText(""); 422 } 423 424 private: 425 PackageIconRepository& fPackageIconRepository; 426 427 BitmapView* fIconView; 428 429 BStringView* fTitleView; 430 BStringView* fPublisherView; 431 432 BStringView* fVersionInfo; 433 434 BCardLayout* fRatingLayout; 435 436 TransitReportingRatingView* fRatingView; 437 BStringView* fAvgRating; 438 BStringView* fVoteInfo; 439 440 TransitReportingButton* fRateButton; 441 }; 442 443 444 // #pragma mark - PackageActionView 445 446 447 class PackageActionView : public BView { 448 public: 449 PackageActionView(ProcessCoordinatorConsumer* processCoordinatorConsumer, 450 Model* model) 451 : 452 BView("about view", B_WILL_DRAW), 453 fModel(model), 454 fLayout(new BGroupLayout(B_HORIZONTAL)), 455 fProcessCoordinatorConsumer(processCoordinatorConsumer), 456 fStatusLabel(NULL), 457 fStatusBar(NULL) 458 { 459 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 460 SetLayout(fLayout); 461 fLayout->AddItem(BSpaceLayoutItem::CreateGlue()); 462 } 463 464 virtual ~PackageActionView() 465 { 466 Clear(); 467 } 468 469 virtual void MessageReceived(BMessage* message) 470 { 471 switch (message->what) { 472 case MSG_PKG_INSTALL: 473 case MSG_PKG_UNINSTALL: 474 case MSG_PKG_OPEN: 475 _RunPackageAction(message); 476 break; 477 default: 478 BView::MessageReceived(message); 479 break; 480 } 481 } 482 483 void SetPackage(const PackageInfoRef package) 484 { 485 if (package->State() == DOWNLOADING) { 486 AdoptDownloadProgress(package); 487 } else { 488 AdoptActions(package); 489 } 490 } 491 492 void AdoptActions(const PackageInfoRef package) 493 { 494 PackageManager manager( 495 BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_HOME); 496 497 // TODO: if the given package is either a system package 498 // or a system dependency, show a message indicating that status 499 // so the user knows why no actions are presented 500 std::vector<PackageActionRef> actions; 501 VectorCollector<PackageActionRef> actionsCollector(actions); 502 manager.CollectPackageActions(package, actionsCollector); 503 504 if (_IsClearNeededToAdoptActions(actions)) { 505 Clear(); 506 _CreateAllNewButtonsForAdoptActions(actions); 507 } else { 508 _UpdateExistingButtonsForAdoptActions(actions); 509 } 510 } 511 512 void AdoptDownloadProgress(const PackageInfoRef package) 513 { 514 if (fButtons.CountItems() > 0) 515 Clear(); 516 517 if (fStatusBar == NULL) { 518 fStatusLabel = new BStringView("progress label", 519 B_TRANSLATE("Downloading:")); 520 fLayout->AddView(fStatusLabel); 521 522 fStatusBar = new BStatusBar("progress"); 523 fStatusBar->SetMaxValue(100.0); 524 fStatusBar->SetExplicitMinSize( 525 BSize(StringWidth("XXX") * 5, B_SIZE_UNSET)); 526 527 fLayout->AddView(fStatusBar); 528 } 529 530 fStatusBar->SetTo(package->DownloadProgress() * 100.0); 531 } 532 533 void Clear() 534 { 535 for (int32 i = fButtons.CountItems() - 1; i >= 0; i--) { 536 BButton* button = (BButton*)fButtons.ItemAtFast(i); 537 button->RemoveSelf(); 538 delete button; 539 } 540 fButtons.MakeEmpty(); 541 542 if (fStatusBar != NULL) { 543 fStatusBar->RemoveSelf(); 544 delete fStatusBar; 545 fStatusBar = NULL; 546 } 547 if (fStatusLabel != NULL) { 548 fStatusLabel->RemoveSelf(); 549 delete fStatusLabel; 550 fStatusLabel = NULL; 551 } 552 } 553 554 private: 555 bool _IsClearNeededToAdoptActions(std::vector<PackageActionRef> actions) 556 { 557 if (fStatusBar != NULL) 558 return true; 559 if (fButtons.CountItems() != static_cast<int32>(actions.size())) 560 return true; 561 return false; 562 } 563 564 void _UpdateExistingButtonsForAdoptActions( 565 std::vector<PackageActionRef> actions) 566 { 567 int32 index = 0; 568 for (int32 i = actions.size() - 1; i >= 0; i--) { 569 const PackageActionRef& action = actions[i]; 570 BMessage* message = new BMessage(action->Message()); 571 BButton* button = (BButton*)fButtons.ItemAtFast(index++); 572 button->SetLabel(action->Title()); 573 button->SetMessage(message); 574 } 575 } 576 577 void _CreateAllNewButtonsForAdoptActions( 578 std::vector<PackageActionRef> actions) 579 { 580 for (int32 i = actions.size() - 1; i >= 0; i--) { 581 const PackageActionRef& action = actions[i]; 582 BMessage* message = new BMessage(action->Message()); 583 BButton* button = new BButton(action->Title(), message); 584 fLayout->AddView(button); 585 button->SetTarget(this); 586 587 fButtons.AddItem(button); 588 } 589 } 590 591 bool _MatchesPackageActionMessage(BButton *button, BMessage* message) 592 { 593 if (button == NULL) 594 return false; 595 BMessage* buttonMessage = button->Message(); 596 if (buttonMessage == NULL) 597 return false; 598 return buttonMessage == message; 599 } 600 601 /*! Since the action has been fired; it should not be possible 602 to run it again because this may make no sense. For this 603 reason, disable the corresponding button. 604 */ 605 606 void _DisableButtonForPackageActionMessage(BMessage* message) 607 { 608 for (int32 i = 0; i < fButtons.CountItems(); i++) { 609 BButton* button = static_cast<BButton*>(fButtons.ItemAt(i)); 610 if (_MatchesPackageActionMessage(button, message)) 611 button->SetEnabled(false); 612 } 613 } 614 615 void _RunPackageAction(BMessage* message) 616 { 617 ProcessCoordinator *processCoordinator = 618 ProcessCoordinatorFactory::CreatePackageActionCoordinator( 619 fModel, message); 620 fProcessCoordinatorConsumer->Consume(processCoordinator); 621 _DisableButtonForPackageActionMessage(message); 622 } 623 624 private: 625 Model* fModel; 626 BGroupLayout* fLayout; 627 ProcessCoordinatorConsumer* 628 fProcessCoordinatorConsumer; 629 BList fButtons; 630 631 BStringView* fStatusLabel; 632 BStatusBar* fStatusBar; 633 }; 634 635 636 // #pragma mark - AboutView 637 638 639 enum { 640 MSG_VISIT_PUBLISHER_WEBSITE = 'vpws', 641 }; 642 643 644 class AboutView : public BView { 645 public: 646 AboutView() 647 : 648 BView("about view", 0) 649 { 650 SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint); 651 652 fDescriptionView = new MarkupTextView("description view"); 653 fDescriptionView->SetViewUIColor(ViewUIColor(), kContentTint); 654 fDescriptionView->SetInsets(be_plain_font->Size()); 655 656 BScrollView* scrollView = new GeneralContentScrollView( 657 "description scroll view", fDescriptionView); 658 659 BFont smallFont; 660 GetFont(&smallFont); 661 smallFont.SetSize(std::max(9.0f, ceilf(smallFont.Size() * 0.85f))); 662 663 fScreenshotView = new LinkedBitmapView("screenshot view", 664 new BMessage(MSG_SHOW_SCREENSHOT)); 665 fScreenshotView->SetExplicitMinSize(BSize(64.0f, 64.0f)); 666 fScreenshotView->SetExplicitMaxSize( 667 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 668 fScreenshotView->SetExplicitAlignment( 669 BAlignment(B_ALIGN_CENTER, B_ALIGN_TOP)); 670 671 fWebsiteIconView = new BitmapView("website icon view"); 672 fWebsiteLinkView = new LinkView("website link view", "", 673 new BMessage(MSG_VISIT_PUBLISHER_WEBSITE)); 674 fWebsiteLinkView->SetFont(&smallFont); 675 676 BGroupView* leftGroup = new BGroupView(B_VERTICAL, 677 B_USE_DEFAULT_SPACING); 678 679 fScreenshotView->SetViewUIColor(ViewUIColor(), kContentTint); 680 fWebsiteLinkView->SetViewUIColor(ViewUIColor(), kContentTint); 681 682 BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0.0f) 683 .AddGroup(leftGroup, 1.0f) 684 .Add(fScreenshotView) 685 .AddGroup(B_HORIZONTAL) 686 .AddGrid(B_USE_HALF_ITEM_SPACING, B_USE_HALF_ITEM_SPACING) 687 .Add(fWebsiteIconView, 0, 1) 688 .Add(fWebsiteLinkView, 1, 1) 689 .End() 690 .End() 691 .SetInsets(B_USE_DEFAULT_SPACING) 692 .SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)) 693 .End() 694 .Add(scrollView, 2.0f) 695 696 .SetExplicitMaxSize(BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED)) 697 .SetInsets(0.0f, -1.0f, -1.0f, -1.0f) 698 ; 699 } 700 701 virtual ~AboutView() 702 { 703 Clear(); 704 } 705 706 virtual void AttachedToWindow() 707 { 708 fScreenshotView->SetTarget(this); 709 fWebsiteLinkView->SetTarget(this); 710 } 711 712 virtual void AllAttached() 713 { 714 SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint); 715 716 for (int32 index = 0; index < CountChildren(); ++index) 717 ChildAt(index)->AdoptParentColors(); 718 } 719 720 virtual void MessageReceived(BMessage* message) 721 { 722 switch (message->what) { 723 case MSG_SHOW_SCREENSHOT: 724 { 725 // Forward to window for now 726 Window()->PostMessage(message, Window()); 727 break; 728 } 729 730 case MSG_VISIT_PUBLISHER_WEBSITE: 731 { 732 BUrl url(fWebsiteLinkView->Text()); 733 url.OpenWithPreferredApplication(); 734 break; 735 } 736 737 default: 738 BView::MessageReceived(message); 739 break; 740 } 741 } 742 743 void SetScreenshotThumbnail(BitmapHolderRef bitmapHolderRef) 744 { 745 if (bitmapHolderRef.IsSet()) { 746 fScreenshotView->SetBitmap(bitmapHolderRef); 747 fScreenshotView->SetEnabled(true); 748 } else { 749 fScreenshotView->UnsetBitmap(); 750 fScreenshotView->SetEnabled(false); 751 } 752 } 753 754 void SetPackage(const PackageInfoRef package) 755 { 756 fDescriptionView->SetText(package->ShortDescription(), package->FullDescription()); 757 fWebsiteIconView->SetBitmap(SharedIcons::IconHTMLPackage16Scaled()); 758 _SetContactInfo(fWebsiteLinkView, package->Publisher().Website()); 759 } 760 761 void Clear() 762 { 763 fDescriptionView->SetText(""); 764 fWebsiteIconView->UnsetBitmap(); 765 fWebsiteLinkView->SetText(""); 766 fScreenshotView->UnsetBitmap(); 767 fScreenshotView->SetEnabled(false); 768 } 769 770 private: 771 void _SetContactInfo(LinkView* view, const BString& string) 772 { 773 if (string.Length() > 0) { 774 view->SetText(string); 775 view->SetEnabled(true); 776 } else { 777 view->SetText(B_TRANSLATE("<no info>")); 778 view->SetEnabled(false); 779 } 780 } 781 782 private: 783 MarkupTextView* fDescriptionView; 784 785 LinkedBitmapView* fScreenshotView; 786 787 BitmapView* fWebsiteIconView; 788 LinkView* fWebsiteLinkView; 789 }; 790 791 792 // #pragma mark - UserRatingsView 793 794 795 class RatingItemView : public BGroupView { 796 public: 797 RatingItemView(const UserRatingRef rating) 798 : 799 BGroupView(B_HORIZONTAL, 0.0f) 800 { 801 SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint); 802 803 BGroupLayout* verticalGroup = new BGroupLayout(B_VERTICAL, 0.0f); 804 GroupLayout()->AddItem(verticalGroup); 805 806 { 807 BStringView* userNicknameView = new BStringView("user-nickname", 808 rating->User().NickName()); 809 userNicknameView->SetFont(be_bold_font); 810 verticalGroup->AddView(userNicknameView); 811 } 812 813 BGroupLayout* ratingGroup = 814 new BGroupLayout(B_HORIZONTAL, B_USE_DEFAULT_SPACING); 815 verticalGroup->AddItem(ratingGroup); 816 817 if (rating->Rating() >= 0) { 818 RatingView* ratingView = new RatingView("package rating view"); 819 ratingView->SetRating(rating->Rating()); 820 ratingGroup->AddView(ratingView); 821 } 822 823 { 824 BString createTimestampPresentation = 825 LocaleUtils::TimestampToDateTimeString( 826 rating->CreateTimestamp()); 827 828 BString ratingContextDescription( 829 B_TRANSLATE("%hd.timestamp% (version %hd.version%)")); 830 ratingContextDescription.ReplaceAll("%hd.timestamp%", 831 createTimestampPresentation); 832 ratingContextDescription.ReplaceAll("%hd.version%", 833 rating->PackageVersion()); 834 835 BStringView* ratingContextView = new BStringView("rating-context", 836 ratingContextDescription); 837 BFont versionFont(be_plain_font); 838 ratingContextView->SetFont(&versionFont); 839 ratingGroup->AddView(ratingContextView); 840 } 841 842 ratingGroup->AddItem(BSpaceLayoutItem::CreateGlue()); 843 844 if (rating->Comment() > 0) { 845 TextView* textView = new TextView("rating-text"); 846 ParagraphStyle paragraphStyle(textView->ParagraphStyle()); 847 paragraphStyle.SetJustify(true); 848 textView->SetParagraphStyle(paragraphStyle); 849 textView->SetText(rating->Comment()); 850 verticalGroup->AddItem(BSpaceLayoutItem::CreateVerticalStrut(8.0f)); 851 verticalGroup->AddView(textView); 852 verticalGroup->AddItem(BSpaceLayoutItem::CreateVerticalStrut(8.0f)); 853 } 854 855 verticalGroup->SetInsets(B_USE_DEFAULT_SPACING); 856 857 SetFlags(Flags() | B_WILL_DRAW); 858 } 859 860 void AllAttached() 861 { 862 for (int32 index = 0; index < CountChildren(); ++index) 863 ChildAt(index)->AdoptParentColors(); 864 } 865 866 void Draw(BRect rect) 867 { 868 rgb_color color = mix_color(ViewColor(), ui_color(B_PANEL_TEXT_COLOR), 64); 869 SetHighColor(color); 870 StrokeLine(Bounds().LeftBottom(), Bounds().RightBottom()); 871 } 872 873 }; 874 875 876 class RatingSummaryView : public BGridView { 877 public: 878 RatingSummaryView() 879 : 880 BGridView("rating summary view", B_USE_HALF_ITEM_SPACING, 0.0f) 881 { 882 float tint = kContentTint - 0.1; 883 SetViewUIColor(B_PANEL_BACKGROUND_COLOR, tint); 884 885 BLayoutBuilder::Grid<> layoutBuilder(this); 886 887 BFont smallFont; 888 GetFont(&smallFont); 889 smallFont.SetSize(std::max(9.0f, floorf(smallFont.Size() * 0.85f))); 890 891 for (int32 i = 0; i < 5; i++) { 892 BString label; 893 label.SetToFormat("%" B_PRId32, 5 - i); 894 fLabelViews[i] = new BStringView("", label); 895 fLabelViews[i]->SetFont(&smallFont); 896 fLabelViews[i]->SetViewUIColor(ViewUIColor(), tint); 897 layoutBuilder.Add(fLabelViews[i], 0, i); 898 899 fDiagramBarViews[i] = new DiagramBarView(); 900 layoutBuilder.Add(fDiagramBarViews[i], 1, i); 901 902 fCountViews[i] = new BStringView("", ""); 903 fCountViews[i]->SetFont(&smallFont); 904 fCountViews[i]->SetViewUIColor(ViewUIColor(), tint); 905 fCountViews[i]->SetAlignment(B_ALIGN_RIGHT); 906 layoutBuilder.Add(fCountViews[i], 2, i); 907 } 908 909 layoutBuilder.SetInsets(5); 910 } 911 912 void SetToSummary(const RatingSummary& summary) { 913 for (int32 i = 0; i < 5; i++) { 914 int32 count = summary.ratingCountByStar[4 - i]; 915 916 BString label; 917 label.SetToFormat("%" B_PRId32, count); 918 fCountViews[i]->SetText(label); 919 920 if (summary.ratingCount > 0) { 921 fDiagramBarViews[i]->SetValue( 922 (float)count / summary.ratingCount); 923 } else 924 fDiagramBarViews[i]->SetValue(0.0f); 925 } 926 } 927 928 void Clear() { 929 for (int32 i = 0; i < 5; i++) { 930 fCountViews[i]->SetText(""); 931 fDiagramBarViews[i]->SetValue(0.0f); 932 } 933 } 934 935 private: 936 BStringView* fLabelViews[5]; 937 DiagramBarView* fDiagramBarViews[5]; 938 BStringView* fCountViews[5]; 939 }; 940 941 942 class UserRatingsView : public BGroupView { 943 public: 944 UserRatingsView() 945 : 946 BGroupView("package ratings view", B_HORIZONTAL) 947 { 948 SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint); 949 950 fRatingSummaryView = new RatingSummaryView(); 951 952 ScrollableGroupView* ratingsContainerView = new ScrollableGroupView(); 953 ratingsContainerView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR, 954 kContentTint); 955 fRatingContainerLayout = ratingsContainerView->GroupLayout(); 956 957 BScrollView* scrollView = new RatingsScrollView( 958 "ratings scroll view", ratingsContainerView); 959 scrollView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 960 B_SIZE_UNLIMITED)); 961 962 BLayoutBuilder::Group<>(this) 963 .AddGroup(B_VERTICAL) 964 .Add(fRatingSummaryView, 0.0f) 965 .AddGlue() 966 .SetInsets(0.0f, B_USE_DEFAULT_SPACING, 0.0f, 0.0f) 967 .End() 968 .AddStrut(64.0) 969 .Add(scrollView, 1.0f) 970 .SetInsets(B_USE_DEFAULT_SPACING, -1.0f, -1.0f, -1.0f) 971 ; 972 } 973 974 virtual ~UserRatingsView() 975 { 976 Clear(); 977 } 978 979 void SetPackage(const PackageInfoRef package) 980 { 981 ClearRatings(); 982 983 // TODO: Re-use rating summary already used for TitleView... 984 fRatingSummaryView->SetToSummary(package->CalculateRatingSummary()); 985 986 int count = package->CountUserRatings(); 987 if (count == 0) { 988 BStringView* noRatingsView = new BStringView("no ratings", 989 B_TRANSLATE("No user ratings available.")); 990 noRatingsView->SetViewUIColor(ViewUIColor(), kContentTint); 991 noRatingsView->SetAlignment(B_ALIGN_CENTER); 992 noRatingsView->SetHighColor(disable_color(ui_color(B_PANEL_TEXT_COLOR), 993 ViewColor())); 994 noRatingsView->SetExplicitMaxSize( 995 BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)); 996 fRatingContainerLayout->AddView(0, noRatingsView); 997 } else { 998 for (int i = count - 1; i >= 0; i--) { 999 UserRatingRef rating = package->UserRatingAtIndex(i); 1000 // was previously filtering comments just for the current 1001 // user's language, but as there are not so many comments at 1002 // the moment, just show all of them for now. 1003 RatingItemView* view = new RatingItemView(rating); 1004 fRatingContainerLayout->AddView(0, view); 1005 } 1006 } 1007 1008 InvalidateLayout(); 1009 } 1010 1011 void Clear() 1012 { 1013 fRatingSummaryView->Clear(); 1014 ClearRatings(); 1015 } 1016 1017 void ClearRatings() 1018 { 1019 for (int32 i = fRatingContainerLayout->CountItems() - 1; 1020 BLayoutItem* item = fRatingContainerLayout->ItemAt(i); i--) { 1021 BView* view = dynamic_cast<RatingItemView*>(item->View()); 1022 if (view == NULL) 1023 view = dynamic_cast<BStringView*>(item->View()); 1024 if (view != NULL) { 1025 view->RemoveSelf(); 1026 delete view; 1027 } 1028 } 1029 } 1030 1031 private: 1032 BGroupLayout* fRatingContainerLayout; 1033 RatingSummaryView* fRatingSummaryView; 1034 }; 1035 1036 1037 // #pragma mark - ContentsView 1038 1039 1040 class ContentsView : public BGroupView { 1041 public: 1042 ContentsView() 1043 : 1044 BGroupView("package contents view", B_HORIZONTAL) 1045 { 1046 SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint); 1047 1048 fPackageContents = new PackageContentsView("contents_list"); 1049 AddChild(fPackageContents); 1050 1051 } 1052 1053 virtual ~ContentsView() 1054 { 1055 } 1056 1057 virtual void Draw(BRect updateRect) 1058 { 1059 } 1060 1061 void SetPackage(const PackageInfoRef package) 1062 { 1063 fPackageContents->SetPackage(package); 1064 } 1065 1066 void Clear() 1067 { 1068 fPackageContents->Clear(); 1069 } 1070 1071 private: 1072 PackageContentsView* fPackageContents; 1073 }; 1074 1075 1076 // #pragma mark - ChangelogView 1077 1078 1079 class ChangelogView : public BGroupView { 1080 public: 1081 ChangelogView() 1082 : 1083 BGroupView("package changelog view", B_HORIZONTAL) 1084 { 1085 SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint); 1086 1087 fTextView = new MarkupTextView("changelog view"); 1088 fTextView->SetLowUIColor(ViewUIColor()); 1089 fTextView->SetInsets(be_plain_font->Size()); 1090 1091 BScrollView* scrollView = new GeneralContentScrollView( 1092 "changelog scroll view", fTextView); 1093 1094 BLayoutBuilder::Group<>(this) 1095 .Add(BSpaceLayoutItem::CreateHorizontalStrut(32.0f)) 1096 .Add(scrollView, 1.0f) 1097 .SetInsets(B_USE_DEFAULT_SPACING, -1.0f, -1.0f, -1.0f) 1098 ; 1099 } 1100 1101 virtual ~ChangelogView() 1102 { 1103 } 1104 1105 virtual void Draw(BRect updateRect) 1106 { 1107 } 1108 1109 void SetPackage(const PackageInfoRef package) 1110 { 1111 const BString& changelog = package->Changelog(); 1112 if (changelog.Length() > 0) 1113 fTextView->SetText(changelog); 1114 else 1115 fTextView->SetDisabledText(B_TRANSLATE("No changelog available.")); 1116 } 1117 1118 void Clear() 1119 { 1120 fTextView->SetText(""); 1121 } 1122 1123 private: 1124 MarkupTextView* fTextView; 1125 }; 1126 1127 1128 // #pragma mark - PagesView 1129 1130 1131 class PagesView : public BTabView { 1132 public: 1133 PagesView() 1134 : 1135 BTabView("pages view", B_WIDTH_FROM_WIDEST) 1136 { 1137 SetBorder(B_NO_BORDER); 1138 1139 fAboutView = new AboutView(); 1140 fUserRatingsView = new UserRatingsView(); 1141 fChangelogView = new ChangelogView(); 1142 fContentsView = new ContentsView(); 1143 1144 AddTab(fAboutView); 1145 AddTab(fUserRatingsView); 1146 AddTab(fChangelogView); 1147 AddTab(fContentsView); 1148 1149 TabAt(TAB_ABOUT)->SetLabel(B_TRANSLATE("About")); 1150 TabAt(TAB_RATINGS)->SetLabel(B_TRANSLATE("Ratings")); 1151 TabAt(TAB_CHANGELOG)->SetLabel(B_TRANSLATE("Changelog")); 1152 TabAt(TAB_CONTENTS)->SetLabel(B_TRANSLATE("Contents")); 1153 1154 Select(TAB_ABOUT); 1155 } 1156 1157 virtual ~PagesView() 1158 { 1159 Clear(); 1160 } 1161 1162 void SetScreenshotThumbnail(BitmapHolderRef bitmap) 1163 { 1164 fAboutView->SetScreenshotThumbnail(bitmap); 1165 } 1166 1167 void SetPackage(const PackageInfoRef package, bool switchToDefaultTab) 1168 { 1169 if (switchToDefaultTab) 1170 Select(TAB_ABOUT); 1171 1172 TabAt(TAB_CHANGELOG)->SetEnabled( 1173 package.IsSet() && package->HasChangelog()); 1174 TabAt(TAB_CONTENTS)->SetEnabled( 1175 package.IsSet() 1176 && (package->State() == ACTIVATED || package->IsLocalFile())); 1177 Invalidate(TabFrame(TAB_CHANGELOG)); 1178 Invalidate(TabFrame(TAB_CONTENTS)); 1179 1180 fAboutView->SetPackage(package); 1181 fUserRatingsView->SetPackage(package); 1182 fChangelogView->SetPackage(package); 1183 fContentsView->SetPackage(package); 1184 } 1185 1186 void Clear() 1187 { 1188 fAboutView->Clear(); 1189 fUserRatingsView->Clear(); 1190 fChangelogView->Clear(); 1191 fContentsView->Clear(); 1192 } 1193 1194 private: 1195 AboutView* fAboutView; 1196 UserRatingsView* fUserRatingsView; 1197 ChangelogView* fChangelogView; 1198 ContentsView* fContentsView; 1199 }; 1200 1201 1202 // #pragma mark - PackageInfoView 1203 1204 1205 PackageInfoView::PackageInfoView(Model* model, 1206 ProcessCoordinatorConsumer* processCoordinatorConsumer) 1207 : 1208 BView("package info view", 0), 1209 fModel(model), 1210 fPackageListener(new(std::nothrow) OnePackageMessagePackageListener(this)), 1211 fProcessCoordinatorConsumer(processCoordinatorConsumer) 1212 { 1213 fCardLayout = new BCardLayout(); 1214 SetLayout(fCardLayout); 1215 1216 BGroupView* noPackageCard = new BGroupView("no package card", B_VERTICAL); 1217 AddChild(noPackageCard); 1218 1219 BStringView* noPackageView = new BStringView("no package view", 1220 B_TRANSLATE("Click a package to view information")); 1221 noPackageView->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT); 1222 noPackageView->SetExplicitAlignment(BAlignment( 1223 B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); 1224 1225 BLayoutBuilder::Group<>(noPackageCard) 1226 .Add(noPackageView) 1227 .SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)) 1228 ; 1229 1230 BGroupView* packageCard = new BGroupView("package card", B_VERTICAL); 1231 AddChild(packageCard); 1232 1233 fCardLayout->SetVisibleItem((int32)0); 1234 1235 fTitleView = new TitleView(fModel->GetPackageIconRepository()); 1236 fPackageActionView = new PackageActionView(processCoordinatorConsumer, 1237 model); 1238 fPackageActionView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 1239 B_SIZE_UNSET)); 1240 fPagesView = new PagesView(); 1241 1242 BLayoutBuilder::Group<>(packageCard) 1243 .AddGroup(B_HORIZONTAL, 0.0f) 1244 .Add(fTitleView, 6.0f) 1245 .Add(fPackageActionView, 1.0f) 1246 .SetInsets( 1247 B_USE_DEFAULT_SPACING, 0.0f, 1248 B_USE_DEFAULT_SPACING, 0.0f) 1249 .End() 1250 .Add(fPagesView) 1251 ; 1252 1253 Clear(); 1254 } 1255 1256 1257 PackageInfoView::~PackageInfoView() 1258 { 1259 fPackageListener->SetPackage(PackageInfoRef(NULL)); 1260 delete fPackageListener; 1261 } 1262 1263 1264 void 1265 PackageInfoView::AttachedToWindow() 1266 { 1267 } 1268 1269 1270 void 1271 PackageInfoView::MessageReceived(BMessage* message) 1272 { 1273 switch (message->what) { 1274 case MSG_UPDATE_PACKAGE: 1275 { 1276 if (!fPackageListener->Package().IsSet()) 1277 break; 1278 1279 BString name; 1280 uint32 changes; 1281 if (message->FindString("name", &name) != B_OK 1282 || message->FindUInt32("changes", &changes) != B_OK) { 1283 break; 1284 } 1285 1286 const PackageInfoRef& package = fPackageListener->Package(); 1287 if (package->Name() != name) 1288 break; 1289 1290 BAutolock _(fModel->Lock()); 1291 1292 if ((changes & PKG_CHANGED_SUMMARY) != 0 1293 || (changes & PKG_CHANGED_DESCRIPTION) != 0 1294 || (changes & PKG_CHANGED_SCREENSHOTS) != 0 1295 || (changes & PKG_CHANGED_TITLE) != 0 1296 || (changes & PKG_CHANGED_RATINGS) != 0 1297 || (changes & PKG_CHANGED_STATE) != 0 1298 || (changes & PKG_CHANGED_CHANGELOG) != 0) { 1299 fPagesView->SetPackage(package, false); 1300 } 1301 1302 if ((changes & PKG_CHANGED_TITLE) != 0 1303 || (changes & PKG_CHANGED_RATINGS) != 0) { 1304 fTitleView->SetPackage(package); 1305 } 1306 1307 if ((changes & PKG_CHANGED_STATE) != 0) 1308 fPackageActionView->SetPackage(package); 1309 1310 break; 1311 } 1312 default: 1313 BView::MessageReceived(message); 1314 break; 1315 } 1316 } 1317 1318 1319 void 1320 PackageInfoView::SetPackage(const PackageInfoRef& packageRef) 1321 { 1322 BAutolock _(fModel->Lock()); 1323 1324 if (!packageRef.IsSet()) { 1325 Clear(); 1326 return; 1327 } 1328 1329 bool switchToDefaultTab = true; 1330 if (fPackage == packageRef) { 1331 // When asked to display the already showing package ref, 1332 // don't switch to the default tab. 1333 switchToDefaultTab = false; 1334 } else if (fPackage.IsSet() && packageRef.IsSet() 1335 && fPackage->Name() == packageRef->Name()) { 1336 // When asked to display a different PackageInfo instance, 1337 // but it has the same package title as the already showing 1338 // instance, this probably means there was a repository 1339 // refresh and we are in fact still requested to show the 1340 // same package as before the refresh. 1341 switchToDefaultTab = false; 1342 } 1343 1344 fTitleView->SetPackage(packageRef); 1345 fPackageActionView->SetPackage(packageRef); 1346 fPagesView->SetPackage(packageRef, switchToDefaultTab); 1347 1348 _SetPackageScreenshotThumb(packageRef); 1349 1350 fCardLayout->SetVisibleItem(1); 1351 1352 fPackageListener->SetPackage(packageRef); 1353 1354 // Set the fPackage reference last, so we keep a reference to the 1355 // previous package before switching all the views to the new package. 1356 // Otherwise the PackageInfo instance may go away because we had the 1357 // last reference. And some of the views, the PackageActionView for 1358 // example, keeps references to stuff from the previous package and 1359 // access it while switching to the new package. 1360 fPackage = packageRef; 1361 } 1362 1363 1364 /*! See if the screenshot is already cached; if it is then load it 1365 immediately. If it is not then trigger a process to start it in 1366 the background. A message will come through later once it is 1367 cached and ready to load. 1368 */ 1369 1370 void 1371 PackageInfoView::_SetPackageScreenshotThumb(const PackageInfoRef& package) 1372 { 1373 ScreenshotCoordinate desiredCoordinate = _ScreenshotThumbCoordinate(package); 1374 bool hasCachedBitmap = false; 1375 1376 if (desiredCoordinate.IsValid()) { 1377 bool present = false; 1378 if (fModel->GetPackageScreenshotRepository()->HasCachedScreenshot( 1379 desiredCoordinate, &present) != B_OK) { 1380 HDERROR("unable to ascertain if screenshot is present for pkg [%s]", package->Name().String()); 1381 } else { 1382 if (present) { 1383 HDDEBUG("screenshot is already cached for [%s] -- will load it", package->Name().String()); 1384 _HandleScreenshotCached(package, desiredCoordinate); 1385 hasCachedBitmap = true; 1386 } else { 1387 HDDEBUG("screenshot is not cached [%s] -- will cache it", package->Name().String()); 1388 ProcessCoordinator *processCoordinator = 1389 ProcessCoordinatorFactory::CacheScreenshotCoordinator( 1390 fModel, desiredCoordinate); 1391 fProcessCoordinatorConsumer->Consume(processCoordinator); 1392 } 1393 } 1394 } else 1395 HDDEBUG("no screenshot for pkg [%s]", package->Name().String()); 1396 1397 if (!hasCachedBitmap) 1398 fPagesView->SetScreenshotThumbnail(BitmapHolderRef()); 1399 } 1400 1401 1402 /*static*/ const ScreenshotCoordinate 1403 PackageInfoView::_ScreenshotThumbCoordinate(const PackageInfoRef& package) 1404 { 1405 if (!package.IsSet()) 1406 return ScreenshotCoordinate(); 1407 if (package->CountScreenshotInfos() == 0) 1408 return ScreenshotCoordinate(); 1409 1410 uint32 screenshotSizeScaled 1411 = MAX(static_cast<uint32>(BControlLook::ComposeIconSize(kScreenshotSize).Width()), 1412 MAX_IMAGE_SIZE); 1413 1414 return ScreenshotCoordinate(package->ScreenshotInfoAtIndex(0)->Code(), screenshotSizeScaled + 1, 1415 screenshotSizeScaled + 1); 1416 } 1417 1418 1419 /*! This message will arrive when the data in the screenshot cache 1420 contains a new screenshot. This logic can check to see if the 1421 screenshot arriving is the one that is being waited for and 1422 would then load the screenshot from the cache and display it. 1423 */ 1424 1425 void 1426 PackageInfoView::HandleScreenshotCached(const ScreenshotCoordinate& coordinate) 1427 { 1428 HDINFO("handle screenshot cached [%s] %" B_PRIu16 " x %" B_PRIu16, coordinate.Code().String(), 1429 coordinate.Width(), coordinate.Height()); 1430 _HandleScreenshotCached(fPackage, coordinate); 1431 } 1432 1433 1434 void 1435 PackageInfoView::_HandleScreenshotCached(const PackageInfoRef& package, 1436 const ScreenshotCoordinate& coordinate) 1437 { 1438 ScreenshotCoordinate desiredCoordinate = _ScreenshotThumbCoordinate(package); 1439 1440 if (desiredCoordinate.IsValid() && desiredCoordinate == coordinate) { 1441 HDDEBUG("screenshot [%s] has been cached and matched; will load", 1442 coordinate.Code().String()); 1443 BitmapHolderRef bitmapHolderRef; 1444 if (fModel->GetPackageScreenshotRepository()->CacheAndLoadScreenshot(coordinate, 1445 bitmapHolderRef) != B_OK) { 1446 HDERROR("unable to load the screenshot [%s]", coordinate.Code().String()); 1447 } else { 1448 fPagesView->SetScreenshotThumbnail(bitmapHolderRef); 1449 } 1450 } 1451 } 1452 1453 1454 void 1455 PackageInfoView::Clear() 1456 { 1457 BAutolock _(fModel->Lock()); 1458 1459 fTitleView->Clear(); 1460 fPackageActionView->Clear(); 1461 fPagesView->Clear(); 1462 1463 fCardLayout->SetVisibleItem((int32)0); 1464 1465 fPackageListener->SetPackage(PackageInfoRef(NULL)); 1466 1467 fPackage.Unset(); 1468 } 1469