xref: /haiku/src/apps/haikudepot/ui/FeaturedPackagesView.cpp (revision a127b88ecbfab58f64944c98aa47722a18e363b2)
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 
432 				if (bitmap != NULL && bitmap->IsValid()) {
433 					SetDrawingMode(B_OP_ALPHA);
434 					DrawBitmap(bitmap, bitmap->Bounds(), targetRect,
435 						B_FILTER_BITMAP_BILINEAR);
436 				}
437 			}
438 		}
439 	}
440 
441 
442 	void _DrawPackageTitle(BRect updateRect, PackageInfoRef pkg, float y,
443 		bool selected)
444 	{
445 		static BFont* sFont = NULL;
446 
447 		if (sFont == NULL) {
448 			sFont = new BFont(be_plain_font);
449 			GetFont(sFont);
450   			font_family family;
451 			font_style style;
452 			sFont->SetSize(ceilf(sFont->Size() * 1.8f));
453 			sFont->GetFamilyAndStyle(&family, &style);
454 			sFont->SetFamilyAndStyle(family, "Bold");
455 		}
456 
457 		SetDrawingMode(B_OP_COPY);
458 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
459 			: B_LIST_ITEM_TEXT_COLOR);
460 		SetFont(sFont);
461 		BPoint pt(HEIGHT_PACKAGE, y + (HEIGHT_PACKAGE * Y_PROPORTION_TITLE));
462 		DrawString(pkg->Title(), pt);
463 
464 		if (pkg->State() == ACTIVATED) {
465 			const BBitmap* bitmap = sInstalledIcon->Bitmap(
466 				BITMAP_SIZE_16);
467 			if (bitmap != NULL && bitmap->IsValid()) {
468 				float stringWidth = StringWidth(pkg->Title());
469 				float offsetX = pt.x + stringWidth + PADDING;
470 				BRect targetRect(offsetX, pt.y - 16,
471 					offsetX + 16, pt.y);
472 				SetDrawingMode(B_OP_ALPHA);
473 				DrawBitmap(bitmap, bitmap->Bounds(), targetRect,
474 					B_FILTER_BITMAP_BILINEAR);
475 			}
476 		}
477 	}
478 
479 
480 	void _DrawPackageGenericTextSlug(BRect updateRect, PackageInfoRef pkg,
481 		const BString& text, float y, float yProportion, bool selected)
482 	{
483 		static BFont* sFont = NULL;
484 
485 		if (sFont == NULL) {
486 			sFont = new BFont(be_plain_font);
487 			font_family family;
488 			font_style style;
489 			sFont->SetSize(std::max(9.0f, floorf(sFont->Size() * 0.92f)));
490 			sFont->GetFamilyAndStyle(&family, &style);
491 			sFont->SetFamilyAndStyle(family, "Italic");
492 		}
493 
494 		SetDrawingMode(B_OP_COPY);
495 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
496 			: B_LIST_ITEM_TEXT_COLOR);
497 		SetFont(sFont);
498 
499 		float maxTextWidth = (X_POSITION_RATING - HEIGHT_PACKAGE) - PADDING;
500 		BString renderedText(text);
501 		TruncateString(&renderedText, B_TRUNCATE_END, maxTextWidth);
502 
503 		DrawString(renderedText, BPoint(HEIGHT_PACKAGE,
504 			y + (HEIGHT_PACKAGE * yProportion)));
505 	}
506 
507 
508 	void _DrawPackagePublisher(BRect updateRect, PackageInfoRef pkg, float y,
509 		bool selected)
510 	{
511 		_DrawPackageGenericTextSlug(updateRect, pkg, pkg->Publisher().Name(), y,
512 			Y_PROPORTION_PUBLISHER, selected);
513 	}
514 
515 
516 	void _DrawPackageCronologicalInfo(BRect updateRect, PackageInfoRef pkg,
517 		float y, bool selected)
518 	{
519 		BString versionCreateTimestampPresentation
520 			= LocaleUtils::TimestampToDateString(pkg->VersionCreateTimestamp());
521 		_DrawPackageGenericTextSlug(updateRect, pkg,
522 			versionCreateTimestampPresentation, y,
523 			Y_PROPORTION_CHRONOLOGICAL_DATA, selected);
524 	}
525 
526 
527 	// TODO; show the sample size
528 	void _DrawPackageRating(BRect updateRect, PackageInfoRef pkg, float y,
529 		bool selected)
530 	{
531 		BPoint at(X_POSITION_RATING,
532 			y + (HEIGHT_PACKAGE - SIZE_RATING_STAR) / 2.0f);
533 		RatingUtils::Draw(this, at,
534 			pkg->CalculateRatingSummary().averageRating);
535 	}
536 
537 
538 	// TODO; handle multi-line rendering of the text
539 	void _DrawPackageSummary(BRect updateRect, PackageInfoRef pkg, float y,
540 		bool selected)
541 	{
542 		BRect bounds = Bounds();
543 
544 		SetDrawingMode(B_OP_COPY);
545 		SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR
546 			: B_LIST_ITEM_TEXT_COLOR);
547 		SetFont(be_plain_font);
548 
549 		float maxTextWidth = bounds.Width() - X_POSITION_SUMMARY - PADDING;
550 		BString summary(pkg->ShortDescription());
551 		TruncateString(&summary, B_TRUNCATE_END, maxTextWidth);
552 
553 		DrawString(summary, BPoint(X_POSITION_SUMMARY,
554 			y + (HEIGHT_PACKAGE * 0.5)));
555 	}
556 
557 
558 // #pragma mark - geometry and scrolling
559 
560 
561 	/*!	This method will make sure that the package at the given index is
562 		visible.  If the whole of the package can be seen already then it will
563 		do nothing.  If the package is located above the visible region then it
564 		will scroll up to it.  If the package is located below the visible
565 		region then it will scroll down to it.
566 	*/
567 
568 	void _EnsureIndexVisible(int32 index)
569 	{
570 		if (!_IsIndexEntirelyVisible(index)) {
571 			BRect bounds = Bounds();
572 			int32 indexOfCentreVisible = _IndexOfY(
573 				bounds.top + bounds.Height() / 2);
574 			if (index < indexOfCentreVisible)
575 				ScrollTo(0, _YOfIndex(index));
576 			else {
577 				float scrollPointY = (_YOfIndex(index) + HEIGHT_PACKAGE)
578 					- bounds.Height();
579 				ScrollTo(0, scrollPointY);
580 			}
581 		}
582 	}
583 
584 
585 	/*!	This method will return true if the package at the supplied index is
586 		entirely visible.
587 	*/
588 
589 	bool _IsIndexEntirelyVisible(int32 index)
590 	{
591 		BRect bounds = Bounds();
592 		return bounds == (bounds | _RectOfIndex(index));
593 	}
594 
595 
596 	BRect _RectOfIndex(int32 index) const
597 	{
598 		if (index < 0)
599 			return BRect(0, 0, 0, 0);
600 		return _RectOfY(_YOfIndex(index));
601 	}
602 
603 
604 	/*!	Provides the top coordinate (offset from the top of view) of the package
605 		supplied.  If the package does not exist in the view then the coordinate
606 		returned will be B_SIZE_UNSET.
607 	*/
608 
609 	float TopOfPackage(const PackageInfoRef& package)
610 	{
611 		if (package.IsSet()) {
612 			int index = _IndexOfPackage(package);
613 			if (-1 != index)
614 				return _YOfIndex(index);
615 		}
616 		return B_SIZE_UNSET;
617 	}
618 
619 
620 	BRect _RectOfY(float y) const
621 	{
622 		return BRect(0, y, Bounds().Width(), y + HEIGHT_PACKAGE);
623 	}
624 
625 
626 	float _YOfIndex(int32 i) const
627 	{
628 		return i * HEIGHT_PACKAGE;
629 	}
630 
631 
632 	/*! Finds the offset into the list of packages for the y-coord in the view's
633 		coordinate space.  If the y is above or below the list of packages then
634 		this will return -1 to signal this.
635 	*/
636 
637 	int32 _IndexOfY(float y) const
638 	{
639 		if (fPackages.empty())
640 			return -1;
641 		int32 i = static_cast<int32>(y / HEIGHT_PACKAGE);
642 		if (i < 0 || i >= static_cast<int32>(fPackages.size()))
643 			return -1;
644 		return i;
645 	}
646 
647 
648 	/*! Find the offset into the list of packages for the y-coord in the view's
649 		coordinate space.  If the y is above or below the list of packages then
650 		this will return the first or last package index respectively.  If there
651 		are no packages then this will return -1;
652 	*/
653 
654 	int32 _IndexRoundedOfY(float y) const
655 	{
656 		if (fPackages.empty())
657 			return -1;
658 		int32 i = static_cast<int32>(y / HEIGHT_PACKAGE);
659 		if (i < 0)
660 			return 0;
661 		return std::min(i, (int32) (fPackages.size() - 1));
662 	}
663 
664 
665 	virtual BSize PreferredSize()
666 	{
667 		return BSize(B_SIZE_UNLIMITED, HEIGHT_PACKAGE * fPackages.size());
668 	}
669 
670 
671 private:
672 			Model&				fModel;
673 			std::vector<PackageInfoRef>
674 								fPackages;
675 			int32				fSelectedIndex;
676 			OnePackageMessagePackageListener*
677 								fPackageListener;
678 			int32				fLowestIndexAddedOrRemoved;
679 };
680 
681 
682 // #pragma mark - FeaturedPackagesView
683 
684 
685 FeaturedPackagesView::FeaturedPackagesView(Model& model)
686 	:
687 	BView(B_TRANSLATE("Featured packages"), 0),
688 	fModel(model)
689 {
690 	fPackagesView = new StackedFeaturedPackagesView(fModel);
691 
692 	fScrollView = new BScrollView("featured packages scroll view",
693 		fPackagesView, 0, false, true, B_FANCY_BORDER);
694 
695 	BLayoutBuilder::Group<>(this)
696 		.Add(fScrollView, 1.0f);
697 }
698 
699 
700 FeaturedPackagesView::~FeaturedPackagesView()
701 {
702 }
703 
704 
705 void
706 FeaturedPackagesView::BeginAddRemove()
707 {
708 	fPackagesView->BeginAddRemove();
709 }
710 
711 
712 void
713 FeaturedPackagesView::EndAddRemove()
714 {
715 	fPackagesView->EndAddRemove();
716 	_AdjustViews();
717 }
718 
719 
720 /*! This method will add the package into the list to be displayed.  The
721     insertion will occur in alphabetical order.
722 */
723 
724 void
725 FeaturedPackagesView::AddPackage(const PackageInfoRef& package)
726 {
727 	fPackagesView->AddPackage(package);
728 }
729 
730 
731 void
732 FeaturedPackagesView::RemovePackage(const PackageInfoRef& package)
733 {
734 	fPackagesView->RemovePackage(package);
735 }
736 
737 
738 void
739 FeaturedPackagesView::Clear()
740 {
741 	HDINFO("did clear the featured packages view");
742 	fPackagesView->Clear();
743 	_AdjustViews();
744 }
745 
746 
747 void
748 FeaturedPackagesView::SelectPackage(const PackageInfoRef& package,
749 	bool scrollToEntry)
750 {
751 	fPackagesView->SelectPackage(package);
752 
753 	if (scrollToEntry) {
754 		float offset = fPackagesView->TopOfPackage(package);
755 		if (offset != B_SIZE_UNSET)
756 			fPackagesView->ScrollTo(0, offset);
757 	}
758 }
759 
760 
761 void
762 FeaturedPackagesView::DoLayout()
763 {
764 	BView::DoLayout();
765 	_AdjustViews();
766 }
767 
768 
769 void
770 FeaturedPackagesView::_AdjustViews()
771 {
772 	fScrollView->FrameResized(fScrollView->Frame().Width(),
773 		fScrollView->Frame().Height());
774 }
775 
776 
777 void
778 FeaturedPackagesView::CleanupIcons()
779 {
780 	sInstalledIcon.Unset();
781 }
782