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