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