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