xref: /haiku/src/apps/haikudepot/ui/PackageInfoView.cpp (revision dc0bc42494264bb8d17b6b081647377b5601570a)
1 /*
2  * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
3  * Copyright 2018-2019, Andrew Lindesay <apl@lindesay.co.nz>.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 
7 #include "PackageInfoView.h"
8 
9 #include <algorithm>
10 #include <stdio.h>
11 
12 #include <Alert.h>
13 #include <Autolock.h>
14 #include <Bitmap.h>
15 #include <Button.h>
16 #include <CardLayout.h>
17 #include <Catalog.h>
18 #include <ColumnListView.h>
19 #include <Font.h>
20 #include <GridView.h>
21 #include <LayoutBuilder.h>
22 #include <LayoutUtils.h>
23 #include <LocaleRoster.h>
24 #include <Message.h>
25 #include <OutlineListView.h>
26 #include <ScrollView.h>
27 #include <SpaceLayoutItem.h>
28 #include <StatusBar.h>
29 #include <StringView.h>
30 #include <TabView.h>
31 #include <Url.h>
32 
33 #include <package/hpkg/PackageReader.h>
34 #include <package/hpkg/NoErrorOutput.h>
35 #include <package/hpkg/PackageContentHandler.h>
36 #include <package/hpkg/PackageEntry.h>
37 
38 #include "BitmapView.h"
39 #include "LinkView.h"
40 #include "LinkedBitmapView.h"
41 #include "LocaleUtils.h"
42 #include "MarkupTextView.h"
43 #include "MessagePackageListener.h"
44 #include "PackageActionHandler.h"
45 #include "PackageContentsView.h"
46 #include "PackageInfo.h"
47 #include "PackageManager.h"
48 #include "RatingView.h"
49 #include "ScrollableGroupView.h"
50 #include "TextView.h"
51 
52 
53 #undef B_TRANSLATION_CONTEXT
54 #define B_TRANSLATION_CONTEXT "PackageInfoView"
55 
56 
57 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->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT);
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->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT);
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->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT);
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->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT);
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 		if (publisher.CountChars() > 45) {
397 			fPublisherView->SetToolTip(publisher);
398 			fPublisherView->SetText(publisher.TruncateChars(45)
399 				.Append(B_UTF8_ELLIPSIS));
400 		} else
401 			fPublisherView->SetText(publisher);
402 
403 		fVersionInfo->SetText(package.Version().ToString());
404 
405 		RatingSummary ratingSummary = package.CalculateRatingSummary();
406 
407 		fRatingView->SetRating(ratingSummary.averageRating);
408 
409 		if (ratingSummary.ratingCount > 0) {
410 			BString avgRating;
411 			avgRating.SetToFormat("%.1f", ratingSummary.averageRating);
412 			fAvgRating->SetText(avgRating);
413 
414 			BString votes;
415 			votes.SetToFormat("%d", ratingSummary.ratingCount);
416 
417 			BString voteInfo(B_TRANSLATE("(%Votes%)"));
418 			voteInfo.ReplaceAll("%Votes%", votes);
419 
420 			fVoteInfo->SetText(voteInfo);
421 		} else {
422 			fAvgRating->SetText("");
423 			fVoteInfo->SetText(B_TRANSLATE("n/a"));
424 		}
425 
426 		InvalidateLayout();
427 		Invalidate();
428 	}
429 
430 	void Clear()
431 	{
432 		fIconView->UnsetBitmap();
433 		fTitleView->SetText("");
434 		fPublisherView->SetText("");
435 		fVersionInfo->SetText("");
436 		fRatingView->SetRating(-1.0f);
437 		fAvgRating->SetText("");
438 		fVoteInfo->SetText("");
439 	}
440 
441 private:
442 	BitmapView*						fIconView;
443 
444 	BStringView*					fTitleView;
445 	BStringView*					fPublisherView;
446 
447 	BStringView*					fVersionInfo;
448 
449 	BCardLayout*					fRatingLayout;
450 
451 	TransitReportingRatingView*		fRatingView;
452 	BStringView*					fAvgRating;
453 	BStringView*					fVoteInfo;
454 
455 	TransitReportingButton*			fRateButton;
456 };
457 
458 
459 // #pragma mark - PackageActionView
460 
461 
462 class PackageActionView : public BView {
463 public:
464 	PackageActionView(PackageActionHandler* handler)
465 		:
466 		BView("about view", B_WILL_DRAW),
467 		fLayout(new BGroupLayout(B_HORIZONTAL)),
468 		fPackageActionHandler(handler),
469 		fStatusLabel(NULL),
470 		fStatusBar(NULL)
471 	{
472 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
473 
474 		SetLayout(fLayout);
475 		fLayout->AddItem(BSpaceLayoutItem::CreateGlue());
476 	}
477 
478 	virtual ~PackageActionView()
479 	{
480 		Clear();
481 	}
482 
483 	virtual void MessageReceived(BMessage* message)
484 	{
485 		switch (message->what) {
486 			case MSG_PACKAGE_ACTION:
487 				_RunPackageAction(message);
488 				break;
489 
490 			default:
491 				BView::MessageReceived(message);
492 				break;
493 		}
494 	}
495 
496 	void SetPackage(const PackageInfo& package)
497 	{
498 		if (package.State() == DOWNLOADING) {
499 			AdoptDownloadProgress(package);
500 		} else {
501 			AdoptActions(package);
502 		}
503 
504 	}
505 
506 	void AdoptActions(const PackageInfo& package)
507 	{
508 		PackageManager manager(
509 			BPackageKit::B_PACKAGE_INSTALLATION_LOCATION_HOME);
510 
511 		// TODO: if the given package is either a system package
512 		// or a system dependency, show a message indicating that status
513 		// so the user knows why no actions are presented
514 		PackageActionList actions = manager.GetPackageActions(
515 			const_cast<PackageInfo*>(&package),
516 			fPackageActionHandler->GetModel());
517 
518 		bool clearNeeded = fStatusBar != NULL;
519 		if (!clearNeeded) {
520 			if (actions.CountItems() != fPackageActions.CountItems())
521 				clearNeeded = true;
522 			else {
523 				for (int32 i = 0; i < actions.CountItems(); i++) {
524 					if (actions.ItemAtFast(i)->Type()
525 							!= fPackageActions.ItemAtFast(i)->Type()) {
526 						clearNeeded = true;
527 						break;
528 					}
529 				}
530 			}
531 		}
532 
533 		fPackageActions = actions;
534 		if (!clearNeeded && fButtons.CountItems() == actions.CountItems()) {
535 			int32 index = 0;
536 			for (int32 i = fPackageActions.CountItems() - 1; i >= 0; i--) {
537 				const PackageActionRef& action = fPackageActions.ItemAtFast(i);
538 				BButton* button = (BButton*)fButtons.ItemAtFast(index++);
539 				button->SetLabel(action->Label());
540 			}
541 			return;
542 		}
543 
544 		Clear();
545 
546 		// Add Buttons in reverse action order
547 		for (int32 i = fPackageActions.CountItems() - 1; i >= 0; i--) {
548 			const PackageActionRef& action = fPackageActions.ItemAtFast(i);
549 
550 			BMessage* message = new BMessage(MSG_PACKAGE_ACTION);
551 			message->AddInt32("index", i);
552 
553 			BButton* button = new BButton(action->Label(), message);
554 			fLayout->AddView(button);
555 			button->SetTarget(this);
556 
557 			fButtons.AddItem(button);
558 		}
559 	}
560 
561 	void AdoptDownloadProgress(const PackageInfo& package)
562 	{
563 		if (fButtons.CountItems() > 0)
564 			Clear();
565 
566 		if (fStatusBar == NULL) {
567 			fStatusLabel = new BStringView("progress label",
568 				B_TRANSLATE("Downloading:"));
569 			fLayout->AddView(fStatusLabel);
570 
571 			fStatusBar = new BStatusBar("progress");
572 			fStatusBar->SetMaxValue(100.0);
573 			fStatusBar->SetExplicitMinSize(
574 				BSize(StringWidth("XXX") * 5, B_SIZE_UNSET));
575 
576 			fLayout->AddView(fStatusBar);
577 		}
578 
579 		fStatusBar->SetTo(package.DownloadProgress() * 100.0);
580 	}
581 
582 	void Clear()
583 	{
584 		for (int32 i = fButtons.CountItems() - 1; i >= 0; i--) {
585 			BButton* button = (BButton*)fButtons.ItemAtFast(i);
586 			button->RemoveSelf();
587 			delete button;
588 		}
589 		fButtons.MakeEmpty();
590 
591 		if (fStatusBar != NULL) {
592 			fStatusBar->RemoveSelf();
593 			delete fStatusBar;
594 			fStatusBar = NULL;
595 		}
596 		if (fStatusLabel != NULL) {
597 			fStatusLabel->RemoveSelf();
598 			delete fStatusLabel;
599 			fStatusLabel = NULL;
600 		}
601 	}
602 
603 private:
604 	void _RunPackageAction(BMessage* message)
605 	{
606 		int32 index;
607 		if (message->FindInt32("index", &index) != B_OK)
608 			return;
609 
610 		const PackageActionRef& action = fPackageActions.ItemAt(index);
611 		if (action.Get() == NULL)
612 			return;
613 
614 		PackageActionList actions;
615 		actions.Add(action);
616 		status_t result
617 			= fPackageActionHandler->SchedulePackageActions(actions);
618 
619 		if (result != B_OK) {
620 			fprintf(stderr, "Failed to schedule action: "
621 				"%s '%s': %s\n", action->Label(),
622 				action->Package()->Name().String(),
623 				strerror(result));
624 			BString message(B_TRANSLATE("The package action "
625 				"could not be scheduled: %Error%"));
626 			message.ReplaceAll("%Error%", strerror(result));
627 			BAlert* alert = new(std::nothrow) BAlert(
628 				B_TRANSLATE("Package action failed"),
629 				message, B_TRANSLATE("OK"), NULL, NULL,
630 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
631 			if (alert != NULL)
632 				alert->Go();
633 		} else {
634 			// Find the button for this action and disable it.
635 			// Actually search the matching button instead of just using
636 			// fButtons.ItemAt((fButtons.CountItems() - 1) - index) to
637 			// make this robust against for example changing the order of
638 			// buttons from right -> left to left -> right...
639 			for (int32 i = 0; i < fButtons.CountItems(); i++) {
640 				BButton* button = (BButton*)fButtons.ItemAt(index);
641 				if (button == NULL)
642 					continue;
643 				BMessage* buttonMessage = button->Message();
644 				if (buttonMessage == NULL)
645 					continue;
646 				int32 buttonIndex;
647 				if (buttonMessage->FindInt32("index", &buttonIndex) != B_OK)
648 					continue;
649 				if (buttonIndex == index) {
650 					button->SetEnabled(false);
651 					break;
652 				}
653 			}
654 		}
655 	}
656 
657 private:
658 	BGroupLayout*		fLayout;
659 	PackageActionList	fPackageActions;
660 	PackageActionHandler* fPackageActionHandler;
661 	BList				fButtons;
662 
663 	BStringView*		fStatusLabel;
664 	BStatusBar*			fStatusBar;
665 };
666 
667 
668 // #pragma mark - AboutView
669 
670 
671 enum {
672 	MSG_EMAIL_PUBLISHER				= 'emlp',
673 	MSG_VISIT_PUBLISHER_WEBSITE		= 'vpws',
674 };
675 
676 
677 class AboutView : public BView {
678 public:
679 	AboutView()
680 		:
681 		BView("about view", 0),
682 		fEmailIcon("text/x-email"),
683 		fWebsiteIcon("text/html")
684 	{
685 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint);
686 
687 		fDescriptionView = new MarkupTextView("description view");
688 		fDescriptionView->SetViewUIColor(ViewUIColor(), kContentTint);
689 		fDescriptionView->SetInsets(be_plain_font->Size());
690 
691 		BScrollView* scrollView = new CustomScrollView(
692 			"description scroll view", fDescriptionView);
693 
694 		BFont smallFont;
695 		GetFont(&smallFont);
696 		smallFont.SetSize(std::max(9.0f, ceilf(smallFont.Size() * 0.85f)));
697 
698 		// TODO: Clicking the screen shot view should open ShowImage with the
699 		// the screen shot. This could be done by writing the screen shot to
700 		// a temporary folder, launching ShowImage to display it, and writing
701 		// all other screenshots associated with the package to the same folder
702 		// so the user can use the ShowImage navigation to view the other
703 		// screenshots.
704 		fScreenshotView = new LinkedBitmapView("screenshot view",
705 			new BMessage(MSG_SHOW_SCREENSHOT));
706 		fScreenshotView->SetExplicitMinSize(BSize(64.0f, 64.0f));
707 		fScreenshotView->SetExplicitMaxSize(
708 			BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
709 		fScreenshotView->SetExplicitAlignment(
710 			BAlignment(B_ALIGN_CENTER, B_ALIGN_TOP));
711 
712 		fEmailIconView = new BitmapView("email icon view");
713 		fEmailLinkView = new LinkView("email link view", "",
714 			new BMessage(MSG_EMAIL_PUBLISHER));
715 		fEmailLinkView->SetFont(&smallFont);
716 
717 		fWebsiteIconView = new BitmapView("website icon view");
718 		fWebsiteLinkView = new LinkView("website link view", "",
719 			new BMessage(MSG_VISIT_PUBLISHER_WEBSITE));
720 		fWebsiteLinkView->SetFont(&smallFont);
721 
722 		BGroupView* leftGroup = new BGroupView(B_VERTICAL,
723 			B_USE_DEFAULT_SPACING);
724 
725 		fScreenshotView->SetViewUIColor(ViewUIColor(), kContentTint);
726 		fEmailLinkView->SetViewUIColor(ViewUIColor(), kContentTint);
727 		fWebsiteLinkView->SetViewUIColor(ViewUIColor(), kContentTint);
728 
729 		BLayoutBuilder::Group<>(this, B_HORIZONTAL, 0.0f)
730 			.AddGroup(leftGroup, 1.0f)
731 				.Add(fScreenshotView)
732 				.AddGroup(B_HORIZONTAL)
733 					.AddGrid(B_USE_HALF_ITEM_SPACING, B_USE_HALF_ITEM_SPACING)
734 						.Add(fEmailIconView, 0, 0)
735 						.Add(fEmailLinkView, 1, 0)
736 						.Add(fWebsiteIconView, 0, 1)
737 						.Add(fWebsiteLinkView, 1, 1)
738 					.End()
739 				.End()
740 				.SetInsets(B_USE_DEFAULT_SPACING)
741 				.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
742 			.End()
743 			.Add(scrollView, 2.0f)
744 
745 			.SetExplicitMaxSize(BSize(B_SIZE_UNSET, B_SIZE_UNLIMITED))
746 			.SetInsets(0.0f, -1.0f, -1.0f, -1.0f)
747 		;
748 	}
749 
750 	virtual ~AboutView()
751 	{
752 		Clear();
753 	}
754 
755 	virtual void AttachedToWindow()
756 	{
757 		fScreenshotView->SetTarget(this);
758 		fEmailLinkView->SetTarget(this);
759 		fWebsiteLinkView->SetTarget(this);
760 	}
761 
762 	virtual void AllAttached()
763 	{
764 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint);
765 
766 		for (int32 index = 0; index < CountChildren(); ++index)
767 			ChildAt(index)->AdoptParentColors();
768 	}
769 
770 	virtual void MessageReceived(BMessage* message)
771 	{
772 		switch (message->what) {
773 			case MSG_SHOW_SCREENSHOT:
774 			{
775 				// Forward to window for now
776 				Window()->PostMessage(message, Window());
777 				break;
778 			}
779 
780 			case MSG_EMAIL_PUBLISHER:
781 			{
782 				// TODO: Implement. If memory serves, there is a
783 				// standard command line interface which mail apps should
784 				// support, i.e. to open a compose window with the TO: field
785 				// already set.
786 				break;
787 			}
788 
789 			case MSG_VISIT_PUBLISHER_WEBSITE:
790 			{
791 				BUrl url(fWebsiteLinkView->Text());
792 				url.OpenWithPreferredApplication();
793 				break;
794 			}
795 
796 			default:
797 				BView::MessageReceived(message);
798 				break;
799 		}
800 	}
801 
802 	void SetPackage(const PackageInfo& package)
803 	{
804 		fDescriptionView->SetText(package.ShortDescription(),
805 			package.FullDescription());
806 
807 		fEmailIconView->SetBitmap(&fEmailIcon, SharedBitmap::SIZE_16);
808 		_SetContactInfo(fEmailLinkView, package.Publisher().Email());
809 		fWebsiteIconView->SetBitmap(&fWebsiteIcon, SharedBitmap::SIZE_16);
810 		_SetContactInfo(fWebsiteLinkView, package.Publisher().Website());
811 
812 		bool hasScreenshot = false;
813 		const BitmapList& screenShots = package.Screenshots();
814 		if (screenShots.CountItems() > 0) {
815 			const BitmapRef& bitmapRef = screenShots.ItemAtFast(0);
816 			if (bitmapRef.Get() != NULL) {
817 				hasScreenshot = true;
818 				fScreenshotView->SetBitmap(bitmapRef);
819 			}
820 		}
821 
822 		if (!hasScreenshot)
823 			fScreenshotView->UnsetBitmap();
824 
825 		fScreenshotView->SetEnabled(hasScreenshot);
826 	}
827 
828 	void Clear()
829 	{
830 		fDescriptionView->SetText("");
831 		fEmailIconView->UnsetBitmap();
832 		fEmailLinkView->SetText("");
833 		fWebsiteIconView->UnsetBitmap();
834 		fWebsiteLinkView->SetText("");
835 
836 		fScreenshotView->UnsetBitmap();
837 		fScreenshotView->SetEnabled(false);
838 	}
839 
840 private:
841 	void _SetContactInfo(LinkView* view, const BString& string)
842 	{
843 		if (string.Length() > 0) {
844 			view->SetText(string);
845 			view->SetEnabled(true);
846 		} else {
847 			view->SetText(B_TRANSLATE("<no info>"));
848 			view->SetEnabled(false);
849 		}
850 	}
851 
852 private:
853 	MarkupTextView*		fDescriptionView;
854 
855 	LinkedBitmapView*	fScreenshotView;
856 
857 	SharedBitmap		fEmailIcon;
858 	BitmapView*			fEmailIconView;
859 	LinkView*			fEmailLinkView;
860 
861 	SharedBitmap		fWebsiteIcon;
862 	BitmapView*			fWebsiteIconView;
863 	LinkView*			fWebsiteLinkView;
864 };
865 
866 
867 // #pragma mark - UserRatingsView
868 
869 
870 class RatingItemView : public BGroupView {
871 public:
872 	RatingItemView(const UserRating& rating)
873 		:
874 		BGroupView(B_HORIZONTAL, 0.0f)
875 	{
876 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint);
877 
878 		BGroupLayout* verticalGroup = new BGroupLayout(B_VERTICAL, 0.0f);
879 		GroupLayout()->AddItem(verticalGroup);
880 
881 		{
882 			BStringView* userNicknameView = new BStringView("user-nickname",
883 				rating.User().NickName());
884 			userNicknameView->SetFont(be_bold_font);
885 			verticalGroup->AddView(userNicknameView);
886 		}
887 
888 		BGroupLayout* ratingGroup =
889 			new BGroupLayout(B_HORIZONTAL, B_USE_DEFAULT_SPACING);
890 		verticalGroup->AddItem(ratingGroup);
891 
892 		if (rating.Rating() >= 0) {
893 			RatingView* ratingView = new RatingView("package rating view");
894 			ratingView->SetRating(rating.Rating());
895 			ratingGroup->AddView(ratingView);
896 		}
897 
898 		{
899 			BString createTimestampPresentation =
900 				LocaleUtils::TimestampToDateTimeString(
901 					rating.CreateTimestamp());
902 
903 			BString ratingContextDescription(
904 				B_TRANSLATE("%hd.timestamp% (version %hd.version%)"));
905 			ratingContextDescription.ReplaceAll("%hd.timestamp%",
906 				createTimestampPresentation);
907 			ratingContextDescription.ReplaceAll("%hd.version%",
908 				rating.PackageVersion());
909 
910 			BStringView* ratingContextView = new BStringView("rating-context",
911 				ratingContextDescription);
912 			BFont versionFont(be_plain_font);
913 			ratingContextView->SetFont(&versionFont);
914 			ratingGroup->AddView(ratingContextView);
915 		}
916 
917 		ratingGroup->AddItem(BSpaceLayoutItem::CreateGlue());
918 
919 		if (rating.Comment() > 0) {
920 			TextView* textView = new TextView("rating-text");
921 			ParagraphStyle paragraphStyle(textView->ParagraphStyle());
922 			paragraphStyle.SetJustify(true);
923 			textView->SetParagraphStyle(paragraphStyle);
924 			textView->SetText(rating.Comment());
925 			verticalGroup->AddItem(BSpaceLayoutItem::CreateVerticalStrut(8.0f));
926 			verticalGroup->AddView(textView);
927 			verticalGroup->AddItem(BSpaceLayoutItem::CreateVerticalStrut(8.0f));
928 		}
929 
930 		verticalGroup->SetInsets(B_USE_DEFAULT_SPACING);
931 
932 		SetFlags(Flags() | B_WILL_DRAW);
933 	}
934 
935 	void AllAttached()
936 	{
937 		for (int32 index = 0; index < CountChildren(); ++index)
938 			ChildAt(index)->AdoptParentColors();
939 	}
940 
941 	void Draw(BRect rect)
942 	{
943 		rgb_color color = mix_color(ViewColor(), ui_color(B_PANEL_TEXT_COLOR), 64);
944 		SetHighColor(color);
945 		StrokeLine(Bounds().LeftBottom(), Bounds().RightBottom());
946 	}
947 
948 };
949 
950 
951 class RatingSummaryView : public BGridView {
952 public:
953 	RatingSummaryView()
954 		:
955 		BGridView("rating summary view", B_USE_HALF_ITEM_SPACING, 0.0f)
956 	{
957 		float tint = kContentTint - 0.1;
958 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR, tint);
959 
960 		BLayoutBuilder::Grid<> layoutBuilder(this);
961 
962 		BFont smallFont;
963 		GetFont(&smallFont);
964 		smallFont.SetSize(std::max(9.0f, floorf(smallFont.Size() * 0.85f)));
965 
966 		for (int32 i = 0; i < 5; i++) {
967 			BString label;
968 			label.SetToFormat("%" B_PRId32, 5 - i);
969 			fLabelViews[i] = new BStringView("", label);
970 			fLabelViews[i]->SetFont(&smallFont);
971 			fLabelViews[i]->SetViewUIColor(ViewUIColor(), tint);
972 			layoutBuilder.Add(fLabelViews[i], 0, i);
973 
974 			fDiagramBarViews[i] = new DiagramBarView();
975 			layoutBuilder.Add(fDiagramBarViews[i], 1, i);
976 
977 			fCountViews[i] = new BStringView("", "");
978 			fCountViews[i]->SetFont(&smallFont);
979 			fCountViews[i]->SetViewUIColor(ViewUIColor(), tint);
980 			fCountViews[i]->SetAlignment(B_ALIGN_RIGHT);
981 			layoutBuilder.Add(fCountViews[i], 2, i);
982 		}
983 
984 		layoutBuilder.SetInsets(5);
985 	}
986 
987 	void SetToSummary(const RatingSummary& summary) {
988 		for (int32 i = 0; i < 5; i++) {
989 			int32 count = summary.ratingCountByStar[4 - i];
990 
991 			BString label;
992 			label.SetToFormat("%" B_PRId32, count);
993 			fCountViews[i]->SetText(label);
994 
995 			if (summary.ratingCount > 0) {
996 				fDiagramBarViews[i]->SetValue(
997 					(float)count / summary.ratingCount);
998 			} else
999 				fDiagramBarViews[i]->SetValue(0.0f);
1000 		}
1001 	}
1002 
1003 	void Clear() {
1004 		for (int32 i = 0; i < 5; i++) {
1005 			fCountViews[i]->SetText("");
1006 			fDiagramBarViews[i]->SetValue(0.0f);
1007 		}
1008 	}
1009 
1010 private:
1011 	BStringView*	fLabelViews[5];
1012 	DiagramBarView*	fDiagramBarViews[5];
1013 	BStringView*	fCountViews[5];
1014 };
1015 
1016 
1017 class UserRatingsView : public BGroupView {
1018 public:
1019 	UserRatingsView()
1020 		:
1021 		BGroupView("package ratings view", B_HORIZONTAL)
1022 	{
1023 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint);
1024 
1025 		fRatingSummaryView = new RatingSummaryView();
1026 
1027 		ScrollableGroupView* ratingsContainerView = new ScrollableGroupView();
1028 		ratingsContainerView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR,
1029 												kContentTint);
1030 		fRatingContainerLayout = ratingsContainerView->GroupLayout();
1031 
1032 		BScrollView* scrollView = new RatingsScrollView(
1033 			"ratings scroll view", ratingsContainerView);
1034 		scrollView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
1035 			B_SIZE_UNLIMITED));
1036 
1037 		BLayoutBuilder::Group<>(this)
1038 			.AddGroup(B_VERTICAL)
1039 				.Add(fRatingSummaryView, 0.0f)
1040 				.AddGlue()
1041 				.SetInsets(0.0f, B_USE_DEFAULT_SPACING, 0.0f, 0.0f)
1042 			.End()
1043 			.AddStrut(64.0)
1044 			.Add(scrollView, 1.0f)
1045 			.SetInsets(B_USE_DEFAULT_SPACING, -1.0f, -1.0f, -1.0f)
1046 		;
1047 	}
1048 
1049 	virtual ~UserRatingsView()
1050 	{
1051 		Clear();
1052 	}
1053 
1054 	void SetPackage(const PackageInfo& package)
1055 	{
1056 		ClearRatings();
1057 
1058 		// TODO: Re-use rating summary already used for TitleView...
1059 		fRatingSummaryView->SetToSummary(package.CalculateRatingSummary());
1060 
1061 		const UserRatingList& userRatings = package.UserRatings();
1062 
1063 		int count = userRatings.CountItems();
1064 		if (count == 0) {
1065 			BStringView* noRatingsView = new BStringView("no ratings",
1066 				B_TRANSLATE("No user ratings available."));
1067 			noRatingsView->SetViewUIColor(ViewUIColor(), kContentTint);
1068 			noRatingsView->SetAlignment(B_ALIGN_CENTER);
1069 			noRatingsView->SetHighColor(disable_color(ui_color(B_PANEL_TEXT_COLOR),
1070 				ViewColor()));
1071 			noRatingsView->SetExplicitMaxSize(
1072 				BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
1073 			fRatingContainerLayout->AddView(0, noRatingsView);
1074 			return;
1075 		}
1076 
1077 		// TODO: Sort by age or usefullness rating
1078 		for (int i = count - 1; i >= 0; i--) {
1079 			const UserRating& rating = userRatings.ItemAtFast(i);
1080 				// was previously filtering comments just for the current
1081 				// user's language, but as there are not so many comments at
1082 				// the moment, just show all of them for now.
1083 			RatingItemView* view = new RatingItemView(rating);
1084 			fRatingContainerLayout->AddView(0, view);
1085 		}
1086 	}
1087 
1088 	void Clear()
1089 	{
1090 		fRatingSummaryView->Clear();
1091 		ClearRatings();
1092 	}
1093 
1094 	void ClearRatings()
1095 	{
1096 		for (int32 i = fRatingContainerLayout->CountItems() - 1;
1097 				BLayoutItem* item = fRatingContainerLayout->ItemAt(i); i--) {
1098 			BView* view = dynamic_cast<RatingItemView*>(item->View());
1099 			if (view == NULL)
1100 				view = dynamic_cast<BStringView*>(item->View());
1101 			if (view != NULL) {
1102 				view->RemoveSelf();
1103 				delete view;
1104 			}
1105 		}
1106 	}
1107 
1108 private:
1109 	BGroupLayout*			fRatingContainerLayout;
1110 	RatingSummaryView*		fRatingSummaryView;
1111 };
1112 
1113 
1114 // #pragma mark - ContentsView
1115 
1116 
1117 class ContentsView : public BGroupView {
1118 public:
1119 	ContentsView()
1120 		:
1121 		BGroupView("package contents view", B_HORIZONTAL)
1122 	{
1123 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint);
1124 
1125 		fPackageContents = new PackageContentsView("contents_list");
1126 		AddChild(fPackageContents);
1127 
1128 	}
1129 
1130 	virtual ~ContentsView()
1131 	{
1132 	}
1133 
1134 	virtual void Draw(BRect updateRect)
1135 	{
1136 	}
1137 
1138 	void SetPackage(const PackageInfoRef& package)
1139 	{
1140 		fPackageContents->SetPackage(package);
1141 	}
1142 
1143 	void Clear()
1144 	{
1145 		fPackageContents->Clear();
1146 	}
1147 
1148 private:
1149 	PackageContentsView*	fPackageContents;
1150 };
1151 
1152 
1153 // #pragma mark - ChangelogView
1154 
1155 
1156 class ChangelogView : public BGroupView {
1157 public:
1158 	ChangelogView()
1159 		:
1160 		BGroupView("package changelog view", B_HORIZONTAL)
1161 	{
1162 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR, kContentTint);
1163 
1164 		fTextView = new MarkupTextView("changelog view");
1165 		fTextView->SetLowUIColor(ViewUIColor());
1166 		fTextView->SetInsets(be_plain_font->Size());
1167 
1168 		BScrollView* scrollView = new CustomScrollView(
1169 			"changelog scroll view", fTextView);
1170 
1171 		BLayoutBuilder::Group<>(this)
1172 			.Add(BSpaceLayoutItem::CreateHorizontalStrut(32.0f))
1173 			.Add(scrollView, 1.0f)
1174 			.SetInsets(B_USE_DEFAULT_SPACING, -1.0f, -1.0f, -1.0f)
1175 		;
1176 	}
1177 
1178 	virtual ~ChangelogView()
1179 	{
1180 	}
1181 
1182 	virtual void Draw(BRect updateRect)
1183 	{
1184 	}
1185 
1186 	void SetPackage(const PackageInfo& package)
1187 	{
1188 		const BString& changelog = package.Changelog();
1189 		if (changelog.Length() > 0)
1190 			fTextView->SetText(changelog);
1191 		else
1192 			fTextView->SetDisabledText(B_TRANSLATE("No changelog available."));
1193 	}
1194 
1195 	void Clear()
1196 	{
1197 		fTextView->SetText("");
1198 	}
1199 
1200 private:
1201 	MarkupTextView*		fTextView;
1202 };
1203 
1204 
1205 // #pragma mark - PagesView
1206 
1207 
1208 class PagesView : public BTabView {
1209 public:
1210 	PagesView()
1211 		:
1212 		BTabView("pages view", B_WIDTH_FROM_WIDEST),
1213 		fLayout(new BCardLayout())
1214 	{
1215 		SetBorder(B_NO_BORDER);
1216 
1217 		fAboutView = new AboutView();
1218 		fUserRatingsView = new UserRatingsView();
1219 		fChangelogView = new ChangelogView();
1220 		fContentsView = new ContentsView();
1221 
1222 		AddTab(fAboutView);
1223 		AddTab(fUserRatingsView);
1224 		AddTab(fChangelogView);
1225 		AddTab(fContentsView);
1226 
1227 		TabAt(0)->SetLabel(B_TRANSLATE("About"));
1228 		TabAt(1)->SetLabel(B_TRANSLATE("Ratings"));
1229 		TabAt(2)->SetLabel(B_TRANSLATE("Changelog"));
1230 		TabAt(3)->SetLabel(B_TRANSLATE("Contents"));
1231 
1232 		Select(0);
1233 	}
1234 
1235 	virtual ~PagesView()
1236 	{
1237 		Clear();
1238 	}
1239 
1240 	void SetPackage(const PackageInfoRef& package, bool switchToDefaultTab)
1241 	{
1242 		if (switchToDefaultTab)
1243 			Select(0);
1244 		fAboutView->SetPackage(*package.Get());
1245 		fUserRatingsView->SetPackage(*package.Get());
1246 		fChangelogView->SetPackage(*package.Get());
1247 		fContentsView->SetPackage(package);
1248 	}
1249 
1250 	void Clear()
1251 	{
1252 		fAboutView->Clear();
1253 		fUserRatingsView->Clear();
1254 		fChangelogView->Clear();
1255 		fContentsView->Clear();
1256 	}
1257 
1258 private:
1259 	BCardLayout*		fLayout;
1260 
1261 	AboutView*			fAboutView;
1262 	UserRatingsView*	fUserRatingsView;
1263 	ChangelogView*		fChangelogView;
1264 	ContentsView* 		fContentsView;
1265 };
1266 
1267 
1268 // #pragma mark - PackageInfoView
1269 
1270 
1271 PackageInfoView::PackageInfoView(BLocker* modelLock,
1272 		PackageActionHandler* handler)
1273 	:
1274 	BView("package info view", 0),
1275 	fModelLock(modelLock),
1276 	fPackageListener(new(std::nothrow) OnePackageMessagePackageListener(this))
1277 {
1278 	fCardLayout = new BCardLayout();
1279 	SetLayout(fCardLayout);
1280 
1281 	BGroupView* noPackageCard = new BGroupView("no package card", B_VERTICAL);
1282 	AddChild(noPackageCard);
1283 
1284 	BStringView* noPackageView = new BStringView("no package view",
1285 		B_TRANSLATE("Click a package to view information"));
1286 	noPackageView->SetHighUIColor(B_PANEL_TEXT_COLOR, B_LIGHTEN_1_TINT);
1287 	noPackageView->SetExplicitAlignment(BAlignment(
1288 		B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER));
1289 
1290 	BLayoutBuilder::Group<>(noPackageCard)
1291 		.Add(noPackageView)
1292 		.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED))
1293 	;
1294 
1295 	BGroupView* packageCard = new BGroupView("package card", B_VERTICAL);
1296 	AddChild(packageCard);
1297 
1298 	fCardLayout->SetVisibleItem((int32)0);
1299 
1300 	fTitleView = new TitleView();
1301 	fPackageActionView = new PackageActionView(handler);
1302 	fPackageActionView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
1303 		B_SIZE_UNSET));
1304 	fPagesView = new PagesView();
1305 
1306 	BLayoutBuilder::Group<>(packageCard)
1307 		.AddGroup(B_HORIZONTAL, 0.0f)
1308 			.Add(fTitleView, 6.0f)
1309 			.Add(fPackageActionView, 1.0f)
1310 			.SetInsets(
1311 				B_USE_DEFAULT_SPACING, 0.0f,
1312 				B_USE_DEFAULT_SPACING, 0.0f)
1313 		.End()
1314 		.Add(fPagesView)
1315 	;
1316 
1317 	Clear();
1318 }
1319 
1320 
1321 PackageInfoView::~PackageInfoView()
1322 {
1323 	fPackageListener->SetPackage(PackageInfoRef(NULL));
1324 	delete fPackageListener;
1325 }
1326 
1327 
1328 void
1329 PackageInfoView::AttachedToWindow()
1330 {
1331 }
1332 
1333 
1334 void
1335 PackageInfoView::MessageReceived(BMessage* message)
1336 {
1337 	switch (message->what) {
1338 		case MSG_UPDATE_PACKAGE:
1339 		{
1340 			if (fPackageListener->Package().Get() == NULL)
1341 				break;
1342 
1343 			BString name;
1344 			uint32 changes;
1345 			if (message->FindString("name", &name) != B_OK
1346 				|| message->FindUInt32("changes", &changes) != B_OK) {
1347 				break;
1348 			}
1349 
1350 			const PackageInfoRef& package = fPackageListener->Package();
1351 			if (package->Name() != name)
1352 				break;
1353 
1354 			BAutolock _(fModelLock);
1355 
1356 			if ((changes & PKG_CHANGED_SUMMARY) != 0
1357 				|| (changes & PKG_CHANGED_DESCRIPTION) != 0
1358 				|| (changes & PKG_CHANGED_SCREENSHOTS) != 0
1359 				|| (changes & PKG_CHANGED_TITLE) != 0
1360 				|| (changes & PKG_CHANGED_RATINGS) != 0
1361 				|| (changes & PKG_CHANGED_STATE) != 0
1362 				|| (changes & PKG_CHANGED_CHANGELOG) != 0) {
1363 				fPagesView->SetPackage(package, false);
1364 			}
1365 
1366 			if ((changes & PKG_CHANGED_TITLE) != 0
1367 				|| (changes & PKG_CHANGED_RATINGS) != 0) {
1368 				fTitleView->SetPackage(*package.Get());
1369 			}
1370 
1371 			if ((changes & PKG_CHANGED_STATE) != 0) {
1372 				fPackageActionView->SetPackage(*package.Get());
1373 			}
1374 
1375 			break;
1376 		}
1377 		default:
1378 			BView::MessageReceived(message);
1379 			break;
1380 	}
1381 }
1382 
1383 
1384 void
1385 PackageInfoView::SetPackage(const PackageInfoRef& packageRef)
1386 {
1387 	BAutolock _(fModelLock);
1388 
1389 	if (packageRef.Get() == NULL) {
1390 		Clear();
1391 		return;
1392 	}
1393 
1394 	bool switchToDefaultTab = true;
1395 	if (fPackage == packageRef) {
1396 		// When asked to display the already showing package ref,
1397 		// don't switch to the default tab.
1398 		switchToDefaultTab = false;
1399 	} else if (fPackage.Get() != NULL && packageRef.Get() != NULL
1400 		&& fPackage->Name() == packageRef->Name()) {
1401 		// When asked to display a different PackageInfo instance,
1402 		// but it has the same package title as the already showing
1403 		// instance, this probably means there was a repository
1404 		// refresh and we are in fact still requested to show the
1405 		// same package as before the refresh.
1406 		switchToDefaultTab = false;
1407 	}
1408 
1409 	const PackageInfo& package = *packageRef.Get();
1410 
1411 	fTitleView->SetPackage(package);
1412 	fPackageActionView->SetPackage(package);
1413 	fPagesView->SetPackage(packageRef, switchToDefaultTab);
1414 
1415 	fCardLayout->SetVisibleItem(1);
1416 
1417 	fPackageListener->SetPackage(packageRef);
1418 
1419 	// Set the fPackage reference last, so we keep a reference to the
1420 	// previous package before switching all the views to the new package.
1421 	// Otherwise the PackageInfo instance may go away because we had the
1422 	// last reference. And some of the views, the PackageActionView for
1423 	// example, keeps references to stuff from the previous package and
1424 	// access it while switching to the new package.
1425 	fPackage = packageRef;
1426 }
1427 
1428 
1429 void
1430 PackageInfoView::Clear()
1431 {
1432 	BAutolock _(fModelLock);
1433 
1434 	fTitleView->Clear();
1435 	fPackageActionView->Clear();
1436 	fPagesView->Clear();
1437 
1438 	fCardLayout->SetVisibleItem((int32)0);
1439 
1440 	fPackageListener->SetPackage(PackageInfoRef(NULL));
1441 
1442 	fPackage.Unset();
1443 }
1444