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