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