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