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