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