repository_infos: Enable automatic updates on the main Haiku repostiory.
[haiku.git] / src / apps / haikudepot / ui / PackageListView.cpp
blob6de9218ba6ccda8ef5480b0394ae1c44c24b8006
1 /*
2 * Copyright 2017, Julian Harnath, <julian.harnath@rwth-aachen.de>.
3 * Copyright 2015, Axel Dörfler, <axeld@pinc-software.de>.
4 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>.
5 * Copyright 2013, Rene Gollent, <rene@gollent.com>.
6 * All rights reserved. Distributed under the terms of the MIT License.
7 */
9 #include "PackageListView.h"
11 #include <algorithm>
12 #include <stdio.h>
14 #include <Autolock.h>
15 #include <Catalog.h>
16 #include <ControlLook.h>
17 #include <MessageFormat.h>
18 #include <ScrollBar.h>
19 #include <StringForSize.h>
20 #include <package/hpkg/Strings.h>
21 #include <Window.h>
23 #include "MainWindow.h"
24 #include "WorkStatusView.h"
27 #undef B_TRANSLATION_CONTEXT
28 #define B_TRANSLATION_CONTEXT "PackageListView"
31 static const char* skPackageStateAvailable = B_TRANSLATE_MARK("Available");
32 static const char* skPackageStateUninstalled = B_TRANSLATE_MARK("Uninstalled");
33 static const char* skPackageStateActive = B_TRANSLATE_MARK("Active");
34 static const char* skPackageStateInactive = B_TRANSLATE_MARK("Inactive");
35 static const char* skPackageStatePending = B_TRANSLATE_MARK(
36 "Pending" B_UTF8_ELLIPSIS);
39 inline BString
40 package_state_to_string(PackageInfoRef ref)
42 switch (ref->State()) {
43 case NONE:
44 return B_TRANSLATE(skPackageStateAvailable);
45 case INSTALLED:
46 return B_TRANSLATE(skPackageStateInactive);
47 case ACTIVATED:
48 return B_TRANSLATE(skPackageStateActive);
49 case UNINSTALLED:
50 return B_TRANSLATE(skPackageStateUninstalled);
51 case DOWNLOADING:
53 BString data;
54 data.SetToFormat("%3.2f%%", ref->DownloadProgress() * 100.0);
55 return data;
57 case PENDING:
58 return B_TRANSLATE(skPackageStatePending);
61 return B_TRANSLATE("Unknown");
65 // A field type displaying both a bitmap and a string so that the
66 // tree display looks nicer (both text and bitmap are indented)
67 class SharedBitmapStringField : public BStringField {
68 typedef BStringField Inherited;
69 public:
70 SharedBitmapStringField(SharedBitmap* bitmap,
71 SharedBitmap::Size size,
72 const char* string);
73 virtual ~SharedBitmapStringField();
75 void SetBitmap(SharedBitmap* bitmap,
76 SharedBitmap::Size size);
77 const BBitmap* Bitmap() const
78 { return fBitmap; }
80 private:
81 BitmapRef fReference;
82 const BBitmap* fBitmap;
86 class RatingField : public BField {
87 public:
88 RatingField(float rating);
89 virtual ~RatingField();
91 void SetRating(float rating);
92 float Rating() const
93 { return fRating; }
94 private:
95 float fRating;
99 class SizeField : public BStringField {
100 public:
101 SizeField(double size);
102 virtual ~SizeField();
104 void SetSize(double size);
105 double Size() const
106 { return fSize; }
107 private:
108 double fSize;
112 // BColumn for PackageListView which knows how to render
113 // a SharedBitmapStringField
114 // TODO: Code-duplication with DriveSetup PartitionList.h
115 class PackageColumn : public BTitledColumn {
116 typedef BTitledColumn Inherited;
117 public:
118 PackageColumn(const char* title,
119 float width, float minWidth,
120 float maxWidth, uint32 truncateMode,
121 alignment align = B_ALIGN_LEFT);
123 virtual void DrawField(BField* field, BRect rect,
124 BView* parent);
125 virtual int CompareFields(BField* field1, BField* field2);
126 virtual float GetPreferredWidth(BField* field,
127 BView* parent) const;
129 virtual bool AcceptsField(const BField* field) const;
131 static void InitTextMargin(BView* parent);
133 private:
134 uint32 fTruncateMode;
135 static float sTextMargin;
139 // BRow for the PartitionListView
140 class PackageRow : public BRow {
141 typedef BRow Inherited;
142 public:
143 PackageRow(const PackageInfoRef& package,
144 PackageListener* listener);
145 virtual ~PackageRow();
147 const PackageInfoRef& Package() const
148 { return fPackage; }
150 void UpdateTitle();
151 void UpdateSummary();
152 void UpdateState();
153 void UpdateRating();
154 void UpdateSize();
155 void UpdateRepository();
157 PackageRow*& NextInHash()
158 { return fNextInHash; }
160 private:
161 PackageInfoRef fPackage;
162 PackageInfoListenerRef fPackageListener;
164 PackageRow* fNextInHash;
165 // link for BOpenHashTable
169 enum {
170 MSG_UPDATE_PACKAGE = 'updp'
174 class PackageListener : public PackageInfoListener {
175 public:
176 PackageListener(PackageListView* view)
178 fView(view)
182 virtual ~PackageListener()
186 virtual void PackageChanged(const PackageInfoEvent& event)
188 BMessenger messenger(fView);
189 if (!messenger.IsValid())
190 return;
192 const PackageInfo& package = *event.Package().Get();
194 BMessage message(MSG_UPDATE_PACKAGE);
195 message.AddString("name", package.Name());
196 message.AddUInt32("changes", event.Changes());
198 messenger.SendMessage(&message);
201 private:
202 PackageListView* fView;
206 // #pragma mark - SharedBitmapStringField
209 SharedBitmapStringField::SharedBitmapStringField(SharedBitmap* bitmap,
210 SharedBitmap::Size size, const char* string)
212 Inherited(string),
213 fBitmap(NULL)
215 SetBitmap(bitmap, size);
219 SharedBitmapStringField::~SharedBitmapStringField()
224 void
225 SharedBitmapStringField::SetBitmap(SharedBitmap* bitmap,
226 SharedBitmap::Size size)
228 fReference = bitmap;
229 fBitmap = bitmap != NULL ? bitmap->Bitmap(size) : NULL;
230 // TODO: cause a redraw?
234 // #pragma mark - RatingField
237 RatingField::RatingField(float rating)
239 fRating(0.0f)
241 SetRating(rating);
245 RatingField::~RatingField()
250 void
251 RatingField::SetRating(float rating)
253 if (rating < 0.0f)
254 rating = 0.0f;
255 if (rating > 5.0f)
256 rating = 5.0f;
258 if (rating == fRating)
259 return;
261 fRating = rating;
265 // #pragma mark - SizeField
268 SizeField::SizeField(double size)
270 BStringField(""),
271 fSize(-1.0)
273 SetSize(size);
277 SizeField::~SizeField()
282 void
283 SizeField::SetSize(double size)
285 if (size < 0.0)
286 size = 0.0;
288 if (size == fSize)
289 return;
291 BString sizeString;
292 if (size == 0) {
293 sizeString = B_TRANSLATE_CONTEXT("-", "no package size");
294 } else {
295 char buffer[256];
296 sizeString = string_for_size(size, buffer, sizeof(buffer));
299 fSize = size;
300 SetString(sizeString.String());
304 // #pragma mark - PackageColumn
307 // TODO: Code-duplication with DriveSetup PartitionList.cpp
310 float PackageColumn::sTextMargin = 0.0;
313 PackageColumn::PackageColumn(const char* title, float width, float minWidth,
314 float maxWidth, uint32 truncateMode, alignment align)
316 Inherited(title, width, minWidth, maxWidth, align),
317 fTruncateMode(truncateMode)
319 SetWantsEvents(true);
323 void
324 PackageColumn::DrawField(BField* field, BRect rect, BView* parent)
326 SharedBitmapStringField* bitmapField
327 = dynamic_cast<SharedBitmapStringField*>(field);
328 BStringField* stringField = dynamic_cast<BStringField*>(field);
329 RatingField* ratingField = dynamic_cast<RatingField*>(field);
331 if (bitmapField != NULL) {
332 const BBitmap* bitmap = bitmapField->Bitmap();
334 // figure out the placement
335 float x = 0.0;
336 BRect r = bitmap ? bitmap->Bounds() : BRect(0, 0, 15, 15);
337 float y = rect.top + ((rect.Height() - r.Height()) / 2);
338 float width = 0.0;
340 switch (Alignment()) {
341 default:
342 case B_ALIGN_LEFT:
343 case B_ALIGN_CENTER:
344 x = rect.left + sTextMargin;
345 width = rect.right - (x + r.Width()) - (2 * sTextMargin);
346 r.Set(x + r.Width(), rect.top, rect.right - width, rect.bottom);
347 break;
349 case B_ALIGN_RIGHT:
350 x = rect.right - sTextMargin - r.Width();
351 width = (x - rect.left - (2 * sTextMargin));
352 r.Set(rect.left, rect.top, rect.left + width, rect.bottom);
353 break;
356 if (width != bitmapField->Width()) {
357 BString truncatedString(bitmapField->String());
358 parent->TruncateString(&truncatedString, fTruncateMode, width + 2);
359 bitmapField->SetClippedString(truncatedString.String());
360 bitmapField->SetWidth(width);
363 // draw the bitmap
364 if (bitmap != NULL) {
365 parent->SetDrawingMode(B_OP_ALPHA);
366 parent->DrawBitmap(bitmap, BPoint(x, y));
367 parent->SetDrawingMode(B_OP_OVER);
370 // draw the string
371 DrawString(bitmapField->ClippedString(), parent, r);
373 } else if (stringField != NULL) {
375 float width = rect.Width() - (2 * sTextMargin);
377 if (width != stringField->Width()) {
378 BString truncatedString(stringField->String());
380 parent->TruncateString(&truncatedString, fTruncateMode, width + 2);
381 stringField->SetClippedString(truncatedString.String());
382 stringField->SetWidth(width);
385 DrawString(stringField->ClippedString(), parent, rect);
387 } else if (ratingField != NULL) {
389 const float kDefaultTextMargin = 8;
391 float width = rect.Width() - (2 * kDefaultTextMargin);
393 BString string = "★★★★★";
394 float stringWidth = parent->StringWidth(string);
395 bool drawOverlay = true;
397 if (width < stringWidth) {
398 string.SetToFormat("%.1f", ratingField->Rating());
399 drawOverlay = false;
400 stringWidth = parent->StringWidth(string);
403 switch (Alignment()) {
404 default:
405 case B_ALIGN_LEFT:
406 rect.left += kDefaultTextMargin;
407 break;
408 case B_ALIGN_CENTER:
409 rect.left = rect.left + (width - stringWidth) / 2.0f;
410 break;
412 case B_ALIGN_RIGHT:
413 rect.left = rect.right - (stringWidth + kDefaultTextMargin);
414 break;
417 rect.left = floorf(rect.left);
418 rect.right = rect.left + stringWidth;
420 if (drawOverlay)
421 parent->SetHighColor(0, 170, 255);
423 font_height fontHeight;
424 parent->GetFontHeight(&fontHeight);
425 float y = rect.top + (rect.Height()
426 - (fontHeight.ascent + fontHeight.descent)) / 2
427 + fontHeight.ascent;
429 parent->DrawString(string, BPoint(rect.left, y));
431 if (drawOverlay) {
432 rect.left = ceilf(rect.left
433 + (ratingField->Rating() / 5.0f) * rect.Width());
435 rgb_color color = parent->LowColor();
436 color.alpha = 190;
437 parent->SetHighColor(color);
439 parent->SetDrawingMode(B_OP_ALPHA);
440 parent->FillRect(rect, B_SOLID_HIGH);
448 PackageColumn::CompareFields(BField* field1, BField* field2)
450 SizeField* sizeField1 = dynamic_cast<SizeField*>(field1);
451 SizeField* sizeField2 = dynamic_cast<SizeField*>(field2);
452 if (sizeField1 != NULL && sizeField2 != NULL) {
453 if (sizeField1->Size() > sizeField2->Size())
454 return -1;
455 else if (sizeField1->Size() < sizeField2->Size())
456 return 1;
457 return 0;
460 BStringField* stringField1 = dynamic_cast<BStringField*>(field1);
461 BStringField* stringField2 = dynamic_cast<BStringField*>(field2);
462 if (stringField1 != NULL && stringField2 != NULL) {
463 // TODO: Locale aware string compare... not too important if
464 // package names are not translated.
465 return strcasecmp(stringField1->String(), stringField2->String());
468 RatingField* ratingField1 = dynamic_cast<RatingField*>(field1);
469 RatingField* ratingField2 = dynamic_cast<RatingField*>(field2);
470 if (ratingField1 != NULL && ratingField2 != NULL) {
471 if (ratingField1->Rating() > ratingField2->Rating())
472 return -1;
473 else if (ratingField1->Rating() < ratingField2->Rating())
474 return 1;
475 return 0;
478 return Inherited::CompareFields(field1, field2);
482 float
483 PackageColumn::GetPreferredWidth(BField *_field, BView* parent) const
485 SharedBitmapStringField* bitmapField
486 = dynamic_cast<SharedBitmapStringField*>(_field);
487 BStringField* stringField = dynamic_cast<BStringField*>(_field);
489 float parentWidth = Inherited::GetPreferredWidth(_field, parent);
490 float width = 0.0;
492 if (bitmapField) {
493 const BBitmap* bitmap = bitmapField->Bitmap();
494 BFont font;
495 parent->GetFont(&font);
496 width = font.StringWidth(bitmapField->String()) + 3 * sTextMargin;
497 if (bitmap)
498 width += bitmap->Bounds().Width();
499 else
500 width += 16;
501 } else if (stringField) {
502 BFont font;
503 parent->GetFont(&font);
504 width = font.StringWidth(stringField->String()) + 2 * sTextMargin;
506 return max_c(width, parentWidth);
510 bool
511 PackageColumn::AcceptsField(const BField* field) const
513 return dynamic_cast<const BStringField*>(field) != NULL
514 || dynamic_cast<const RatingField*>(field) != NULL;
518 void
519 PackageColumn::InitTextMargin(BView* parent)
521 BFont font;
522 parent->GetFont(&font);
523 sTextMargin = ceilf(font.Size() * 0.8);
527 // #pragma mark - PackageRow
530 enum {
531 kTitleColumn,
532 kRatingColumn,
533 kDescriptionColumn,
534 kSizeColumn,
535 kStatusColumn,
536 kRepositoryColumn
540 PackageRow::PackageRow(const PackageInfoRef& packageRef,
541 PackageListener* packageListener)
543 Inherited(ceilf(be_plain_font->Size() * 1.8f)),
544 fPackage(packageRef),
545 fPackageListener(packageListener),
546 fNextInHash(NULL)
548 if (packageRef.Get() == NULL)
549 return;
551 PackageInfo& package = *packageRef.Get();
553 // Package icon and title
554 // NOTE: The icon BBitmap is referenced by the fPackage member.
555 UpdateTitle();
557 // Rating
558 UpdateRating();
560 // Summary
561 UpdateSummary();
563 // Size
564 UpdateSize();
566 // Status
567 UpdateState();
569 // Repository
570 UpdateRepository();
572 package.AddListener(fPackageListener);
576 PackageRow::~PackageRow()
578 if (fPackage.Get() != NULL)
579 fPackage->RemoveListener(fPackageListener);
583 void
584 PackageRow::UpdateTitle()
586 if (fPackage.Get() == NULL)
587 return;
589 SetField(new SharedBitmapStringField(fPackage->Icon(),
590 SharedBitmap::SIZE_16, fPackage->Title()), kTitleColumn);
594 void
595 PackageRow::UpdateState()
597 if (fPackage.Get() == NULL)
598 return;
600 SetField(new BStringField(package_state_to_string(fPackage)),
601 kStatusColumn);
605 void
606 PackageRow::UpdateSummary()
608 if (fPackage.Get() == NULL)
609 return;
611 SetField(new BStringField(fPackage->ShortDescription()),
612 kDescriptionColumn);
616 void
617 PackageRow::UpdateRating()
619 if (fPackage.Get() == NULL)
620 return;
621 RatingSummary summary = fPackage->CalculateRatingSummary();
622 SetField(new RatingField(summary.averageRating), kRatingColumn);
626 void
627 PackageRow::UpdateSize()
629 if (fPackage.Get() == NULL)
630 return;
632 SetField(new SizeField(fPackage->Size()), kSizeColumn);
636 void
637 PackageRow::UpdateRepository()
639 if (fPackage.Get() == NULL)
640 return;
642 SetField(new BStringField(fPackage->DepotName()), kRepositoryColumn);
646 // #pragma mark - ItemCountView
649 class PackageListView::ItemCountView : public BView {
650 public:
651 ItemCountView()
653 BView("item count view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
654 fItemCount(0)
656 BFont font(be_plain_font);
657 font.SetSize(9.0f);
658 SetFont(&font);
660 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
661 SetLowUIColor(ViewUIColor());
662 SetHighUIColor(LowUIColor(), B_DARKEN_4_TINT);
665 virtual BSize MinSize()
667 BString label(_GetLabel());
668 return BSize(StringWidth(label) + 10, B_H_SCROLL_BAR_HEIGHT);
671 virtual BSize PreferredSize()
673 return MinSize();
676 virtual BSize MaxSize()
678 return MinSize();
681 virtual void Draw(BRect updateRect)
683 FillRect(updateRect, B_SOLID_LOW);
685 BString label(_GetLabel());
687 font_height fontHeight;
688 GetFontHeight(&fontHeight);
690 BRect bounds(Bounds());
691 float width = StringWidth(label);
693 BPoint offset;
694 offset.x = bounds.left + (bounds.Width() - width) / 2.0f;
695 offset.y = bounds.top + (bounds.Height()
696 - (fontHeight.ascent + fontHeight.descent)) / 2.0f
697 + fontHeight.ascent;
699 DrawString(label, offset);
702 void SetItemCount(int32 count)
704 if (count == fItemCount)
705 return;
706 BSize minSize = MinSize();
707 fItemCount = count;
708 if (minSize != MinSize())
709 InvalidateLayout();
710 Invalidate();
713 private:
714 BString _GetLabel() const
716 static BMessageFormat format(B_TRANSLATE("{0, plural, "
717 "one{# item} other{# items}}"));
719 BString label;
720 format.Format(label, fItemCount);
721 return label;
724 int32 fItemCount;
728 // #pragma mark - PackageListView::RowByNameHashDefinition
731 struct PackageListView::RowByNameHashDefinition {
732 typedef const char* KeyType;
733 typedef PackageRow ValueType;
735 size_t HashKey(const char* key) const
737 return BPackageKit::BHPKG::BPrivate::hash_string(key);
740 size_t Hash(PackageRow* value) const
742 return BPackageKit::BHPKG::BPrivate::hash_string(
743 value->Package()->Name().String());
746 bool Compare(const char* key, PackageRow* value) const
748 return value->Package()->Name() == key;
751 ValueType*& GetLink(PackageRow* value) const
753 return value->NextInHash();
758 // #pragma mark - PackageListView
761 PackageListView::PackageListView(BLocker* modelLock)
763 BColumnListView("package list view", 0, B_FANCY_BORDER, true),
764 fModelLock(modelLock),
765 fPackageListener(new(std::nothrow) PackageListener(this)),
766 fRowByNameTable(new RowByNameTable()),
767 fWorkStatusView(NULL)
769 float scale = be_plain_font->Size() / 12.f;
770 float spacing = be_control_look->DefaultItemSpacing() * 2;
772 AddColumn(new PackageColumn(B_TRANSLATE("Name"), 150 * scale, 50 * scale,
773 300 * scale, B_TRUNCATE_MIDDLE), kTitleColumn);
774 AddColumn(new PackageColumn(B_TRANSLATE("Rating"), 80 * scale, 50 * scale,
775 100 * scale, B_TRUNCATE_MIDDLE), kRatingColumn);
776 AddColumn(new PackageColumn(B_TRANSLATE("Description"), 300 * scale,
777 80 * scale, 1000 * scale,
778 B_TRUNCATE_MIDDLE), kDescriptionColumn);
779 PackageColumn* sizeColumn = new PackageColumn(B_TRANSLATE("Size"),
780 spacing + StringWidth(B_TRANSLATE("9999.99 KiB")), 50 * scale,
781 140 * scale, B_TRUNCATE_END);
782 sizeColumn->SetAlignment(B_ALIGN_RIGHT);
783 AddColumn(sizeColumn, kSizeColumn);
784 AddColumn(new PackageColumn(B_TRANSLATE("Status"),
785 spacing + StringWidth(B_TRANSLATE("Available")), 60 * scale,
786 140 * scale, B_TRUNCATE_END), kStatusColumn);
788 AddColumn(new PackageColumn(B_TRANSLATE("Repository"), 120 * scale,
789 50 * scale, 200 * scale, B_TRUNCATE_MIDDLE), kRepositoryColumn);
790 SetColumnVisible(kRepositoryColumn, false);
791 // invisible by default
793 fItemCountView = new ItemCountView();
794 AddStatusView(fItemCountView);
798 PackageListView::~PackageListView()
800 Clear();
801 delete fPackageListener;
805 void
806 PackageListView::AttachedToWindow()
808 BColumnListView::AttachedToWindow();
810 PackageColumn::InitTextMargin(ScrollView());
814 void
815 PackageListView::AllAttached()
817 BColumnListView::AllAttached();
819 SetSortingEnabled(true);
820 SetSortColumn(ColumnAt(0), false, true);
824 void
825 PackageListView::MessageReceived(BMessage* message)
827 switch (message->what) {
828 case MSG_UPDATE_PACKAGE:
830 BString name;
831 uint32 changes;
832 if (message->FindString("name", &name) != B_OK
833 || message->FindUInt32("changes", &changes) != B_OK) {
834 break;
837 BAutolock _(fModelLock);
838 PackageRow* row = _FindRow(name);
839 if (row != NULL) {
840 if ((changes & PKG_CHANGED_TITLE) != 0)
841 row->UpdateTitle();
842 if ((changes & PKG_CHANGED_SUMMARY) != 0)
843 row->UpdateSummary();
844 if ((changes & PKG_CHANGED_RATINGS) != 0)
845 row->UpdateRating();
846 if ((changes & PKG_CHANGED_STATE) != 0) {
847 row->UpdateState();
848 if (fWorkStatusView != NULL) {
849 fWorkStatusView->PackageStatusChanged(
850 row->Package());
853 if ((changes & PKG_CHANGED_SIZE) != 0)
854 row->UpdateSize();
855 if ((changes & PKG_CHANGED_ICON) != 0)
856 row->UpdateTitle();
857 if ((changes & PKG_CHANGED_DEPOT) != 0)
858 row->UpdateRepository();
860 break;
863 default:
864 BColumnListView::MessageReceived(message);
865 break;
870 void
871 PackageListView::SelectionChanged()
873 BColumnListView::SelectionChanged();
875 BMessage message(MSG_PACKAGE_SELECTED);
877 PackageRow* selected = dynamic_cast<PackageRow*>(CurrentSelection());
878 if (selected != NULL)
879 message.AddString("name", selected->Package()->Name());
881 Window()->PostMessage(&message);
885 void
886 PackageListView::Clear()
888 fItemCountView->SetItemCount(0);
889 BColumnListView::Clear();
890 fRowByNameTable->Clear();
894 void
895 PackageListView::AddPackage(const PackageInfoRef& package)
897 PackageRow* packageRow = _FindRow(package);
899 // forget about it if this package is already in the listview
900 if (packageRow != NULL)
901 return;
903 BAutolock _(fModelLock);
905 // create the row for this package
906 packageRow = new PackageRow(package, fPackageListener);
908 // add the row, parent may be NULL (add at top level)
909 AddRow(packageRow);
911 // add to hash table for quick lookup of row by package name
912 fRowByNameTable->Insert(packageRow);
914 // make sure the row is initially expanded
915 ExpandOrCollapse(packageRow, true);
917 fItemCountView->SetItemCount(CountRows());
921 void
922 PackageListView::RemovePackage(const PackageInfoRef& package)
924 PackageRow* packageRow = _FindRow(package);
925 if (packageRow == NULL)
926 return;
928 fRowByNameTable->Remove(packageRow);
930 RemoveRow(packageRow);
931 delete packageRow;
933 fItemCountView->SetItemCount(CountRows());
937 void
938 PackageListView::SelectPackage(const PackageInfoRef& package)
940 PackageRow* row = _FindRow(package);
941 BRow* selected = CurrentSelection();
942 if (row != selected)
943 DeselectAll();
944 if (row != NULL) {
945 AddToSelection(row);
946 SetFocusRow(row, false);
947 ScrollTo(row);
952 void
953 PackageListView::AttachWorkStatusView(WorkStatusView* view)
955 fWorkStatusView = view;
959 PackageRow*
960 PackageListView::_FindRow(const PackageInfoRef& package)
962 if (package.Get() == NULL)
963 return NULL;
964 return fRowByNameTable->Lookup(package->Name().String());
968 PackageRow*
969 PackageListView::_FindRow(const BString& packageName)
971 return fRowByNameTable->Lookup(packageName.String());