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