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 */ 8 9 #include "PackageListView.h" 10 11 #include <algorithm> 12 #include <stdio.h> 13 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> 22 23 #include "MainWindow.h" 24 #include "WorkStatusView.h" 25 26 27 #undef B_TRANSLATION_CONTEXT 28 #define B_TRANSLATION_CONTEXT "PackageListView" 29 30 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); 37 38 39 inline BString 40 package_state_to_string(PackageInfoRef ref) 41 { 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: 52 { 53 BString data; 54 data.SetToFormat("%3.2f%%", ref->DownloadProgress() * 100.0); 55 return data; 56 } 57 case PENDING: 58 return B_TRANSLATE(skPackageStatePending); 59 } 60 61 return B_TRANSLATE("Unknown"); 62 } 63 64 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(); 74 75 void SetBitmap(SharedBitmap* bitmap, 76 SharedBitmap::Size size); 77 const BBitmap* Bitmap() const 78 { return fBitmap; } 79 80 private: 81 BitmapRef fReference; 82 const BBitmap* fBitmap; 83 }; 84 85 86 class RatingField : public BField { 87 public: 88 RatingField(float rating); 89 virtual ~RatingField(); 90 91 void SetRating(float rating); 92 float Rating() const 93 { return fRating; } 94 private: 95 float fRating; 96 }; 97 98 99 class SizeField : public BStringField { 100 public: 101 SizeField(double size); 102 virtual ~SizeField(); 103 104 void SetSize(double size); 105 double Size() const 106 { return fSize; } 107 private: 108 double fSize; 109 }; 110 111 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); 122 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; 128 129 virtual bool AcceptsField(const BField* field) const; 130 131 static void InitTextMargin(BView* parent); 132 133 private: 134 uint32 fTruncateMode; 135 static float sTextMargin; 136 }; 137 138 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(); 146 147 const PackageInfoRef& Package() const 148 { return fPackage; } 149 150 void UpdateTitle(); 151 void UpdateSummary(); 152 void UpdateState(); 153 void UpdateRating(); 154 void UpdateSize(); 155 void UpdateRepository(); 156 157 PackageRow*& NextInHash() 158 { return fNextInHash; } 159 160 private: 161 PackageInfoRef fPackage; 162 PackageInfoListenerRef fPackageListener; 163 164 PackageRow* fNextInHash; 165 // link for BOpenHashTable 166 }; 167 168 169 enum { 170 MSG_UPDATE_PACKAGE = 'updp' 171 }; 172 173 174 class PackageListener : public PackageInfoListener { 175 public: 176 PackageListener(PackageListView* view) 177 : 178 fView(view) 179 { 180 } 181 182 virtual ~PackageListener() 183 { 184 } 185 186 virtual void PackageChanged(const PackageInfoEvent& event) 187 { 188 BMessenger messenger(fView); 189 if (!messenger.IsValid()) 190 return; 191 192 const PackageInfo& package = *event.Package().Get(); 193 194 BMessage message(MSG_UPDATE_PACKAGE); 195 message.AddString("name", package.Name()); 196 message.AddUInt32("changes", event.Changes()); 197 198 messenger.SendMessage(&message); 199 } 200 201 private: 202 PackageListView* fView; 203 }; 204 205 206 // #pragma mark - SharedBitmapStringField 207 208 209 SharedBitmapStringField::SharedBitmapStringField(SharedBitmap* bitmap, 210 SharedBitmap::Size size, const char* string) 211 : 212 Inherited(string), 213 fBitmap(NULL) 214 { 215 SetBitmap(bitmap, size); 216 } 217 218 219 SharedBitmapStringField::~SharedBitmapStringField() 220 { 221 } 222 223 224 void 225 SharedBitmapStringField::SetBitmap(SharedBitmap* bitmap, 226 SharedBitmap::Size size) 227 { 228 fReference = bitmap; 229 fBitmap = bitmap != NULL ? bitmap->Bitmap(size) : NULL; 230 // TODO: cause a redraw? 231 } 232 233 234 // #pragma mark - RatingField 235 236 237 RatingField::RatingField(float rating) 238 : 239 fRating(0.0f) 240 { 241 SetRating(rating); 242 } 243 244 245 RatingField::~RatingField() 246 { 247 } 248 249 250 void 251 RatingField::SetRating(float rating) 252 { 253 if (rating < 0.0f) 254 rating = 0.0f; 255 if (rating > 5.0f) 256 rating = 5.0f; 257 258 if (rating == fRating) 259 return; 260 261 fRating = rating; 262 } 263 264 265 // #pragma mark - SizeField 266 267 268 SizeField::SizeField(double size) 269 : 270 BStringField(""), 271 fSize(-1.0) 272 { 273 SetSize(size); 274 } 275 276 277 SizeField::~SizeField() 278 { 279 } 280 281 282 void 283 SizeField::SetSize(double size) 284 { 285 if (size < 0.0) 286 size = 0.0; 287 288 if (size == fSize) 289 return; 290 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)); 297 } 298 299 fSize = size; 300 SetString(sizeString.String()); 301 } 302 303 304 // #pragma mark - PackageColumn 305 306 307 // TODO: Code-duplication with DriveSetup PartitionList.cpp 308 309 310 float PackageColumn::sTextMargin = 0.0; 311 312 313 PackageColumn::PackageColumn(const char* title, float width, float minWidth, 314 float maxWidth, uint32 truncateMode, alignment align) 315 : 316 Inherited(title, width, minWidth, maxWidth, align), 317 fTruncateMode(truncateMode) 318 { 319 SetWantsEvents(true); 320 } 321 322 323 void 324 PackageColumn::DrawField(BField* field, BRect rect, BView* parent) 325 { 326 SharedBitmapStringField* bitmapField 327 = dynamic_cast<SharedBitmapStringField*>(field); 328 BStringField* stringField = dynamic_cast<BStringField*>(field); 329 RatingField* ratingField = dynamic_cast<RatingField*>(field); 330 331 if (bitmapField != NULL) { 332 const BBitmap* bitmap = bitmapField->Bitmap(); 333 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; 339 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; 348 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; 354 } 355 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); 361 } 362 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); 368 } 369 370 // draw the string 371 DrawString(bitmapField->ClippedString(), parent, r); 372 373 } else if (stringField != NULL) { 374 375 float width = rect.Width() - (2 * sTextMargin); 376 377 if (width != stringField->Width()) { 378 BString truncatedString(stringField->String()); 379 380 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 381 stringField->SetClippedString(truncatedString.String()); 382 stringField->SetWidth(width); 383 } 384 385 DrawString(stringField->ClippedString(), parent, rect); 386 387 } else if (ratingField != NULL) { 388 389 const float kDefaultTextMargin = 8; 390 391 float width = rect.Width() - (2 * kDefaultTextMargin); 392 393 BString string = "★★★★★"; 394 float stringWidth = parent->StringWidth(string); 395 bool drawOverlay = true; 396 397 if (width < stringWidth) { 398 string.SetToFormat("%.1f", ratingField->Rating()); 399 drawOverlay = false; 400 stringWidth = parent->StringWidth(string); 401 } 402 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; 411 412 case B_ALIGN_RIGHT: 413 rect.left = rect.right - (stringWidth + kDefaultTextMargin); 414 break; 415 } 416 417 rect.left = floorf(rect.left); 418 rect.right = rect.left + stringWidth; 419 420 if (drawOverlay) 421 parent->SetHighColor(0, 170, 255); 422 423 font_height fontHeight; 424 parent->GetFontHeight(&fontHeight); 425 float y = rect.top + (rect.Height() 426 - (fontHeight.ascent + fontHeight.descent)) / 2 427 + fontHeight.ascent; 428 429 parent->DrawString(string, BPoint(rect.left, y)); 430 431 if (drawOverlay) { 432 rect.left = ceilf(rect.left 433 + (ratingField->Rating() / 5.0f) * rect.Width()); 434 435 rgb_color color = parent->LowColor(); 436 color.alpha = 190; 437 parent->SetHighColor(color); 438 439 parent->SetDrawingMode(B_OP_ALPHA); 440 parent->FillRect(rect, B_SOLID_HIGH); 441 442 } 443 } 444 } 445 446 447 int 448 PackageColumn::CompareFields(BField* field1, BField* field2) 449 { 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; 458 } 459 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()); 466 } 467 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; 476 } 477 478 return Inherited::CompareFields(field1, field2); 479 } 480 481 482 float 483 PackageColumn::GetPreferredWidth(BField *_field, BView* parent) const 484 { 485 SharedBitmapStringField* bitmapField 486 = dynamic_cast<SharedBitmapStringField*>(_field); 487 BStringField* stringField = dynamic_cast<BStringField*>(_field); 488 489 float parentWidth = Inherited::GetPreferredWidth(_field, parent); 490 float width = 0.0; 491 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; 505 } 506 return max_c(width, parentWidth); 507 } 508 509 510 bool 511 PackageColumn::AcceptsField(const BField* field) const 512 { 513 return dynamic_cast<const BStringField*>(field) != NULL 514 || dynamic_cast<const RatingField*>(field) != NULL; 515 } 516 517 518 void 519 PackageColumn::InitTextMargin(BView* parent) 520 { 521 BFont font; 522 parent->GetFont(&font); 523 sTextMargin = ceilf(font.Size() * 0.8); 524 } 525 526 527 // #pragma mark - PackageRow 528 529 530 enum { 531 kTitleColumn, 532 kRatingColumn, 533 kDescriptionColumn, 534 kSizeColumn, 535 kStatusColumn, 536 kRepositoryColumn 537 }; 538 539 540 PackageRow::PackageRow(const PackageInfoRef& packageRef, 541 PackageListener* packageListener) 542 : 543 Inherited(ceilf(be_plain_font->Size() * 1.8f)), 544 fPackage(packageRef), 545 fPackageListener(packageListener), 546 fNextInHash(NULL) 547 { 548 if (packageRef.Get() == NULL) 549 return; 550 551 PackageInfo& package = *packageRef.Get(); 552 553 // Package icon and title 554 // NOTE: The icon BBitmap is referenced by the fPackage member. 555 UpdateTitle(); 556 557 // Rating 558 UpdateRating(); 559 560 // Summary 561 UpdateSummary(); 562 563 // Size 564 UpdateSize(); 565 566 // Status 567 UpdateState(); 568 569 // Repository 570 UpdateRepository(); 571 572 package.AddListener(fPackageListener); 573 } 574 575 576 PackageRow::~PackageRow() 577 { 578 if (fPackage.Get() != NULL) 579 fPackage->RemoveListener(fPackageListener); 580 } 581 582 583 void 584 PackageRow::UpdateTitle() 585 { 586 if (fPackage.Get() == NULL) 587 return; 588 589 SetField(new SharedBitmapStringField(fPackage->Icon(), 590 SharedBitmap::SIZE_16, fPackage->Title()), kTitleColumn); 591 } 592 593 594 void 595 PackageRow::UpdateState() 596 { 597 if (fPackage.Get() == NULL) 598 return; 599 600 SetField(new BStringField(package_state_to_string(fPackage)), 601 kStatusColumn); 602 } 603 604 605 void 606 PackageRow::UpdateSummary() 607 { 608 if (fPackage.Get() == NULL) 609 return; 610 611 SetField(new BStringField(fPackage->ShortDescription()), 612 kDescriptionColumn); 613 } 614 615 616 void 617 PackageRow::UpdateRating() 618 { 619 if (fPackage.Get() == NULL) 620 return; 621 RatingSummary summary = fPackage->CalculateRatingSummary(); 622 SetField(new RatingField(summary.averageRating), kRatingColumn); 623 } 624 625 626 void 627 PackageRow::UpdateSize() 628 { 629 if (fPackage.Get() == NULL) 630 return; 631 632 SetField(new SizeField(fPackage->Size()), kSizeColumn); 633 } 634 635 636 void 637 PackageRow::UpdateRepository() 638 { 639 if (fPackage.Get() == NULL) 640 return; 641 642 SetField(new BStringField(fPackage->DepotName()), kRepositoryColumn); 643 } 644 645 646 // #pragma mark - ItemCountView 647 648 649 class PackageListView::ItemCountView : public BView { 650 public: 651 ItemCountView() 652 : 653 BView("item count view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 654 fItemCount(0) 655 { 656 BFont font(be_plain_font); 657 font.SetSize(9.0f); 658 SetFont(&font); 659 660 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 661 SetLowUIColor(ViewUIColor()); 662 SetHighUIColor(LowUIColor(), B_DARKEN_4_TINT); 663 } 664 665 virtual BSize MinSize() 666 { 667 BString label(_GetLabel()); 668 return BSize(StringWidth(label) + 10, B_H_SCROLL_BAR_HEIGHT); 669 } 670 671 virtual BSize PreferredSize() 672 { 673 return MinSize(); 674 } 675 676 virtual BSize MaxSize() 677 { 678 return MinSize(); 679 } 680 681 virtual void Draw(BRect updateRect) 682 { 683 FillRect(updateRect, B_SOLID_LOW); 684 685 BString label(_GetLabel()); 686 687 font_height fontHeight; 688 GetFontHeight(&fontHeight); 689 690 BRect bounds(Bounds()); 691 float width = StringWidth(label); 692 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; 698 699 DrawString(label, offset); 700 } 701 702 void SetItemCount(int32 count) 703 { 704 if (count == fItemCount) 705 return; 706 BSize minSize = MinSize(); 707 fItemCount = count; 708 if (minSize != MinSize()) 709 InvalidateLayout(); 710 Invalidate(); 711 } 712 713 private: 714 BString _GetLabel() const 715 { 716 static BMessageFormat format(B_TRANSLATE("{0, plural, " 717 "one{# item} other{# items}}")); 718 719 BString label; 720 format.Format(label, fItemCount); 721 return label; 722 } 723 724 int32 fItemCount; 725 }; 726 727 728 // #pragma mark - PackageListView::RowByNameHashDefinition 729 730 731 struct PackageListView::RowByNameHashDefinition { 732 typedef const char* KeyType; 733 typedef PackageRow ValueType; 734 735 size_t HashKey(const char* key) const 736 { 737 return BPackageKit::BHPKG::BPrivate::hash_string(key); 738 } 739 740 size_t Hash(PackageRow* value) const 741 { 742 return BPackageKit::BHPKG::BPrivate::hash_string( 743 value->Package()->Name().String()); 744 } 745 746 bool Compare(const char* key, PackageRow* value) const 747 { 748 return value->Package()->Name() == key; 749 } 750 751 ValueType*& GetLink(PackageRow* value) const 752 { 753 return value->NextInHash(); 754 } 755 }; 756 757 758 // #pragma mark - PackageListView 759 760 761 PackageListView::PackageListView(BLocker* modelLock) 762 : 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) 768 { 769 float scale = be_plain_font->Size() / 12.f; 770 float spacing = be_control_look->DefaultItemSpacing() * 2; 771 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); 787 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 792 793 fItemCountView = new ItemCountView(); 794 AddStatusView(fItemCountView); 795 } 796 797 798 PackageListView::~PackageListView() 799 { 800 Clear(); 801 delete fPackageListener; 802 } 803 804 805 void 806 PackageListView::AttachedToWindow() 807 { 808 BColumnListView::AttachedToWindow(); 809 810 PackageColumn::InitTextMargin(ScrollView()); 811 } 812 813 814 void 815 PackageListView::AllAttached() 816 { 817 BColumnListView::AllAttached(); 818 819 SetSortingEnabled(true); 820 SetSortColumn(ColumnAt(0), false, true); 821 } 822 823 824 void 825 PackageListView::MessageReceived(BMessage* message) 826 { 827 switch (message->what) { 828 case MSG_UPDATE_PACKAGE: 829 { 830 BString name; 831 uint32 changes; 832 if (message->FindString("name", &name) != B_OK 833 || message->FindUInt32("changes", &changes) != B_OK) { 834 break; 835 } 836 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 ((changes & PKG_CHANGED_SIZE) != 0) 849 row->UpdateSize(); 850 if ((changes & PKG_CHANGED_ICON) != 0) 851 row->UpdateTitle(); 852 if ((changes & PKG_CHANGED_DEPOT) != 0) 853 row->UpdateRepository(); 854 } 855 break; 856 } 857 858 default: 859 BColumnListView::MessageReceived(message); 860 break; 861 } 862 } 863 864 865 void 866 PackageListView::SelectionChanged() 867 { 868 BColumnListView::SelectionChanged(); 869 870 BMessage message(MSG_PACKAGE_SELECTED); 871 872 PackageRow* selected = dynamic_cast<PackageRow*>(CurrentSelection()); 873 if (selected != NULL) 874 message.AddString("name", selected->Package()->Name()); 875 876 Window()->PostMessage(&message); 877 } 878 879 880 void 881 PackageListView::Clear() 882 { 883 fItemCountView->SetItemCount(0); 884 BColumnListView::Clear(); 885 fRowByNameTable->Clear(); 886 } 887 888 889 void 890 PackageListView::AddPackage(const PackageInfoRef& package) 891 { 892 PackageRow* packageRow = _FindRow(package); 893 894 // forget about it if this package is already in the listview 895 if (packageRow != NULL) 896 return; 897 898 BAutolock _(fModelLock); 899 900 // create the row for this package 901 packageRow = new PackageRow(package, fPackageListener); 902 903 // add the row, parent may be NULL (add at top level) 904 AddRow(packageRow); 905 906 // add to hash table for quick lookup of row by package name 907 fRowByNameTable->Insert(packageRow); 908 909 // make sure the row is initially expanded 910 ExpandOrCollapse(packageRow, true); 911 912 fItemCountView->SetItemCount(CountRows()); 913 } 914 915 916 void 917 PackageListView::RemovePackage(const PackageInfoRef& package) 918 { 919 PackageRow* packageRow = _FindRow(package); 920 if (packageRow == NULL) 921 return; 922 923 fRowByNameTable->Remove(packageRow); 924 925 RemoveRow(packageRow); 926 delete packageRow; 927 928 fItemCountView->SetItemCount(CountRows()); 929 } 930 931 932 void 933 PackageListView::SelectPackage(const PackageInfoRef& package) 934 { 935 PackageRow* row = _FindRow(package); 936 BRow* selected = CurrentSelection(); 937 if (row != selected) 938 DeselectAll(); 939 if (row != NULL) { 940 AddToSelection(row); 941 SetFocusRow(row, false); 942 ScrollTo(row); 943 } 944 } 945 946 947 void 948 PackageListView::AttachWorkStatusView(WorkStatusView* view) 949 { 950 fWorkStatusView = view; 951 } 952 953 954 PackageRow* 955 PackageListView::_FindRow(const PackageInfoRef& package) 956 { 957 if (package.Get() == NULL) 958 return NULL; 959 return fRowByNameTable->Lookup(package->Name().String()); 960 } 961 962 963 PackageRow* 964 PackageListView::_FindRow(const BString& packageName) 965 { 966 return fRowByNameTable->Lookup(packageName.String()); 967 } 968