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