xref: /haiku/src/apps/haikudepot/ui/FeaturedPackagesView.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>.
3  * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
4  * Copyright 2020-2021, Andrew Lindesay <apl@lindesay.co.nz>.
5  * All rights reserved. Distributed under the terms of the MIT License.
6  */
7 
8 #include "FeaturedPackagesView.h"
9 
10 #include <algorithm>
11 #include <vector>
12 
13 #include <Bitmap.h>
14 #include <Catalog.h>
15 #include <Font.h>
16 #include <LayoutBuilder.h>
17 #include <LayoutItem.h>
18 #include <Message.h>
19 #include <ScrollView.h>
20 #include <StringView.h>
21 #include <SpaceLayoutItem.h>
22 
23 #include "BitmapView.h"
24 #include "HaikuDepotConstants.h"
25 #include "LocaleUtils.h"
26 #include "Logger.h"
27 #include "MainWindow.h"
28 #include "MarkupTextView.h"
29 #include "MessagePackageListener.h"
30 #include "RatingUtils.h"
31 #include "RatingView.h"
32 #include "ScrollableGroupView.h"
33 #include "SharedBitmap.h"
34 
35 
36 #undef B_TRANSLATION_CONTEXT
37 #define B_TRANSLATION_CONTEXT "FeaturedPackagesView"
38 
39 
40 #define HEIGHT_PACKAGE 84.0f
41 #define SIZE_ICON 64.0f
42 #define X_POSITION_RATING 350.0f
43 #define X_POSITION_SUMMARY 500.0f
44 #define WIDTH_RATING 100.0f
45 #define Y_PROPORTION_TITLE 0.35f
46 #define Y_PROPORTION_PUBLISHER 0.60f
47 #define Y_PROPORTION_CHRONOLOGICAL_DATA 0.75f
48 #define PADDING 8.0f
49 
50 
51 static BitmapRef sInstalledIcon(new(std::nothrow)
52 	SharedBitmap(RSRC_INSTALLED), true);
53 
54 
55 // #pragma mark - PackageView
56 
57 
58 class StackedFeaturedPackagesView : public BView {
59 public:
60 	StackedFeaturedPackagesView(Model& model)
61 		:
62 		BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS),
63 		fModel(model),
64 		fSelectedIndex(-1),
65 		fPackageListener(
66 			new(std::nothrow) OnePackageMessagePackageListener(this)),
67 		fLowestIndexAddedOrRemoved(-1)
68 	{
69 		SetEventMask(B_POINTER_EVENTS);
70 		Clear();
71 	}
72 
73 
74 	virtual ~StackedFeaturedPackagesView()
75 	{
76 		fPackageListener->SetPackage(PackageInfoRef(NULL));
77 		fPackageListener->ReleaseReference();
78 	}
79 
80 // #pragma mark - message handling and events
81 
82 	virtual void MessageReceived(BMessage* message)
83 	{
84 		switch (message->what) {
85 			case MSG_UPDATE_PACKAGE:
86 			{
87 				BString name;
88 				if (message->FindString("name", &name) != B_OK)
89 					HDINFO("expected 'name' key on package update message");
90 				else
91 					_HandleUpdatePackage(name);
92 				break;
93 			}
94 
95 			case B_COLORS_UPDATED:
96 			{
97 				Invalidate();
98 				break;
99 			}
100 
101 			default:
102 				BView::MessageReceived(message);
103 				break;
104 		}
105 	}
106 
107 
108 	virtual void MouseDown(BPoint where)
109 	{
110 		if (Window()->IsActive() && !IsHidden()) {
111 			BRect bounds = Bounds();
112 			BRect parentBounds = Parent()->Bounds();
113 			ConvertFromParent(&parentBounds);
114 			bounds = bounds & parentBounds;
115 			if (bounds.Contains(where)) {
116 				_MessageSelectIndex(_IndexOfY(where.y));
117 				MakeFocus();
118 			}
119 		}
120 	}
121 
122 
123 	virtual void KeyDown(const char* bytes, int32 numBytes)
124 	{
125 		char key = bytes[0];
126 
127 		switch (key) {
128 			case B_RIGHT_ARROW:
129 			case B_DOWN_ARROW:
130 			{
131 				int32 lastIndex = static_cast<int32>(fPackages.size()) - 1;
132 				if (!IsEmpty() && fSelectedIndex != -1
133 						&& fSelectedIndex < lastIndex) {
134 					_MessageSelectIndex(fSelectedIndex + 1);
135 				}
136 				break;
137 			}
138 			case B_LEFT_ARROW:
139 			case B_UP_ARROW:
140 				if (fSelectedIndex > 0)
141 					_MessageSelectIndex( fSelectedIndex - 1);
142 				break;
143 			case B_PAGE_UP:
144 			{
145 				BRect bounds = Bounds();
146 				ScrollTo(0, fmaxf(0, bounds.top - bounds.Height()));
147 				break;
148 			}
149 			case B_PAGE_DOWN:
150 			{
151 				BRect bounds = Bounds();
152 				float height = fPackages.size() * HEIGHT_PACKAGE;
153 				float maxScrollY = height - bounds.Height();
154 				float pageDownScrollY = bounds.top + bounds.Height();
155 				ScrollTo(0, fminf(maxScrollY, pageDownScrollY));
156 				break;
157 			}
158 			default:
159 				BView::KeyDown(bytes, numBytes);
160 				break;
161 		}
162 	}
163 
164 
165 	/*!	This method will send a message to the Window so that it can signal
166 		back to this and other views that a package has been selected.  This
167 		method won't actually change the state of this view directly.
168 	*/
169 
170 	void _MessageSelectIndex(int32 index) const
171 	{
172 		if (index != -1) {
173 			BMessage message(MSG_PACKAGE_SELECTED);
174 			BString packageName = fPackages[index]->Name();
175 			message.AddString("name", packageName);
176 			Window()->PostMessage(&message);
177 		}
178 	}
179 
180 
181 	virtual void FrameResized(float width, float height)
182 	{
183 		BView::FrameResized(width, height);
184 
185 		// because the summary text will wrap, a resize of the frame will
186 		// result in all of the summary area needing to be redrawn.
187 
188 		BRect rectToInvalidate = Bounds();
189 		rectToInvalidate.left = X_POSITION_SUMMARY;
190 		Invalidate(rectToInvalidate);
191 	}
192 
193 
194 // #pragma mark - update / add / remove / clear data
195 
196 
197 	void UpdatePackage(uint32 changeMask, const PackageInfoRef& package)
198 	{
199 		// TODO; could optimize the invalidation?
200 		int32 index = _IndexOfPackage(package);
201 		if (index >= 0) {
202 			fPackages[index] = package;
203 			Invalidate(_RectOfIndex(index));
204 		}
205 	}
206 
207 
208 	void Clear()
209 	{
210 		for (std::vector<PackageInfoRef>::iterator it = fPackages.begin();
211 				it != fPackages.end(); it++) {
212 			(*it)->RemoveListener(fPackageListener);
213 		}
214 		fPackages.clear();
215 		fSelectedIndex = -1;
216 		Invalidate();
217 	}
218 
219 
220 	bool IsEmpty() const
221 	{
222 		return fPackages.size() == 0;
223 	}
224 
225 
226 	void _HandleUpdatePackage(const BString& name)
227 	{
228 		int32 index = _IndexOfName(name);
229 		if (index != -1)
230 			Invalidate(_RectOfIndex(index));
231 	}
232 
233 
234 	static int _CmpProminences(int64 a, int64 b)
235 	{
236 		if (a <= 0)
237 			a = PROMINANCE_ORDERING_MAX;
238 		if (b <= 0)
239 			b = PROMINANCE_ORDERING_MAX;
240 		if (a == b)
241 			return 0;
242 		if (a > b)
243 			return 1;
244 		return -1;
245 	}
246 
247 
248 	/*! This method will return true if the packageA is ordered before
249 		packageB.
250 	*/
251 
252 	static bool _IsPackageBefore(const PackageInfoRef& packageA,
253 		const PackageInfoRef& packageB)
254 	{
255 		if (!packageA.IsSet() || !packageB.IsSet())
256 			HDFATAL("unexpected NULL reference in a referencable");
257 		int c = _CmpProminences(packageA->Prominence(), packageB->Prominence());
258 		if (c == 0)
259 			c = packageA->Title().ICompare(packageB->Title());
260 		if (c == 0)
261 			c = packageA->Name().Compare(packageB->Name());
262 		return c < 0;
263 	}
264 
265 
266 	void BeginAddRemove()
267 	{
268 		fLowestIndexAddedOrRemoved = INT32_MAX;
269 	}
270 
271 
272 	void EndAddRemove()
273 	{
274 		if (fLowestIndexAddedOrRemoved < INT32_MAX) {
275 			if (fPackages.empty())
276 				Invalidate();
277 			else {
278 				BRect invalidRect = Bounds();
279 				invalidRect.top = _YOfIndex(fLowestIndexAddedOrRemoved);
280 				Invalidate(invalidRect);
281 			}
282 		}
283 	}
284 
285 
286 	void AddPackage(const PackageInfoRef& package)
287 	{
288 		// fPackages is sorted and for this reason it is possible to find the
289 		// insertion point by identifying the first item in fPackages that does
290 		// not return true from the method '_IsPackageBefore'.
291 
292 		std::vector<PackageInfoRef>::iterator itInsertionPt
293 			= std::lower_bound(fPackages.begin(), fPackages.end(), package,
294 				&_IsPackageBefore);
295 
296 		if (itInsertionPt == fPackages.end()
297 				|| package->Name() != (*itInsertionPt)->Name()) {
298 			int32 insertionIndex =
299 				std::distance<std::vector<PackageInfoRef>::const_iterator>(
300 					fPackages.begin(), itInsertionPt);
301 			if (fSelectedIndex >= insertionIndex)
302 				fSelectedIndex++;
303 			fPackages.insert(itInsertionPt, package);
304 			package->AddListener(fPackageListener);
305 			if (insertionIndex < fLowestIndexAddedOrRemoved)
306 				fLowestIndexAddedOrRemoved = insertionIndex;
307 		}
308 	}
309 
310 
311 	void RemovePackage(const PackageInfoRef& package)
312 	{
313 		int32 index = _IndexOfPackage(package);
314 		if (index >= 0) {
315 			if (fSelectedIndex == index)
316 				fSelectedIndex = -1;
317 			if (fSelectedIndex > index)
318 				fSelectedIndex--;
319 			fPackages[index]->RemoveListener(fPackageListener);
320 			fPackages.erase(fPackages.begin() + index);
321 			if (index < fLowestIndexAddedOrRemoved)
322 				fLowestIndexAddedOrRemoved = index;
323 		}
324 	}
325 
326 
327 // #pragma mark - selection and index handling
328 
329 
330 	void SelectPackage(const PackageInfoRef& package)
331 	{
332 		_SelectIndex(_IndexOfPackage(package));
333 	}
334 
335 
336 	void _SelectIndex(int32 index)
337 	{
338 		if (index != fSelectedIndex) {
339 			int32 previousSelectedIndex = fSelectedIndex;
340 			fSelectedIndex = index;
341 			if (fSelectedIndex >= 0)
342 				Invalidate(_RectOfIndex(fSelectedIndex));
343 			if (previousSelectedIndex >= 0)
344 				Invalidate(_RectOfIndex(previousSelectedIndex));
345 			_EnsureIndexVisible(index);
346 		}
347 	}
348 
349 
350 	int32 _IndexOfPackage(PackageInfoRef package) const
351 	{
352 		std::vector<PackageInfoRef>::const_iterator it
353 			= std::lower_bound(fPackages.begin(), fPackages.end(), package,
354 				&_IsPackageBefore);
355 
356 		return (it == fPackages.end() || (*it)->Name() != package->Name())
357 			? -1 : it - fPackages.begin();
358 	}
359 
360 
361 	int32 _IndexOfName(const BString& name) const
362 	{
363 		// TODO; slow linear search.
364 		// the fPackages is not sorted on name and for this reason it is not
365 		// possible to do a binary search.
366 		for (uint32 i = 0; i < fPackages.size(); i++) {
367 			if (fPackages[i]->Name() == name)
368 				return i;
369 		}
370 		return -1;
371 	}
372 
373 
374 // #pragma mark - drawing and rendering
375 
376 
377 	virtual void Draw(BRect updateRect)
378 	{
379 		SetHighUIColor(B_LIST_BACKGROUND_COLOR);
380 		FillRect(updateRect);
381 
382 		int32 iStart = _IndexRoundedOfY(updateRect.top);
383 
384 		if (iStart != -1) {
385 			int32 iEnd = _IndexRoundedOfY(updateRect.bottom);
386 			for (int32 i = iStart; i <= iEnd; i++)
387 				_DrawPackageAtIndex(updateRect, i);
388 		}
389 	}
390 
391 
392 	void _DrawPackageAtIndex(BRect updateRect, int32 index)
393 	{
394 		_DrawPackage(updateRect, fPackages[index], index, _YOfIndex(index),
395 			index == fSelectedIndex);
396 	}
397 
398 
399 	void _DrawPackage(BRect updateRect, PackageInfoRef pkg, int index, float y,
400 		bool selected)
401 	{
402 		if (selected) {
403 			SetLowUIColor(B_LIST_SELECTED_BACKGROUND_COLOR);
404 			FillRect(_RectOfY(y), B_SOLID_LOW);
405 		} else {
406 			SetLowUIColor(B_LIST_BACKGROUND_COLOR);
407 		}
408 		// TODO; optimization; the updateRect may only cover some of this?
409 		_DrawPackageIcon(updateRect, pkg, y, selected);
410 		_DrawPackageTitle(updateRect, pkg, y, selected);
411 		_DrawPackagePublisher(updateRect, pkg, y, selected);
412 		_DrawPackageCronologicalInfo(updateRect, pkg, y, selected);
413 		_DrawPackageRating(updateRect, pkg, y, selected);
414 		_DrawPackageSummary(updateRect, pkg, y, selected);
415 	}
416 
417 
418 	void _DrawPackageIcon(BRect updateRect, PackageInfoRef pkg, float y,
419 		bool selected)
420 	{
421 		BitmapRef icon;
422 		status_t iconResult = fModel.GetPackageIconRepository().GetIcon(
423 			pkg->Name(), BITMAP_SIZE_64, icon);
424 
425 		if (iconResult == B_OK) {
426 			if (icon.IsSet()) {
427 				float inset = (HEIGHT_PACKAGE - SIZE_ICON) / 2.0;
428 				BRect targetRect = BRect(inset, y + inset, SIZE_ICON + inset,
429 					y + SIZE_ICON + inset);
430 				const BBitmap* bitmap = icon->Bitmap(BITMAP_SIZE_64);
431 				SetDrawingMode(B_OP_ALPHA);
432 				DrawBitmap(bitmap, bitmap->Bounds(), targetRect,
433 					B_FILTER_BITMAP_BILINEAR);
434 			}
435 		}
436 	}
437 
438 
439 	void _DrawPackageTitle(BRect updateRect, PackageInfoRef pkg, float y,
440 		bool selected)
441 	{
442 		static BFont* sFont = NULL;
443 
444 		if (sFont == NULL) {
445 			sFont = new BFont(be_plain_font);
446 			GetFont(sFont);
447   			font_family family;
448 			font_style style;
449 			sFont->SetSize(ceilf(sFont->Size() * 1.8f));
450 			sFont->GetFamilyAndStyle(&family, &style);
451 			sFont->SetFamilyAndStyle(family, "Bold");
452 		}
453 
454 		SetDrawingMode(B_OP_COPY);
455 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
456 			: B_LIST_ITEM_TEXT_COLOR);
457 		SetFont(sFont);
458 		BPoint pt(HEIGHT_PACKAGE, y + (HEIGHT_PACKAGE * Y_PROPORTION_TITLE));
459 		DrawString(pkg->Title(), pt);
460 
461 		if (pkg->State() == ACTIVATED) {
462 			const BBitmap* bitmap = sInstalledIcon->Bitmap(
463 				BITMAP_SIZE_16);
464 			float stringWidth = StringWidth(pkg->Title());
465 			float offsetX = pt.x + stringWidth + PADDING;
466 			BRect targetRect(offsetX, pt.y - 16, offsetX + 16, pt.y);
467 			SetDrawingMode(B_OP_ALPHA);
468 			DrawBitmap(bitmap, bitmap->Bounds(), targetRect,
469 				B_FILTER_BITMAP_BILINEAR);
470 		}
471 	}
472 
473 
474 	void _DrawPackageGenericTextSlug(BRect updateRect, PackageInfoRef pkg,
475 		const BString& text, float y, float yProportion, bool selected)
476 	{
477 		static BFont* sFont = NULL;
478 
479 		if (sFont == NULL) {
480 			sFont = new BFont(be_plain_font);
481 			font_family family;
482 			font_style style;
483 			sFont->SetSize(std::max(9.0f, floorf(sFont->Size() * 0.92f)));
484 			sFont->GetFamilyAndStyle(&family, &style);
485 			sFont->SetFamilyAndStyle(family, "Italic");
486 		}
487 
488 		SetDrawingMode(B_OP_COPY);
489 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
490 			: B_LIST_ITEM_TEXT_COLOR);
491 		SetFont(sFont);
492 
493 		float maxTextWidth = (X_POSITION_RATING - HEIGHT_PACKAGE) - PADDING;
494 		BString renderedText(text);
495 		TruncateString(&renderedText, B_TRUNCATE_END, maxTextWidth);
496 
497 		DrawString(renderedText, BPoint(HEIGHT_PACKAGE,
498 			y + (HEIGHT_PACKAGE * yProportion)));
499 	}
500 
501 
502 	void _DrawPackagePublisher(BRect updateRect, PackageInfoRef pkg, float y,
503 		bool selected)
504 	{
505 		_DrawPackageGenericTextSlug(updateRect, pkg, pkg->Publisher().Name(), y,
506 			Y_PROPORTION_PUBLISHER, selected);
507 	}
508 
509 
510 	void _DrawPackageCronologicalInfo(BRect updateRect, PackageInfoRef pkg,
511 		float y, bool selected)
512 	{
513 		BString versionCreateTimestampPresentation
514 			= B_TRANSLATE("%VersionCreateDate%");
515 		versionCreateTimestampPresentation.ReplaceAll("%VersionCreateDate%",
516 			LocaleUtils::TimestampToDateString(
517 				pkg->VersionCreateTimestamp()));
518 		_DrawPackageGenericTextSlug(updateRect, pkg,
519 			versionCreateTimestampPresentation, y,
520 			Y_PROPORTION_CHRONOLOGICAL_DATA, selected);
521 	}
522 
523 
524 	// TODO; show the sample size
525 	void _DrawPackageRating(BRect updateRect, PackageInfoRef pkg, float y,
526 		bool selected)
527 	{
528 		BPoint at(X_POSITION_RATING,
529 			y + (HEIGHT_PACKAGE - SIZE_RATING_STAR) / 2.0f);
530 		RatingUtils::Draw(this, at,
531 			pkg->CalculateRatingSummary().averageRating);
532 	}
533 
534 
535 	// TODO; handle multi-line rendering of the text
536 	void _DrawPackageSummary(BRect updateRect, PackageInfoRef pkg, float y,
537 		bool selected)
538 	{
539 		BRect bounds = Bounds();
540 
541 		SetDrawingMode(B_OP_COPY);
542 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
543 			: B_LIST_ITEM_TEXT_COLOR);
544 		SetFont(be_plain_font);
545 
546 		float maxTextWidth = bounds.Width() - X_POSITION_SUMMARY - PADDING;
547 		BString summary(pkg->ShortDescription());
548 		TruncateString(&summary, B_TRUNCATE_END, maxTextWidth);
549 
550 		DrawString(summary, BPoint(X_POSITION_SUMMARY,
551 			y + (HEIGHT_PACKAGE * 0.5)));
552 	}
553 
554 
555 // #pragma mark - geometry and scrolling
556 
557 
558 	/*!	This method will make sure that the package at the given index is
559 		visible.  If the whole of the package can be seen already then it will
560 		do nothing.  If the package is located above the visible region then it
561 		will scroll up to it.  If the package is located below the visible
562 		region then it will scroll down to it.
563 	*/
564 
565 	void _EnsureIndexVisible(int32 index)
566 	{
567 		if (!_IsIndexEntirelyVisible(index)) {
568 			BRect bounds = Bounds();
569 			int32 indexOfCentreVisible = _IndexOfY(
570 				bounds.top + bounds.Height() / 2);
571 			if (index < indexOfCentreVisible)
572 				ScrollTo(0, _YOfIndex(index));
573 			else {
574 				float scrollPointY = (_YOfIndex(index) + HEIGHT_PACKAGE)
575 					- bounds.Height();
576 				ScrollTo(0, scrollPointY);
577 			}
578 		}
579 	}
580 
581 
582 	/*!	This method will return true if the package at the supplied index is
583 		entirely visible.
584 	*/
585 
586 	bool _IsIndexEntirelyVisible(int32 index)
587 	{
588 		BRect bounds = Bounds();
589 		return bounds == (bounds | _RectOfIndex(index));
590 	}
591 
592 
593 	BRect _RectOfIndex(int32 index) const
594 	{
595 		if (index < 0)
596 			return BRect(0, 0, 0, 0);
597 		return _RectOfY(_YOfIndex(index));
598 	}
599 
600 
601 	/*!	Provides the top coordinate (offset from the top of view) of the package
602 		supplied.  If the package does not exist in the view then the coordinate
603 		returned will be B_SIZE_UNSET.
604 	*/
605 
606 	float TopOfPackage(const PackageInfoRef& package)
607 	{
608 		if (package.IsSet()) {
609 			int index = _IndexOfPackage(package);
610 			if (-1 != index)
611 				return _YOfIndex(index);
612 		}
613 		return B_SIZE_UNSET;
614 	}
615 
616 
617 	BRect _RectOfY(float y) const
618 	{
619 		return BRect(0, y, Bounds().Width(), y + HEIGHT_PACKAGE);
620 	}
621 
622 
623 	float _YOfIndex(int32 i) const
624 	{
625 		return i * HEIGHT_PACKAGE;
626 	}
627 
628 
629 	/*! Finds the offset into the list of packages for the y-coord in the view's
630 		coordinate space.  If the y is above or below the list of packages then
631 		this will return -1 to signal this.
632 	*/
633 
634 	int32 _IndexOfY(float y) const
635 	{
636 		if (fPackages.empty())
637 			return -1;
638 		int32 i = static_cast<int32>(y / HEIGHT_PACKAGE);
639 		if (i < 0 || i >= static_cast<int32>(fPackages.size()))
640 			return -1;
641 		return i;
642 	}
643 
644 
645 	/*! Find the offset into the list of packages for the y-coord in the view's
646 		coordinate space.  If the y is above or below the list of packages then
647 		this will return the first or last package index respectively.  If there
648 		are no packages then this will return -1;
649 	*/
650 
651 	int32 _IndexRoundedOfY(float y) const
652 	{
653 		if (fPackages.empty())
654 			return -1;
655 		int32 i = static_cast<int32>(y / HEIGHT_PACKAGE);
656 		if (i < 0)
657 			return 0;
658 		return std::min(i, (int32) (fPackages.size() - 1));
659 	}
660 
661 
662 	virtual BSize PreferredSize()
663 	{
664 		return BSize(B_SIZE_UNLIMITED, HEIGHT_PACKAGE * fPackages.size());
665 	}
666 
667 
668 private:
669 			Model&				fModel;
670 			std::vector<PackageInfoRef>
671 								fPackages;
672 			int32				fSelectedIndex;
673 			OnePackageMessagePackageListener*
674 								fPackageListener;
675 			int32				fLowestIndexAddedOrRemoved;
676 };
677 
678 
679 // #pragma mark - FeaturedPackagesView
680 
681 
682 FeaturedPackagesView::FeaturedPackagesView(Model& model)
683 	:
684 	BView(B_TRANSLATE("Featured packages"), 0),
685 	fModel(model)
686 {
687 	fPackagesView = new StackedFeaturedPackagesView(fModel);
688 
689 	fScrollView = new BScrollView("featured packages scroll view",
690 		fPackagesView, 0, false, true, B_FANCY_BORDER);
691 
692 	BLayoutBuilder::Group<>(this)
693 		.Add(fScrollView, 1.0f);
694 }
695 
696 
697 FeaturedPackagesView::~FeaturedPackagesView()
698 {
699 }
700 
701 
702 void
703 FeaturedPackagesView::BeginAddRemove()
704 {
705 	fPackagesView->BeginAddRemove();
706 }
707 
708 
709 void
710 FeaturedPackagesView::EndAddRemove()
711 {
712 	fPackagesView->EndAddRemove();
713 	_AdjustViews();
714 }
715 
716 
717 /*! This method will add the package into the list to be displayed.  The
718     insertion will occur in alphabetical order.
719 */
720 
721 void
722 FeaturedPackagesView::AddPackage(const PackageInfoRef& package)
723 {
724 	fPackagesView->AddPackage(package);
725 }
726 
727 
728 void
729 FeaturedPackagesView::RemovePackage(const PackageInfoRef& package)
730 {
731 	fPackagesView->RemovePackage(package);
732 }
733 
734 
735 void
736 FeaturedPackagesView::Clear()
737 {
738 	HDINFO("did clear the featured packages view");
739 	fPackagesView->Clear();
740 	_AdjustViews();
741 }
742 
743 
744 void
745 FeaturedPackagesView::SelectPackage(const PackageInfoRef& package,
746 	bool scrollToEntry)
747 {
748 	fPackagesView->SelectPackage(package);
749 
750 	if (scrollToEntry) {
751 		float offset = fPackagesView->TopOfPackage(package);
752 		if (offset != B_SIZE_UNSET)
753 			fPackagesView->ScrollTo(0, offset);
754 	}
755 }
756 
757 
758 void
759 FeaturedPackagesView::DoLayout()
760 {
761 	BView::DoLayout();
762 	_AdjustViews();
763 }
764 
765 
766 void
767 FeaturedPackagesView::_AdjustViews()
768 {
769 	fScrollView->FrameResized(fScrollView->Frame().Width(),
770 		fScrollView->Frame().Height());
771 }
772 
773 
774 void
775 FeaturedPackagesView::CleanupIcons()
776 {
777 	sInstalledIcon.Unset();
778 }
779