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