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