xref: /haiku/src/apps/haikudepot/ui/FeaturedPackagesView.cpp (revision 0975f16f7ca94b654cb6a55b3316daae89843abb)
1 /*
2  * Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>.
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 #include "FeaturedPackagesView.h"
7 
8 #include <stdio.h>
9 
10 #include <Catalog.h>
11 #include <Font.h>
12 #include <LayoutBuilder.h>
13 #include <Message.h>
14 #include <ScrollView.h>
15 #include <StringView.h>
16 #include <SpaceLayoutItem.h>
17 
18 #include "BitmapView.h"
19 #include "MainWindow.h"
20 #include "MarkupTextView.h"
21 #include "MessagePackageListener.h"
22 #include "RatingView.h"
23 #include "ScrollableGroupView.h"
24 
25 
26 #undef B_TRANSLATION_CONTEXT
27 #define B_TRANSLATION_CONTEXT "FeaturedPackagesView"
28 
29 
30 static const rgb_color kLightBlack = (rgb_color){ 60, 60, 60, 255 };
31 
32 static BitmapRef sInstalledIcon(new(std::nothrow) SharedBitmap(504), true);
33 
34 
35 // #pragma mark - PackageView
36 
37 
38 class PackageView : public BGroupView {
39 public:
40 	PackageView()
41 		:
42 		BGroupView("package view", B_HORIZONTAL),
43 		fPackageListener(
44 			new(std::nothrow) OnePackageMessagePackageListener(this)),
45 		fSelected(false)
46 	{
47 		SetViewColor(255, 255, 255);
48 		SetEventMask(B_POINTER_EVENTS);
49 
50 		fIconView = new BitmapView("package icon view");
51 		fInstalledIconView = new BitmapView("installed icon view");
52 		fTitleView = new BStringView("package title view", "");
53 		fPublisherView = new BStringView("package publisher view", "");
54 
55 		// Title font
56 		BFont font;
57 		GetFont(&font);
58 		font_family family;
59 		font_style style;
60 		font.SetSize(ceilf(font.Size() * 1.8f));
61 		font.GetFamilyAndStyle(&family, &style);
62 		font.SetFamilyAndStyle(family, "Bold");
63 		fTitleView->SetFont(&font);
64 
65 		// Publisher font
66 		GetFont(&font);
67 		font.SetSize(std::max(9.0f, floorf(font.Size() * 0.92f)));
68 		font.SetFamilyAndStyle(family, "Italic");
69 		fPublisherView->SetFont(&font);
70 		fPublisherView->SetHighColor(kLightBlack);
71 
72 		// Summary text view
73 		fSummaryView = new MarkupTextView("package summary");
74 		fSummaryView->SetSelectionEnabled(false);
75 
76 		// Rating view
77 		fRatingView = new RatingView("package rating view");
78 
79 		fAvgRating = new BStringView("package average rating", "");
80 		fAvgRating->SetFont(&font);
81 		fAvgRating->SetHighColor(kLightBlack);
82 
83 		fVoteInfo = new BStringView("package vote info", "");
84 		// small font
85 		GetFont(&font);
86 		font.SetSize(std::max(9.0f, floorf(font.Size() * 0.85f)));
87 		fVoteInfo->SetFont(&font);
88 		fVoteInfo->SetHighColor(kLightBlack);
89 
90 		BLayoutBuilder::Group<>(this)
91 			.Add(fIconView)
92 			.AddGroup(B_VERTICAL, 1.0f, 2.2f)
93 				.AddGroup(B_HORIZONTAL)
94 					.Add(fTitleView)
95 					.Add(fInstalledIconView)
96 					.AddGlue()
97 				.End()
98 				.Add(fPublisherView)
99 				.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
100 			.End()
101 			.AddGlue(0.1f)
102 			.AddGroup(B_HORIZONTAL, 0.8f)
103 				.Add(fRatingView)
104 				.Add(fAvgRating)
105 				.Add(fVoteInfo)
106 			.End()
107 			.AddGlue(0.2f)
108 			.Add(fSummaryView, 2.0f)
109 
110 			.SetInsets(B_USE_WINDOW_INSETS)
111 		;
112 
113 		Clear();
114 	}
115 
116 	virtual ~PackageView()
117 	{
118 		fPackageListener->SetPackage(PackageInfoRef(NULL));
119 		delete fPackageListener;
120 	}
121 
122 	virtual void MessageReceived(BMessage* message)
123 	{
124 		switch (message->what) {
125 			case B_MOUSE_WHEEL_CHANGED:
126 				Window()->PostMessage(message, Parent());
127 				break;
128 
129 			case MSG_UPDATE_PACKAGE:
130 				SetPackage(fPackageListener->Package());
131 				break;
132 		}
133 	}
134 
135 	virtual void MouseDown(BPoint where)
136 	{
137 		BRect bounds = Bounds();
138 		BRect parentBounds = Parent()->Bounds();
139 		ConvertFromParent(&parentBounds);
140 		bounds = bounds & parentBounds;
141 
142 		if (bounds.Contains(where) && Window()->IsActive()) {
143 			BMessage message(MSG_PACKAGE_SELECTED);
144 			message.AddString("name", PackageName());
145 			Window()->PostMessage(&message);
146 		}
147 	}
148 
149 	void SetPackage(const PackageInfoRef& package)
150 	{
151 		fPackageListener->SetPackage(package);
152 
153 		if (package->Icon().Get() != NULL) {
154 			fIconView->SetBitmap(package->Icon(), SharedBitmap::SIZE_64);
155 		} else
156 			fIconView->UnsetBitmap();
157 
158 		if (package->State() == ACTIVATED) {
159 			fInstalledIconView->SetBitmap(sInstalledIcon,
160 				SharedBitmap::SIZE_16);
161 		} else
162 			fInstalledIconView->UnsetBitmap();
163 
164 		fTitleView->SetText(package->Title());
165 
166 		BString publisher = package->Publisher().Name();
167 		fPublisherView->SetText(publisher);
168 
169 		BString summary = package->ShortDescription();
170 		fSummaryView->SetText(summary);
171 
172 		RatingSummary ratingSummary = package->CalculateRatingSummary();
173 
174 		fRatingView->SetRating(ratingSummary.averageRating);
175 
176 		if (ratingSummary.ratingCount > 0) {
177 			BString avgRating;
178 			avgRating.SetToFormat("%.1f", ratingSummary.averageRating);
179 			fAvgRating->SetText(avgRating);
180 
181 			BString votes;
182 			votes.SetToFormat("%d", ratingSummary.ratingCount);
183 
184 			BString voteInfo(B_TRANSLATE("(%Votes%)"));
185 			voteInfo.ReplaceAll("%Votes%", votes);
186 
187 			fVoteInfo->SetText(voteInfo);
188 		} else {
189 			fAvgRating->SetText("");
190 			fVoteInfo->SetText("");
191 		}
192 
193 		InvalidateLayout();
194 		Invalidate();
195 	}
196 
197 	void Clear()
198 	{
199 		fPackageListener->SetPackage(PackageInfoRef(NULL));
200 
201 		fIconView->UnsetBitmap();
202 		fInstalledIconView->UnsetBitmap();
203 		fTitleView->SetText("");
204 		fPublisherView->SetText("");
205 		fSummaryView->SetText("");
206 		fRatingView->SetRating(-1.0f);
207 		fAvgRating->SetText("");
208 		fVoteInfo->SetText("");
209 	}
210 
211 	const char* PackageTitle() const
212 	{
213 		return fTitleView->Text();
214 	}
215 
216 	const char* PackageName() const
217 	{
218 		if (fPackageListener->Package().Get() != NULL)
219 			return fPackageListener->Package()->Name();
220 		else
221 			return "";
222 	}
223 
224 	void SetSelected(bool selected)
225 	{
226 		if (fSelected == selected)
227 			return;
228 		fSelected = selected;
229 
230 		rgb_color bgColor;
231 		if (fSelected)
232 			bgColor = ui_color(B_LIST_SELECTED_BACKGROUND_COLOR);
233 		else
234 			bgColor = (rgb_color){ 255, 255, 255, 255 };
235 
236 		List<BView*, true> views;
237 
238 		views.Add(this);
239 		views.Add(fIconView);
240 		views.Add(fInstalledIconView);
241 		views.Add(fTitleView);
242 		views.Add(fPublisherView);
243 		views.Add(fSummaryView);
244 		views.Add(fRatingView);
245 		views.Add(fAvgRating);
246 		views.Add(fVoteInfo);
247 
248 		for (int32 i = 0; i < views.CountItems(); i++) {
249 			BView* view = views.ItemAtFast(i);
250 
251 			view->SetViewColor(bgColor);
252 			view->SetLowColor(bgColor);
253 			view->Invalidate();
254 		}
255 	}
256 
257 private:
258 	OnePackageMessagePackageListener* fPackageListener;
259 
260 	BitmapView*						fIconView;
261 	BitmapView*						fInstalledIconView;
262 
263 	BStringView*					fTitleView;
264 	BStringView*					fPublisherView;
265 
266 	MarkupTextView*					fSummaryView;
267 
268 	RatingView*						fRatingView;
269 	BStringView*					fAvgRating;
270 	BStringView*					fVoteInfo;
271 
272 	bool							fSelected;
273 
274 	BString							fPackageName;
275 };
276 
277 
278 // #pragma mark - FeaturedPackagesView
279 
280 
281 FeaturedPackagesView::FeaturedPackagesView()
282 	:
283 	BView("featured package view", 0)
284 {
285 	BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
286 	SetLayout(layout);
287 
288 	ScrollableGroupView* containerView = new ScrollableGroupView();
289 	containerView->SetViewColor(255, 255, 255);
290 	fPackageListLayout = containerView->GroupLayout();
291 
292 	BScrollView* scrollView = new BScrollView(
293 		"featured packages scroll view", containerView,
294 		0, false, true, B_FANCY_BORDER);
295 
296 	BScrollBar* scrollBar = scrollView->ScrollBar(B_VERTICAL);
297 	if (scrollBar != NULL)
298 		scrollBar->SetSteps(10.0f, 20.0f);
299 
300 	BLayoutBuilder::Group<>(this)
301 		.Add(scrollView, 1.0f)
302 	;
303 }
304 
305 
306 FeaturedPackagesView::~FeaturedPackagesView()
307 {
308 }
309 
310 
311 void
312 FeaturedPackagesView::AddPackage(const PackageInfoRef& package)
313 {
314 	// Find insertion index (alphabetical)
315 	int32 index = 0;
316 	for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) {
317 		PackageView* view = dynamic_cast<PackageView*>(item->View());
318 		if (view == NULL)
319 			break;
320 
321 		BString name = view->PackageName();
322 		if (name == package->Name()) {
323 			// Don't add packages more than once
324 			return;
325 		}
326 
327 		BString title = view->PackageTitle();
328 		if (title.Compare(package->Title()) < 0)
329 			index++;
330 	}
331 
332 	PackageView* view = new PackageView();
333 	view->SetPackage(package);
334 
335 	fPackageListLayout->AddView(index, view);
336 }
337 
338 
339 void
340 FeaturedPackagesView::RemovePackage(const PackageInfoRef& package)
341 {
342 	// Find the package
343 	for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) {
344 		PackageView* view = dynamic_cast<PackageView*>(item->View());
345 		if (view == NULL)
346 			break;
347 
348 		BString name = view->PackageName();
349 		if (name == package->Name()) {
350 			view->RemoveSelf();
351 			delete view;
352 			break;
353 		}
354 	}
355 }
356 
357 
358 void
359 FeaturedPackagesView::Clear()
360 {
361 	for (int32 i = fPackageListLayout->CountItems() - 1;
362 			BLayoutItem* item = fPackageListLayout->ItemAt(i); i--) {
363 		BView* view = dynamic_cast<PackageView*>(item->View());
364 		if (view != NULL) {
365 			view->RemoveSelf();
366 			delete view;
367 		}
368 	}
369 }
370 
371 
372 void
373 FeaturedPackagesView::SelectPackage(const PackageInfoRef& package)
374 {
375 	BString selectedName;
376 	if (package.Get() != NULL)
377 		selectedName = package->Name();
378 
379 	for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) {
380 		PackageView* view = dynamic_cast<PackageView*>(item->View());
381 		if (view == NULL)
382 			break;
383 
384 		BString name = view->PackageName();
385 		view->SetSelected(name == selectedName);
386 	}
387 }
388 
389 
390 void
391 FeaturedPackagesView::CleanupIcons()
392 {
393 	sInstalledIcon.Unset();
394 }
395