1 /* 2 * Copyright 2018, Andrew Lindesay, <apl@lindesay.co.nz>. 3 * Copyright 2017, Julian Harnath, <julian.harnath@rwth-aachen.de>. 4 * Copyright 2015, Axel Dörfler, <axeld@pinc-software.de>. 5 * Copyright 2013-2014, Stephan Aßmus <superstippi@gmx.de>. 6 * Copyright 2013, Rene Gollent, <rene@gollent.com>. 7 * All rights reserved. Distributed under the terms of the MIT License. 8 */ 9 10 #include "PackageListView.h" 11 12 #include <algorithm> 13 #include <stdio.h> 14 15 #include <Autolock.h> 16 #include <Catalog.h> 17 #include <ControlLook.h> 18 #include <ScrollBar.h> 19 #include <StringFormat.h> 20 #include <StringForSize.h> 21 #include <package/hpkg/Strings.h> 22 #include <Window.h> 23 24 #include "MainWindow.h" 25 #include "WorkStatusView.h" 26 27 28 #undef B_TRANSLATION_CONTEXT 29 #define B_TRANSLATION_CONTEXT "PackageListView" 30 31 32 static const char* skPackageStateAvailable = B_TRANSLATE_MARK("Available"); 33 static const char* skPackageStateUninstalled = B_TRANSLATE_MARK("Uninstalled"); 34 static const char* skPackageStateActive = B_TRANSLATE_MARK("Active"); 35 static const char* skPackageStateInactive = B_TRANSLATE_MARK("Inactive"); 36 static const char* skPackageStatePending = B_TRANSLATE_MARK( 37 "Pending" B_UTF8_ELLIPSIS); 38 39 40 inline BString 41 package_state_to_string(PackageInfoRef ref) 42 { 43 switch (ref->State()) { 44 case NONE: 45 return B_TRANSLATE(skPackageStateAvailable); 46 case INSTALLED: 47 return B_TRANSLATE(skPackageStateInactive); 48 case ACTIVATED: 49 return B_TRANSLATE(skPackageStateActive); 50 case UNINSTALLED: 51 return B_TRANSLATE(skPackageStateUninstalled); 52 case DOWNLOADING: 53 { 54 BString data; 55 data.SetToFormat("%3.2f%%", ref->DownloadProgress() * 100.0); 56 return data; 57 } 58 case PENDING: 59 return B_TRANSLATE(skPackageStatePending); 60 } 61 62 return B_TRANSLATE("Unknown"); 63 } 64 65 66 // A field type displaying both a bitmap and a string so that the 67 // tree display looks nicer (both text and bitmap are indented) 68 class SharedBitmapStringField : public BStringField { 69 typedef BStringField Inherited; 70 public: 71 SharedBitmapStringField(SharedBitmap* bitmap, 72 SharedBitmap::Size size, 73 const char* string); 74 virtual ~SharedBitmapStringField(); 75 76 void SetBitmap(SharedBitmap* bitmap, 77 SharedBitmap::Size size); 78 const BBitmap* Bitmap() const 79 { return fBitmap; } 80 81 private: 82 BitmapRef fReference; 83 const BBitmap* fBitmap; 84 }; 85 86 87 class RatingField : public BField { 88 public: 89 RatingField(float rating); 90 virtual ~RatingField(); 91 92 void SetRating(float rating); 93 float Rating() const 94 { return fRating; } 95 private: 96 float fRating; 97 }; 98 99 100 class SizeField : public BStringField { 101 public: 102 SizeField(double size); 103 virtual ~SizeField(); 104 105 void SetSize(double size); 106 double Size() const 107 { return fSize; } 108 private: 109 double fSize; 110 }; 111 112 113 // BColumn for PackageListView which knows how to render 114 // a SharedBitmapStringField 115 // TODO: Code-duplication with DriveSetup PartitionList.h 116 class PackageColumn : public BTitledColumn { 117 typedef BTitledColumn Inherited; 118 public: 119 PackageColumn(const char* title, 120 float width, float minWidth, 121 float maxWidth, uint32 truncateMode, 122 alignment align = B_ALIGN_LEFT); 123 124 virtual void DrawField(BField* field, BRect rect, 125 BView* parent); 126 virtual int CompareFields(BField* field1, BField* field2); 127 virtual float GetPreferredWidth(BField* field, 128 BView* parent) const; 129 130 virtual bool AcceptsField(const BField* field) const; 131 132 static void InitTextMargin(BView* parent); 133 134 private: 135 uint32 fTruncateMode; 136 static float sTextMargin; 137 }; 138 139 140 // BRow for the PartitionListView 141 class PackageRow : public BRow { 142 typedef BRow Inherited; 143 public: 144 PackageRow(const PackageInfoRef& package, 145 PackageListener* listener); 146 virtual ~PackageRow(); 147 148 const PackageInfoRef& Package() const 149 { return fPackage; } 150 151 void UpdateTitle(); 152 void UpdateSummary(); 153 void UpdateState(); 154 void UpdateRating(); 155 void UpdateSize(); 156 void UpdateRepository(); 157 158 PackageRow*& NextInHash() 159 { return fNextInHash; } 160 161 private: 162 PackageInfoRef fPackage; 163 PackageInfoListenerRef fPackageListener; 164 165 PackageRow* fNextInHash; 166 // link for BOpenHashTable 167 }; 168 169 170 enum { 171 MSG_UPDATE_PACKAGE = 'updp' 172 }; 173 174 175 class PackageListener : public PackageInfoListener { 176 public: 177 PackageListener(PackageListView* view) 178 : 179 fView(view) 180 { 181 } 182 183 virtual ~PackageListener() 184 { 185 } 186 187 virtual void PackageChanged(const PackageInfoEvent& event) 188 { 189 BMessenger messenger(fView); 190 if (!messenger.IsValid()) 191 return; 192 193 const PackageInfo& package = *event.Package().Get(); 194 195 BMessage message(MSG_UPDATE_PACKAGE); 196 message.AddString("name", package.Name()); 197 message.AddUInt32("changes", event.Changes()); 198 199 messenger.SendMessage(&message); 200 } 201 202 private: 203 PackageListView* fView; 204 }; 205 206 207 // #pragma mark - SharedBitmapStringField 208 209 210 SharedBitmapStringField::SharedBitmapStringField(SharedBitmap* bitmap, 211 SharedBitmap::Size size, const char* string) 212 : 213 Inherited(string), 214 fBitmap(NULL) 215 { 216 SetBitmap(bitmap, size); 217 } 218 219 220 SharedBitmapStringField::~SharedBitmapStringField() 221 { 222 } 223 224 225 void 226 SharedBitmapStringField::SetBitmap(SharedBitmap* bitmap, 227 SharedBitmap::Size size) 228 { 229 fReference = bitmap; 230 fBitmap = bitmap != NULL ? bitmap->Bitmap(size) : NULL; 231 // TODO: cause a redraw? 232 } 233 234 235 // #pragma mark - RatingField 236 237 238 RatingField::RatingField(float rating) 239 : 240 fRating(0.0f) 241 { 242 SetRating(rating); 243 } 244 245 246 RatingField::~RatingField() 247 { 248 } 249 250 251 void 252 RatingField::SetRating(float rating) 253 { 254 if (rating < 0.0f) 255 rating = 0.0f; 256 if (rating > 5.0f) 257 rating = 5.0f; 258 259 if (rating == fRating) 260 return; 261 262 fRating = rating; 263 } 264 265 266 // #pragma mark - SizeField 267 268 269 SizeField::SizeField(double size) 270 : 271 BStringField(""), 272 fSize(-1.0) 273 { 274 SetSize(size); 275 } 276 277 278 SizeField::~SizeField() 279 { 280 } 281 282 283 void 284 SizeField::SetSize(double size) 285 { 286 if (size < 0.0) 287 size = 0.0; 288 289 if (size == fSize) 290 return; 291 292 BString sizeString; 293 if (size == 0) { 294 sizeString = B_TRANSLATE_CONTEXT("-", "no package size"); 295 } else { 296 char buffer[256]; 297 sizeString = string_for_size(size, buffer, sizeof(buffer)); 298 } 299 300 fSize = size; 301 SetString(sizeString.String()); 302 } 303 304 305 // #pragma mark - PackageColumn 306 307 308 // TODO: Code-duplication with DriveSetup PartitionList.cpp 309 310 311 float PackageColumn::sTextMargin = 0.0; 312 313 314 PackageColumn::PackageColumn(const char* title, float width, float minWidth, 315 float maxWidth, uint32 truncateMode, alignment align) 316 : 317 Inherited(title, width, minWidth, maxWidth, align), 318 fTruncateMode(truncateMode) 319 { 320 SetWantsEvents(true); 321 } 322 323 324 void 325 PackageColumn::DrawField(BField* field, BRect rect, BView* parent) 326 { 327 SharedBitmapStringField* bitmapField 328 = dynamic_cast<SharedBitmapStringField*>(field); 329 BStringField* stringField = dynamic_cast<BStringField*>(field); 330 RatingField* ratingField = dynamic_cast<RatingField*>(field); 331 332 if (bitmapField != NULL) { 333 const BBitmap* bitmap = bitmapField->Bitmap(); 334 335 // Scale the bitmap to 16x16 336 BRect r = BRect(0, 0, 15, 15); 337 338 // figure out the placement 339 float x = 0.0; 340 float y = rect.top + ((rect.Height() - r.Height()) / 2) - 1; 341 float width = 0.0; 342 343 switch (Alignment()) { 344 default: 345 case B_ALIGN_LEFT: 346 case B_ALIGN_CENTER: 347 x = rect.left + sTextMargin; 348 width = rect.right - (x + r.Width()) - (2 * sTextMargin); 349 r.Set(x + r.Width(), rect.top, rect.right - width, rect.bottom); 350 break; 351 352 case B_ALIGN_RIGHT: 353 x = rect.right - sTextMargin - r.Width(); 354 width = (x - rect.left - (2 * sTextMargin)); 355 r.Set(rect.left, rect.top, rect.left + width, rect.bottom); 356 break; 357 } 358 359 if (width != bitmapField->Width()) { 360 BString truncatedString(bitmapField->String()); 361 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 362 bitmapField->SetClippedString(truncatedString.String()); 363 bitmapField->SetWidth(width); 364 } 365 366 // draw the bitmap 367 if (bitmap != NULL) { 368 parent->SetDrawingMode(B_OP_ALPHA); 369 BRect viewRect(x, y, x + 15, y + 15); 370 parent->DrawBitmap(bitmap, bitmap->Bounds(), viewRect); 371 parent->SetDrawingMode(B_OP_OVER); 372 } 373 374 // draw the string 375 DrawString(bitmapField->ClippedString(), parent, r); 376 377 } else if (stringField != NULL) { 378 379 float width = rect.Width() - (2 * sTextMargin); 380 381 if (width != stringField->Width()) { 382 BString truncatedString(stringField->String()); 383 384 parent->TruncateString(&truncatedString, fTruncateMode, width + 2); 385 stringField->SetClippedString(truncatedString.String()); 386 stringField->SetWidth(width); 387 } 388 389 DrawString(stringField->ClippedString(), parent, rect); 390 391 } else if (ratingField != NULL) { 392 393 const float kDefaultTextMargin = 8; 394 395 float width = rect.Width() - (2 * kDefaultTextMargin); 396 397 BString string = "★★★★★"; 398 float stringWidth = parent->StringWidth(string); 399 bool drawOverlay = true; 400 401 if (width < stringWidth) { 402 string.SetToFormat("%.1f", ratingField->Rating()); 403 drawOverlay = false; 404 stringWidth = parent->StringWidth(string); 405 } 406 407 switch (Alignment()) { 408 default: 409 case B_ALIGN_LEFT: 410 rect.left += kDefaultTextMargin; 411 break; 412 case B_ALIGN_CENTER: 413 rect.left = rect.left + (width - stringWidth) / 2.0f; 414 break; 415 416 case B_ALIGN_RIGHT: 417 rect.left = rect.right - (stringWidth + kDefaultTextMargin); 418 break; 419 } 420 421 rect.left = floorf(rect.left); 422 rect.right = rect.left + stringWidth; 423 424 if (drawOverlay) 425 parent->SetHighColor(0, 170, 255); 426 427 font_height fontHeight; 428 parent->GetFontHeight(&fontHeight); 429 float y = rect.top + (rect.Height() 430 - (fontHeight.ascent + fontHeight.descent)) / 2 431 + fontHeight.ascent; 432 433 parent->DrawString(string, BPoint(rect.left, y)); 434 435 if (drawOverlay) { 436 rect.left = ceilf(rect.left 437 + (ratingField->Rating() / 5.0f) * rect.Width()); 438 439 rgb_color color = parent->LowColor(); 440 color.alpha = 190; 441 parent->SetHighColor(color); 442 443 parent->SetDrawingMode(B_OP_ALPHA); 444 parent->FillRect(rect, B_SOLID_HIGH); 445 446 } 447 } 448 } 449 450 451 int 452 PackageColumn::CompareFields(BField* field1, BField* field2) 453 { 454 SizeField* sizeField1 = dynamic_cast<SizeField*>(field1); 455 SizeField* sizeField2 = dynamic_cast<SizeField*>(field2); 456 if (sizeField1 != NULL && sizeField2 != NULL) { 457 if (sizeField1->Size() > sizeField2->Size()) 458 return -1; 459 else if (sizeField1->Size() < sizeField2->Size()) 460 return 1; 461 return 0; 462 } 463 464 BStringField* stringField1 = dynamic_cast<BStringField*>(field1); 465 BStringField* stringField2 = dynamic_cast<BStringField*>(field2); 466 if (stringField1 != NULL && stringField2 != NULL) { 467 // TODO: Locale aware string compare... not too important if 468 // package names are not translated. 469 return strcasecmp(stringField1->String(), stringField2->String()); 470 } 471 472 RatingField* ratingField1 = dynamic_cast<RatingField*>(field1); 473 RatingField* ratingField2 = dynamic_cast<RatingField*>(field2); 474 if (ratingField1 != NULL && ratingField2 != NULL) { 475 if (ratingField1->Rating() > ratingField2->Rating()) 476 return -1; 477 else if (ratingField1->Rating() < ratingField2->Rating()) 478 return 1; 479 return 0; 480 } 481 482 return Inherited::CompareFields(field1, field2); 483 } 484 485 486 float 487 PackageColumn::GetPreferredWidth(BField *_field, BView* parent) const 488 { 489 SharedBitmapStringField* bitmapField 490 = dynamic_cast<SharedBitmapStringField*>(_field); 491 BStringField* stringField = dynamic_cast<BStringField*>(_field); 492 493 float parentWidth = Inherited::GetPreferredWidth(_field, parent); 494 float width = 0.0; 495 496 if (bitmapField) { 497 const BBitmap* bitmap = bitmapField->Bitmap(); 498 BFont font; 499 parent->GetFont(&font); 500 width = font.StringWidth(bitmapField->String()) + 3 * sTextMargin; 501 if (bitmap) 502 width += bitmap->Bounds().Width(); 503 else 504 width += 16; 505 } else if (stringField) { 506 BFont font; 507 parent->GetFont(&font); 508 width = font.StringWidth(stringField->String()) + 2 * sTextMargin; 509 } 510 return max_c(width, parentWidth); 511 } 512 513 514 bool 515 PackageColumn::AcceptsField(const BField* field) const 516 { 517 return dynamic_cast<const BStringField*>(field) != NULL 518 || dynamic_cast<const RatingField*>(field) != NULL; 519 } 520 521 522 void 523 PackageColumn::InitTextMargin(BView* parent) 524 { 525 BFont font; 526 parent->GetFont(&font); 527 sTextMargin = ceilf(font.Size() * 0.8); 528 } 529 530 531 // #pragma mark - PackageRow 532 533 534 enum { 535 kTitleColumn, 536 kRatingColumn, 537 kDescriptionColumn, 538 kSizeColumn, 539 kStatusColumn, 540 kRepositoryColumn 541 }; 542 543 544 PackageRow::PackageRow(const PackageInfoRef& packageRef, 545 PackageListener* packageListener) 546 : 547 Inherited(ceilf(be_plain_font->Size() * 1.8f)), 548 fPackage(packageRef), 549 fPackageListener(packageListener), 550 fNextInHash(NULL) 551 { 552 if (packageRef.Get() == NULL) 553 return; 554 555 PackageInfo& package = *packageRef.Get(); 556 557 // Package icon and title 558 // NOTE: The icon BBitmap is referenced by the fPackage member. 559 UpdateTitle(); 560 561 // Rating 562 UpdateRating(); 563 564 // Summary 565 UpdateSummary(); 566 567 // Size 568 UpdateSize(); 569 570 // Status 571 UpdateState(); 572 573 // Repository 574 UpdateRepository(); 575 576 package.AddListener(fPackageListener); 577 } 578 579 580 PackageRow::~PackageRow() 581 { 582 if (fPackage.Get() != NULL) 583 fPackage->RemoveListener(fPackageListener); 584 } 585 586 587 void 588 PackageRow::UpdateTitle() 589 { 590 if (fPackage.Get() == NULL) 591 return; 592 593 SetField(new SharedBitmapStringField(fPackage->Icon(), 594 SharedBitmap::SIZE_16, fPackage->Title()), kTitleColumn); 595 } 596 597 598 void 599 PackageRow::UpdateState() 600 { 601 if (fPackage.Get() == NULL) 602 return; 603 604 SetField(new BStringField(package_state_to_string(fPackage)), 605 kStatusColumn); 606 } 607 608 609 void 610 PackageRow::UpdateSummary() 611 { 612 if (fPackage.Get() == NULL) 613 return; 614 615 SetField(new BStringField(fPackage->ShortDescription()), 616 kDescriptionColumn); 617 } 618 619 620 void 621 PackageRow::UpdateRating() 622 { 623 if (fPackage.Get() == NULL) 624 return; 625 RatingSummary summary = fPackage->CalculateRatingSummary(); 626 SetField(new RatingField(summary.averageRating), kRatingColumn); 627 } 628 629 630 void 631 PackageRow::UpdateSize() 632 { 633 if (fPackage.Get() == NULL) 634 return; 635 636 SetField(new SizeField(fPackage->Size()), kSizeColumn); 637 } 638 639 640 void 641 PackageRow::UpdateRepository() 642 { 643 if (fPackage.Get() == NULL) 644 return; 645 646 SetField(new BStringField(fPackage->DepotName()), kRepositoryColumn); 647 } 648 649 650 // #pragma mark - ItemCountView 651 652 653 class PackageListView::ItemCountView : public BView { 654 public: 655 ItemCountView() 656 : 657 BView("item count view", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), 658 fItemCount(0) 659 { 660 BFont font(be_plain_font); 661 font.SetSize(9.0f); 662 SetFont(&font); 663 664 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 665 SetLowUIColor(ViewUIColor()); 666 SetHighUIColor(LowUIColor(), B_DARKEN_4_TINT); 667 668 // constantly calculating the size is expensive so here a sensible 669 // upper limit on the number of packages is arbitrarily chosen. 670 fMinSize = BSize(StringWidth(_DeriveLabel(999999)) + 10, 671 B_H_SCROLL_BAR_HEIGHT); 672 } 673 674 virtual BSize MinSize() 675 { 676 return fMinSize; 677 } 678 679 virtual BSize PreferredSize() 680 { 681 return MinSize(); 682 } 683 684 virtual BSize MaxSize() 685 { 686 return MinSize(); 687 } 688 689 virtual void Draw(BRect updateRect) 690 { 691 FillRect(updateRect, B_SOLID_LOW); 692 693 font_height fontHeight; 694 GetFontHeight(&fontHeight); 695 696 BRect bounds(Bounds()); 697 float width = StringWidth(fLabel); 698 699 BPoint offset; 700 offset.x = bounds.left + (bounds.Width() - width) / 2.0f; 701 offset.y = bounds.top + (bounds.Height() 702 - (fontHeight.ascent + fontHeight.descent)) / 2.0f 703 + fontHeight.ascent; 704 705 DrawString(fLabel, offset); 706 } 707 708 void SetItemCount(int32 count) 709 { 710 if (count == fItemCount) 711 return; 712 fItemCount = count; 713 fLabel = _DeriveLabel(fItemCount); 714 Invalidate(); 715 } 716 717 private: 718 719 /*! This method is hit quite often when the list of packages in the 720 table-view are updated. Derivation of the plural for some 721 languages such as Russian can be slow so this method should be 722 called sparingly. 723 */ 724 725 BString _DeriveLabel(int32 count) const 726 { 727 static BStringFormat format(B_TRANSLATE("{0, plural, " 728 "one{# item} other{# items}}")); 729 BString label; 730 format.Format(label, count); 731 return label; 732 } 733 734 int32 fItemCount; 735 BString fLabel; 736 BSize fMinSize; 737 }; 738 739 740 // #pragma mark - PackageListView::RowByNameHashDefinition 741 742 743 struct PackageListView::RowByNameHashDefinition { 744 typedef const char* KeyType; 745 typedef PackageRow ValueType; 746 747 size_t HashKey(const char* key) const 748 { 749 return BPackageKit::BHPKG::BPrivate::hash_string(key); 750 } 751 752 size_t Hash(PackageRow* value) const 753 { 754 return BPackageKit::BHPKG::BPrivate::hash_string( 755 value->Package()->Name().String()); 756 } 757 758 bool Compare(const char* key, PackageRow* value) const 759 { 760 return value->Package()->Name() == key; 761 } 762 763 ValueType*& GetLink(PackageRow* value) const 764 { 765 return value->NextInHash(); 766 } 767 }; 768 769 770 // #pragma mark - PackageListView 771 772 773 PackageListView::PackageListView(BLocker* modelLock) 774 : 775 BColumnListView(B_TRANSLATE("All packages"), 0, B_FANCY_BORDER, true), 776 fModelLock(modelLock), 777 fPackageListener(new(std::nothrow) PackageListener(this)), 778 fRowByNameTable(new RowByNameTable()), 779 fWorkStatusView(NULL) 780 { 781 float scale = be_plain_font->Size() / 12.f; 782 float spacing = be_control_look->DefaultItemSpacing() * 2; 783 784 AddColumn(new PackageColumn(B_TRANSLATE("Name"), 150 * scale, 50 * scale, 785 300 * scale, B_TRUNCATE_MIDDLE), kTitleColumn); 786 AddColumn(new PackageColumn(B_TRANSLATE("Rating"), 80 * scale, 50 * scale, 787 100 * scale, B_TRUNCATE_MIDDLE), kRatingColumn); 788 AddColumn(new PackageColumn(B_TRANSLATE("Description"), 300 * scale, 789 80 * scale, 1000 * scale, 790 B_TRUNCATE_MIDDLE), kDescriptionColumn); 791 PackageColumn* sizeColumn = new PackageColumn(B_TRANSLATE("Size"), 792 spacing + StringWidth(B_TRANSLATE("9999.99 KiB")), 50 * scale, 793 140 * scale, B_TRUNCATE_END); 794 sizeColumn->SetAlignment(B_ALIGN_RIGHT); 795 AddColumn(sizeColumn, kSizeColumn); 796 AddColumn(new PackageColumn(B_TRANSLATE("Status"), 797 spacing + StringWidth(B_TRANSLATE("Available")), 60 * scale, 798 140 * scale, B_TRUNCATE_END), kStatusColumn); 799 800 AddColumn(new PackageColumn(B_TRANSLATE("Repository"), 120 * scale, 801 50 * scale, 200 * scale, B_TRUNCATE_MIDDLE), kRepositoryColumn); 802 SetColumnVisible(kRepositoryColumn, false); 803 // invisible by default 804 805 fItemCountView = new ItemCountView(); 806 AddStatusView(fItemCountView); 807 } 808 809 810 PackageListView::~PackageListView() 811 { 812 Clear(); 813 delete fPackageListener; 814 } 815 816 817 void 818 PackageListView::AttachedToWindow() 819 { 820 BColumnListView::AttachedToWindow(); 821 822 PackageColumn::InitTextMargin(ScrollView()); 823 } 824 825 826 void 827 PackageListView::AllAttached() 828 { 829 BColumnListView::AllAttached(); 830 831 SetSortingEnabled(true); 832 SetSortColumn(ColumnAt(0), false, true); 833 } 834 835 836 void 837 PackageListView::MessageReceived(BMessage* message) 838 { 839 switch (message->what) { 840 case MSG_UPDATE_PACKAGE: 841 { 842 BString name; 843 uint32 changes; 844 if (message->FindString("name", &name) != B_OK 845 || message->FindUInt32("changes", &changes) != B_OK) { 846 break; 847 } 848 849 BAutolock _(fModelLock); 850 PackageRow* row = _FindRow(name); 851 if (row != NULL) { 852 if ((changes & PKG_CHANGED_TITLE) != 0) 853 row->UpdateTitle(); 854 if ((changes & PKG_CHANGED_SUMMARY) != 0) 855 row->UpdateSummary(); 856 if ((changes & PKG_CHANGED_RATINGS) != 0) 857 row->UpdateRating(); 858 if ((changes & PKG_CHANGED_STATE) != 0) 859 row->UpdateState(); 860 if ((changes & PKG_CHANGED_SIZE) != 0) 861 row->UpdateSize(); 862 if ((changes & PKG_CHANGED_ICON) != 0) 863 row->UpdateTitle(); 864 if ((changes & PKG_CHANGED_DEPOT) != 0) 865 row->UpdateRepository(); 866 } 867 break; 868 } 869 870 default: 871 BColumnListView::MessageReceived(message); 872 break; 873 } 874 } 875 876 877 void 878 PackageListView::SelectionChanged() 879 { 880 BColumnListView::SelectionChanged(); 881 882 BMessage message(MSG_PACKAGE_SELECTED); 883 884 PackageRow* selected = dynamic_cast<PackageRow*>(CurrentSelection()); 885 if (selected != NULL) 886 message.AddString("name", selected->Package()->Name()); 887 888 Window()->PostMessage(&message); 889 } 890 891 892 void 893 PackageListView::Clear() 894 { 895 fItemCountView->SetItemCount(0); 896 BColumnListView::Clear(); 897 fRowByNameTable->Clear(); 898 } 899 900 901 void 902 PackageListView::AddPackage(const PackageInfoRef& package) 903 { 904 PackageRow* packageRow = _FindRow(package); 905 906 // forget about it if this package is already in the listview 907 if (packageRow != NULL) 908 return; 909 910 BAutolock _(fModelLock); 911 912 // create the row for this package 913 packageRow = new PackageRow(package, fPackageListener); 914 915 // add the row, parent may be NULL (add at top level) 916 AddRow(packageRow); 917 918 // add to hash table for quick lookup of row by package name 919 fRowByNameTable->Insert(packageRow); 920 921 // make sure the row is initially expanded 922 ExpandOrCollapse(packageRow, true); 923 924 fItemCountView->SetItemCount(CountRows()); 925 } 926 927 928 void 929 PackageListView::RemovePackage(const PackageInfoRef& package) 930 { 931 PackageRow* packageRow = _FindRow(package); 932 if (packageRow == NULL) 933 return; 934 935 fRowByNameTable->Remove(packageRow); 936 937 RemoveRow(packageRow); 938 delete packageRow; 939 940 fItemCountView->SetItemCount(CountRows()); 941 } 942 943 944 void 945 PackageListView::SelectPackage(const PackageInfoRef& package) 946 { 947 PackageRow* row = _FindRow(package); 948 BRow* selected = CurrentSelection(); 949 if (row != selected) 950 DeselectAll(); 951 if (row != NULL) { 952 AddToSelection(row); 953 SetFocusRow(row, false); 954 ScrollTo(row); 955 } 956 } 957 958 959 void 960 PackageListView::AttachWorkStatusView(WorkStatusView* view) 961 { 962 fWorkStatusView = view; 963 } 964 965 966 PackageRow* 967 PackageListView::_FindRow(const PackageInfoRef& package) 968 { 969 if (package.Get() == NULL) 970 return NULL; 971 return fRowByNameTable->Lookup(package->Name().String()); 972 } 973 974 975 PackageRow* 976 PackageListView::_FindRow(const BString& packageName) 977 { 978 return fRowByNameTable->Lookup(packageName.String()); 979 } 980