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