1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 // Icon cache is used for drawing node icons; it caches icons 36 // and reuses them for successive draws 37 38 // 39 // Possible performance improvements: 40 // - Mime API requires BBitmaps to retrieve bits and successive 41 // SetBits that cause app server contention 42 // Consider having special purpose "give me just the bits" calls 43 // to deal with that. 44 // - Related to this, node cache entries would only store the raw bits 45 // to cut down on number of BBitmaps and related overhead 46 // - Make the cache miss and fill case for the shared cache reuse the 47 // already calculated hash value 48 // 49 // Other ToDo items: 50 // Use lazily allocated bitmap arrays for every view for node icon cache 51 // drawing 52 // Have an overflow list for deleting shared icons, delete from the list 53 // every now and then 54 55 56 // Actual icon lookup sequence: 57 // icon from node 58 // preferred app for node -> icon for type 59 // preferred app for type -> icon for type 60 // metamime -> icon for type 61 // preferred app for supertype -> icon for type 62 // supertype metamime -> icon for type 63 // generic icon 64 65 66 #include <Debug.h> 67 #include <Screen.h> 68 #include <Volume.h> 69 #include <fs_info.h> 70 71 #include "Bitmaps.h" 72 #include "FSUtils.h" 73 #include "IconCache.h" 74 #include "MimeTypes.h" 75 #include "Model.h" 76 77 #if DEBUG 78 // #define LOG_DISK_HITS 79 // the LOG_DISK_HITS define is used to check that the disk is not hit more 80 // than needed - enable it, open a window with a bunch of poses, force 81 // it to redraw, shouldn't recache 82 // #define LOG_ADD_ITEM 83 #endif 84 85 // set up a few printing macros to get rid of a ton of debugging ifdefs in the code 86 #ifdef LOG_DISK_HITS 87 #define PRINT_DISK_HITS(ARGS) _debugPrintf ARGS 88 #else 89 #define PRINT_DISK_HITS(ARGS) (void)0 90 #endif 91 92 #ifdef LOG_ADD_ITEM 93 #define PRINT_ADD_ITEM(ARGS) _debugPrintf ARGS 94 #else 95 #define PRINT_ADD_ITEM(ARGS) (void)0 96 #endif 97 98 #undef NODE_CACHE_ASYNC_DRAWS 99 100 101 IconCacheEntry::IconCacheEntry() 102 : fLargeIcon(NULL), 103 fMiniIcon(NULL), 104 fHilitedLargeIcon(NULL), 105 fHilitedMiniIcon(NULL), 106 fAliasForIndex(-1) 107 { 108 } 109 110 111 IconCacheEntry::~IconCacheEntry() 112 { 113 if (fAliasForIndex < 0) { 114 delete fLargeIcon; 115 delete fMiniIcon; 116 delete fHilitedLargeIcon; 117 delete fHilitedMiniIcon; 118 119 // clean up a bit to leave the hash table entry in an initialized state 120 fLargeIcon = NULL; 121 fMiniIcon = NULL; 122 fHilitedLargeIcon = NULL; 123 fHilitedMiniIcon = NULL; 124 125 } 126 fAliasForIndex = -1; 127 } 128 129 130 void 131 IconCacheEntry::SetAliasFor(const SharedIconCache* sharedCache, 132 const SharedCacheEntry* entry) 133 { 134 sharedCache->SetAliasFor(this, entry); 135 ASSERT(fAliasForIndex >= 0); 136 } 137 138 139 IconCacheEntry* 140 IconCacheEntry::ResolveIfAlias(const SharedIconCache* sharedCache) 141 { 142 return sharedCache->ResolveIfAlias(this); 143 } 144 145 146 IconCacheEntry* 147 IconCacheEntry::ResolveIfAlias(const SharedIconCache* sharedCache, 148 IconCacheEntry* entry) 149 { 150 if (!entry) 151 return NULL; 152 153 return sharedCache->ResolveIfAlias(entry); 154 } 155 156 157 bool 158 IconCacheEntry::CanConstructBitmap(IconDrawMode mode, icon_size) const 159 { 160 if (mode == kSelected) 161 // for now only 162 return true; 163 164 return false; 165 } 166 167 168 bool 169 IconCacheEntry::HaveIconBitmap(IconDrawMode mode, icon_size size) const 170 { 171 ASSERT(mode == kSelected || mode == kNormalIcon); 172 // for now only 173 174 if (mode == kNormalIcon) { 175 if (size == B_MINI_ICON) 176 return fMiniIcon != NULL; 177 else 178 return fLargeIcon != NULL 179 && fLargeIcon->Bounds().IntegerWidth() + 1 == size; 180 } else if (mode == kSelected) { 181 if (size == B_MINI_ICON) 182 return fHilitedMiniIcon != NULL; 183 else 184 return fHilitedLargeIcon != NULL 185 && fHilitedLargeIcon->Bounds().IntegerWidth() + 1 == size; 186 } 187 return false; 188 } 189 190 191 BBitmap* 192 IconCacheEntry::IconForMode(IconDrawMode mode, icon_size size) const 193 { 194 ASSERT(mode == kSelected || mode == kNormalIcon); 195 // for now only 196 197 if (mode == kNormalIcon) { 198 if (size == B_MINI_ICON) 199 return fMiniIcon; 200 else 201 return fLargeIcon; 202 } else if (mode == kSelected) { 203 if (size == B_MINI_ICON) 204 return fHilitedMiniIcon; 205 else 206 return fHilitedLargeIcon; 207 } 208 return NULL; 209 } 210 211 212 bool 213 IconCacheEntry::IconHitTest(BPoint where, IconDrawMode mode, icon_size size) const 214 { 215 ASSERT(where.x < size && where.y < size); 216 BBitmap* bitmap = IconForMode(mode, size); 217 if (!bitmap) 218 return false; 219 220 uchar* bits = (uchar*)bitmap->Bits(); 221 ASSERT(bits); 222 223 BRect bounds(bitmap->Bounds()); 224 bounds.InsetBy((bounds.Width() + 1.0) / 8.0, (bounds.Height() + 1.0) / 8.0); 225 if (bounds.Contains(where)) 226 return true; 227 228 switch (bitmap->ColorSpace()) { 229 case B_RGBA32: 230 // test alpha channel 231 return *(bits + (int32)(floorf(where.y) * bitmap->BytesPerRow() 232 + floorf(where.x) * 4 + 3)) > 20; 233 234 case B_CMAP8: 235 return *(bits + (int32)(floorf(where.y) * size + where.x)) 236 != B_TRANSPARENT_8_BIT; 237 238 default: 239 return true; 240 } 241 } 242 243 244 BBitmap* 245 IconCacheEntry::ConstructBitmap(BBitmap* constructFrom, 246 IconDrawMode requestedMode, IconDrawMode constructFromMode, 247 icon_size size, LazyBitmapAllocator* lazyBitmap) 248 { 249 ASSERT(requestedMode == kSelected && constructFromMode == kNormalIcon); 250 // for now 251 252 if (requestedMode == kSelected && constructFromMode == kNormalIcon) { 253 return IconCache::sIconCache->MakeSelectedIcon(constructFrom, size, 254 lazyBitmap); 255 } 256 257 return NULL; 258 } 259 260 261 BBitmap* 262 IconCacheEntry::ConstructBitmap(IconDrawMode requestedMode, icon_size size, 263 LazyBitmapAllocator* lazyBitmap) 264 { 265 BBitmap* source = (size == B_MINI_ICON) ? fMiniIcon : fLargeIcon; 266 ASSERT(source); 267 return ConstructBitmap(source, requestedMode, kNormalIcon, size, 268 lazyBitmap); 269 } 270 271 272 bool 273 IconCacheEntry::AlternateModeForIconConstructing(IconDrawMode requestedMode, 274 IconDrawMode &alternate, icon_size) 275 { 276 if (requestedMode & kSelected) { 277 // for now 278 alternate = kNormalIcon; 279 return true; 280 } 281 return false; 282 } 283 284 285 void 286 IconCacheEntry::SetIcon(BBitmap* bitmap, IconDrawMode mode, icon_size size, 287 bool /*create*/) 288 { 289 if (mode == kNormalIcon) { 290 if (size == B_MINI_ICON) 291 fMiniIcon = bitmap; 292 else 293 fLargeIcon = bitmap; 294 } else if (mode == kSelectedIcon) { 295 if (size == B_MINI_ICON) 296 fHilitedMiniIcon = bitmap; 297 else 298 fHilitedLargeIcon = bitmap; 299 } else 300 TRESPASS(); 301 } 302 303 304 IconCache::IconCache() 305 : fInitHiliteTable(true) 306 { 307 InitHiliteTable(); 308 } 309 310 311 // The following calls use the icon lookup sequence node-prefered app for 312 // node-metamime-preferred app for metamime to find an icon; 313 // if we are trying to get a specialized icon, we will first look for a normal 314 // icon in each of the locations, if we get a hit, we look for the 315 // specialized, if we don't find one, we try to auto-construct one, if we 316 // can't we assume the icon is not available for now the code only looks for 317 // normal icons, selected icons are auto-generated 318 IconCacheEntry* 319 IconCache::GetIconForPreferredApp(const char* fileTypeSignature, 320 const char* preferredApp, IconDrawMode mode, icon_size size, 321 LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry) 322 { 323 ASSERT(fSharedCache.IsLocked()); 324 325 if (!preferredApp[0]) 326 return NULL; 327 328 if (!entry) { 329 entry = fSharedCache.FindItem(fileTypeSignature, preferredApp); 330 if (entry) { 331 entry = entry->ResolveIfAlias(&fSharedCache, entry); 332 #if xDEBUG 333 PRINT(("File %s; Line %d # looking for %s, type %s, found %x\n", 334 __FILE__, __LINE__, preferredApp, fileTypeSignature, entry)); 335 #endif 336 if (entry->HaveIconBitmap(mode, size)) 337 return entry; 338 } 339 } 340 341 if (!entry || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 342 343 PRINT_DISK_HITS( 344 ("File %s; Line %d # hitting disk for preferredApp %s, type %s\n", 345 __FILE__, __LINE__, preferredApp, fileTypeSignature)); 346 347 BMimeType preferredAppType(preferredApp); 348 BString signature(fileTypeSignature); 349 signature.ToLower(); 350 if (preferredAppType.GetIconForType(signature.String(), 351 lazyBitmap->Get(), size) != B_OK) { 352 return NULL; 353 } 354 355 BBitmap* bitmap = lazyBitmap->Adopt(); 356 if (!entry) { 357 PRINT_ADD_ITEM( 358 ("File %s; Line %d # adding entry for preferredApp %s, " 359 "type %s\n", __FILE__, __LINE__, preferredApp, 360 fileTypeSignature)); 361 entry = fSharedCache.AddItem(fileTypeSignature, preferredApp); 362 } 363 entry->SetIcon(bitmap, kNormalIcon, size); 364 } 365 366 if (mode != kNormalIcon 367 && entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 368 entry->ConstructBitmap(mode, size, lazyBitmap); 369 entry->SetIcon(lazyBitmap->Adopt(), mode, size); 370 } 371 372 return entry; 373 } 374 375 376 IconCacheEntry* 377 IconCache::GetIconFromMetaMime(const char* fileType, IconDrawMode mode, 378 icon_size size, LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry) 379 { 380 ASSERT(fSharedCache.IsLocked()); 381 382 if (!entry) 383 entry = fSharedCache.FindItem(fileType); 384 385 if (entry) { 386 entry = entry->ResolveIfAlias(&fSharedCache, entry); 387 // metamime defines an icon and we have it cached 388 if (entry->HaveIconBitmap(mode, size)) 389 return entry; 390 } 391 392 if (!entry || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 393 PRINT_DISK_HITS(("File %s; Line %d # hitting disk for metamime %s\n", 394 __FILE__, __LINE__, fileType)); 395 396 BMimeType mime(fileType); 397 // try getting the icon directly from the metamime 398 if (mime.GetIcon(lazyBitmap->Get(), size) != B_OK) { 399 // try getting it from the preferred app of this type 400 char preferredAppSig[B_MIME_TYPE_LENGTH]; 401 if (mime.GetPreferredApp(preferredAppSig) != B_OK) 402 return NULL; 403 404 SharedCacheEntry* aliasTo = NULL; 405 if (entry) { 406 aliasTo 407 = (SharedCacheEntry*)entry->ResolveIfAlias(&fSharedCache); 408 } 409 410 // look for icon defined by preferred app from metamime 411 aliasTo = (SharedCacheEntry*)GetIconForPreferredApp(fileType, 412 preferredAppSig, mode, size, lazyBitmap, aliasTo); 413 414 if (aliasTo == NULL) 415 return NULL; 416 417 // make an aliased entry so that the next time we get a 418 // hit on the first FindItem in here 419 if (!entry) { 420 PRINT_ADD_ITEM( 421 ("File %s; Line %d # adding entry as alias for type %s\n", 422 __FILE__, __LINE__, fileType)); 423 entry = fSharedCache.AddItem(&aliasTo, fileType); 424 entry->SetAliasFor(&fSharedCache, aliasTo); 425 } 426 ASSERT(aliasTo->HaveIconBitmap(mode, size)); 427 return aliasTo; 428 } 429 430 // at this point, we've found an icon for the MIME type 431 BBitmap* bitmap = lazyBitmap->Adopt(); 432 if (!entry) { 433 PRINT_ADD_ITEM(("File %s; Line %d # adding entry for type %s\n", 434 __FILE__, __LINE__, fileType)); 435 entry = fSharedCache.AddItem(fileType); 436 } 437 entry->SetIcon(bitmap, kNormalIcon, size); 438 } 439 440 ASSERT(entry); 441 if (mode != kNormalIcon 442 && entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 443 entry->ConstructBitmap(mode, size, lazyBitmap); 444 entry->SetIcon(lazyBitmap->Adopt(), mode, size); 445 } 446 447 #if xDEBUG 448 if (!entry->HaveIconBitmap(mode, size)) 449 PRINT(("failing on %s, mode %ld, size %ld\n", fileType, mode, size)); 450 #endif 451 452 ASSERT(entry->HaveIconBitmap(mode, size)); 453 return entry; 454 } 455 456 457 IconCacheEntry* 458 IconCache::GetIconFromFileTypes(ModelNodeLazyOpener* modelOpener, 459 IconSource &source, IconDrawMode mode, icon_size size, 460 LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry) 461 { 462 ASSERT(fSharedCache.IsLocked()); 463 // use file types to get the icon 464 Model* model = modelOpener->TargetModel(); 465 466 const char* fileType = model->MimeType(); 467 const char* nodePreferredApp = model->PreferredAppSignature(); 468 if (source == kUnknownSource || source == kUnknownNotFromNode 469 || source == kPreferredAppForNode) { 470 471 if (nodePreferredApp[0]) { 472 // file has a locally set preferred app, try getting an icon from 473 // there 474 entry = GetIconForPreferredApp(fileType, nodePreferredApp, mode, 475 size, lazyBitmap, entry); 476 #if xDEBUG 477 PRINT(("File %s; Line %d # looking for %s, type %s, found %x\n", 478 __FILE__, __LINE__, nodePreferredApp, fileType, entry)); 479 #endif 480 if (entry) { 481 source = kPreferredAppForNode; 482 ASSERT(entry->HaveIconBitmap(mode, size)); 483 return entry; 484 } 485 } 486 if (source == kPreferredAppForNode) 487 source = kUnknownSource; 488 } 489 490 entry = GetIconFromMetaMime(fileType, mode, size, lazyBitmap, entry); 491 if (!entry) { 492 // Try getting a supertype handler icon 493 BMimeType mime(fileType); 494 if (!mime.IsSupertypeOnly()) { 495 BMimeType superType; 496 mime.GetSupertype(&superType); 497 const char* superTypeFileType = superType.Type(); 498 if (superTypeFileType) 499 entry = GetIconFromMetaMime(superTypeFileType, mode, size, 500 lazyBitmap, entry); 501 #if DEBUG 502 else 503 PRINT( 504 ("File %s; Line %d # failed to get supertype for " 505 "type %s\n", __FILE__, __LINE__, fileType)); 506 #endif 507 } 508 } 509 ASSERT(!entry || entry->HaveIconBitmap(mode, size)); 510 if (entry) { 511 if (nodePreferredApp[0]) { 512 // we got a miss using GetIconForPreferredApp before, cache this 513 // fileType/preferredApp combo with an aliased entry 514 515 // make an aliased entry so that the next time we get a 516 // hit and substitute a generic icon right away 517 518 PRINT_ADD_ITEM( 519 ("File %s; Line %d # adding entry as alias for " 520 "preferredApp %s, type %s\n", 521 __FILE__, __LINE__, nodePreferredApp, fileType)); 522 IconCacheEntry* aliasedEntry 523 = fSharedCache.AddItem((SharedCacheEntry**)&entry, fileType, 524 nodePreferredApp); 525 aliasedEntry->SetAliasFor(&fSharedCache, 526 (SharedCacheEntry*)entry); 527 // OK to cast here, have a runtime check 528 source = kPreferredAppForNode; 529 // set source as preferred for node, so that next time we 530 // get a hit in the initial find that uses 531 // GetIconForPreferredApp 532 } else 533 source = kMetaMime; 534 #if DEBUG 535 if (!entry->HaveIconBitmap(mode, size)) 536 model->PrintToStream(); 537 #endif 538 ASSERT(entry->HaveIconBitmap(mode, size)); 539 } 540 return entry; 541 } 542 543 IconCacheEntry* 544 IconCache::GetVolumeIcon(AutoLock<SimpleIconCache>*nodeCacheLocker, 545 AutoLock<SimpleIconCache>* sharedCacheLocker, 546 AutoLock<SimpleIconCache>** resultingOpenCache, 547 Model* model, IconSource &source, 548 IconDrawMode mode, icon_size size, LazyBitmapAllocator* lazyBitmap) 549 { 550 *resultingOpenCache = nodeCacheLocker; 551 nodeCacheLocker->Lock(); 552 553 IconCacheEntry* entry = 0; 554 if (source != kUnknownSource) { 555 // cached in the node cache 556 entry = fNodeCache.FindItem(model->NodeRef()); 557 if (entry) { 558 entry = IconCacheEntry::ResolveIfAlias(&fSharedCache, entry); 559 560 if (source == kTrackerDefault) { 561 // if tracker default, resolved entry is from shared cache 562 // this could be done a little cleaner if entry had a way to 563 // reach the cache it is in 564 *resultingOpenCache = sharedCacheLocker; 565 sharedCacheLocker->Lock(); 566 } 567 568 if (entry->HaveIconBitmap(mode, size)) 569 return entry; 570 } 571 } 572 573 // try getting using the BVolume::GetIcon call; if miss, 574 // go for the default mime based icon 575 if (!entry || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 576 BVolume volume(model->NodeRef()->device); 577 578 if (volume.IsShared()) { 579 // Check if it's a network share and give it a special icon 580 BBitmap* bitmap = lazyBitmap->Get(); 581 GetTrackerResources()->GetIconResource(R_ShareIcon, size, bitmap); 582 if (!entry) { 583 PRINT_ADD_ITEM( 584 ("File %s; Line %d # adding entry for model %s\n", 585 __FILE__, __LINE__, model->Name())); 586 entry = fNodeCache.AddItem(model->NodeRef()); 587 } 588 entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size); 589 } else if (volume.GetIcon(lazyBitmap->Get(), size) == B_OK) { 590 // Ask the device for an icon 591 BBitmap* bitmap = lazyBitmap->Adopt(); 592 ASSERT(bitmap); 593 if (!entry) { 594 PRINT_ADD_ITEM( 595 ("File %s; Line %d # adding entry for model %s\n", 596 __FILE__, __LINE__, model->Name())); 597 entry = fNodeCache.AddItem(model->NodeRef()); 598 } 599 ASSERT(entry); 600 entry->SetIcon(bitmap, kNormalIcon, size); 601 source = kVolume; 602 } else { 603 *resultingOpenCache = sharedCacheLocker; 604 sharedCacheLocker->Lock(); 605 606 // If the volume doesnt have a device it should have 607 // the generic icon 608 entry = GetIconFromMetaMime(B_VOLUME_MIMETYPE, mode, 609 size, lazyBitmap, entry); 610 } 611 } 612 613 if (mode != kNormalIcon 614 && entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 615 entry->ConstructBitmap(mode, size, lazyBitmap); 616 entry->SetIcon(lazyBitmap->Adopt(), mode, size); 617 } 618 return entry; 619 } 620 621 622 IconCacheEntry* 623 IconCache::GetRootIcon(AutoLock<SimpleIconCache>*, 624 AutoLock<SimpleIconCache>* sharedCacheLocker, 625 AutoLock<SimpleIconCache>** resultingOpenCache, 626 Model*, IconSource &source, IconDrawMode mode, 627 icon_size size, LazyBitmapAllocator* lazyBitmap) 628 { 629 *resultingOpenCache = sharedCacheLocker; 630 (*resultingOpenCache)->Lock(); 631 632 source = kTrackerSupplied; 633 return GetIconFromMetaMime(B_ROOT_MIMETYPE, mode, size, lazyBitmap, 0); 634 } 635 636 637 IconCacheEntry* 638 IconCache::GetWellKnownIcon(AutoLock<SimpleIconCache>*, 639 AutoLock<SimpleIconCache>* sharedCacheLocker, 640 AutoLock<SimpleIconCache>** resultingOpenCache, 641 Model* model, IconSource &source, IconDrawMode mode, icon_size size, 642 LazyBitmapAllocator* lazyBitmap) 643 { 644 const WellKnowEntryList::WellKnownEntry* wellKnownEntry 645 = WellKnowEntryList::MatchEntry(model->NodeRef()); 646 if (!wellKnownEntry) 647 return NULL; 648 649 IconCacheEntry* entry = NULL; 650 651 BString type("tracker/active_"); 652 type += wellKnownEntry->name; 653 654 *resultingOpenCache = sharedCacheLocker; 655 (*resultingOpenCache)->Lock(); 656 657 source = kTrackerSupplied; 658 659 entry = fSharedCache.FindItem(type.String()); 660 if (entry) { 661 entry = entry->ResolveIfAlias(&fSharedCache, entry); 662 if (entry->HaveIconBitmap(mode, size)) 663 return entry; 664 } 665 666 if (!entry || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 667 // match up well known entries in the file system with specialized 668 // icons stored in Tracker's resources 669 int32 resid = -1; 670 switch ((uint32)wellKnownEntry->which) { 671 case B_BOOT_DISK: 672 resid = R_BootVolumeIcon; 673 break; 674 675 case B_BEOS_DIRECTORY: 676 resid = R_BeosFolderIcon; 677 break; 678 679 case B_USER_DIRECTORY: 680 resid = R_HomeDirIcon; 681 break; 682 683 case B_BEOS_FONTS_DIRECTORY: 684 case B_COMMON_FONTS_DIRECTORY: 685 case B_USER_FONTS_DIRECTORY: 686 resid = R_FontDirIcon; 687 break; 688 689 case B_BEOS_APPS_DIRECTORY: 690 case B_APPS_DIRECTORY: 691 case B_USER_DESKBAR_APPS_DIRECTORY: 692 resid = R_AppsDirIcon; 693 break; 694 695 case B_BEOS_PREFERENCES_DIRECTORY: 696 case B_PREFERENCES_DIRECTORY: 697 case B_USER_DESKBAR_PREFERENCES_DIRECTORY: 698 resid = R_PrefsDirIcon; 699 break; 700 701 case B_USER_MAIL_DIRECTORY: 702 resid = R_MailDirIcon; 703 break; 704 705 case B_USER_QUERIES_DIRECTORY: 706 resid = R_QueryDirIcon; 707 break; 708 709 case B_COMMON_DEVELOP_DIRECTORY: 710 case B_USER_DESKBAR_DEVELOP_DIRECTORY: 711 resid = R_DevelopDirIcon; 712 break; 713 714 case B_USER_CONFIG_DIRECTORY: 715 resid = R_ConfigDirIcon; 716 break; 717 718 case B_USER_PEOPLE_DIRECTORY: 719 resid = R_PersonDirIcon; 720 break; 721 722 case B_USER_DOWNLOADS_DIRECTORY: 723 resid = R_DownloadDirIcon; 724 break; 725 726 default: 727 return NULL; 728 } 729 730 entry = fSharedCache.AddItem(type.String()); 731 732 BBitmap* bitmap = lazyBitmap->Get(); 733 GetTrackerResources()->GetIconResource(resid, size, bitmap); 734 entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size); 735 } 736 737 if (mode != kNormalIcon 738 && entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 739 entry->ConstructBitmap(mode, size, lazyBitmap); 740 entry->SetIcon(lazyBitmap->Adopt(), mode, size); 741 } 742 743 ASSERT(entry->HaveIconBitmap(mode, size)); 744 return entry; 745 } 746 747 748 IconCacheEntry* 749 IconCache::GetNodeIcon(ModelNodeLazyOpener* modelOpener, 750 AutoLock<SimpleIconCache>* nodeCacheLocker, 751 AutoLock<SimpleIconCache>** resultingOpenCache, 752 Model* model, IconSource &source, 753 IconDrawMode mode, icon_size size, 754 LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry, bool permanent) 755 { 756 *resultingOpenCache = nodeCacheLocker; 757 (*resultingOpenCache)->Lock(); 758 759 entry = fNodeCache.FindItem(model->NodeRef()); 760 if (!entry || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 761 modelOpener->OpenNode(); 762 763 BFile* file = NULL; 764 765 // if we are dealing with an application, use the BAppFileInfo 766 // superset of node; this makes GetIcon grab the proper icon for 767 // an app 768 if (model->IsExecutable()) 769 file = dynamic_cast<BFile*>(model->Node()); 770 771 PRINT_DISK_HITS(("File %s; Line %d # hitting disk for node %s\n", 772 __FILE__, __LINE__, model->Name())); 773 774 status_t result; 775 if (file) 776 result = GetAppIconFromAttr(file, lazyBitmap->Get(), size); 777 else { 778 result = GetFileIconFromAttr(model->Node(), lazyBitmap->Get(), 779 size); 780 } 781 782 if (result == B_OK) { 783 // node has it's own icon, use it 784 785 BBitmap* bitmap = lazyBitmap->Adopt(); 786 PRINT_ADD_ITEM(("File %s; Line %d # adding entry for model %s\n", 787 __FILE__, __LINE__, model->Name())); 788 entry = fNodeCache.AddItem(model->NodeRef(), permanent); 789 ASSERT(entry); 790 entry->SetIcon(bitmap, kNormalIcon, size); 791 if (mode != kNormalIcon) { 792 entry->ConstructBitmap(mode, size, lazyBitmap); 793 entry->SetIcon(lazyBitmap->Adopt(), mode, size); 794 } 795 source = kNode; 796 } 797 } 798 799 if (!entry) { 800 (*resultingOpenCache)->Unlock(); 801 *resultingOpenCache = NULL; 802 } else if (!entry->HaveIconBitmap(mode, size) 803 && entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 804 entry->ConstructBitmap(mode, size, lazyBitmap); 805 entry->SetIcon(lazyBitmap->Adopt(), mode, size); 806 ASSERT(entry->HaveIconBitmap(mode, size)); 807 } 808 809 return entry; 810 } 811 812 813 IconCacheEntry* 814 IconCache::GetGenericIcon(AutoLock<SimpleIconCache>* sharedCacheLocker, 815 AutoLock<SimpleIconCache>** resultingOpenCache, 816 Model* model, IconSource &source, 817 IconDrawMode mode, icon_size size, 818 LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry) 819 { 820 *resultingOpenCache = sharedCacheLocker; 821 (*resultingOpenCache)->Lock(); 822 823 entry = GetIconFromMetaMime(B_FILE_MIMETYPE, mode, 824 size, lazyBitmap, 0); 825 826 if (!entry) 827 return NULL; 828 829 // make an aliased entry so that the next time we get a 830 // hit and substitute a generic icon right away 831 PRINT_ADD_ITEM( 832 ("File %s; Line %d # adding entry for preferredApp %s, type %s\n", 833 __FILE__, __LINE__, model->PreferredAppSignature(), 834 model->MimeType())); 835 IconCacheEntry* aliasedEntry = fSharedCache.AddItem( 836 (SharedCacheEntry**)&entry, model->MimeType(), 837 model->PreferredAppSignature()); 838 aliasedEntry->SetAliasFor(&fSharedCache, (SharedCacheEntry*)entry); 839 840 source = kMetaMime; 841 842 ASSERT(entry->HaveIconBitmap(mode, size)); 843 return entry; 844 } 845 846 847 IconCacheEntry* 848 IconCache::GetFallbackIcon(AutoLock<SimpleIconCache>* sharedCacheLocker, 849 AutoLock<SimpleIconCache>** resultingOpenCache, 850 Model* model, IconDrawMode mode, icon_size size, 851 LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry) 852 { 853 *resultingOpenCache = sharedCacheLocker; 854 (*resultingOpenCache)->Lock(); 855 856 entry = fSharedCache.AddItem(model->MimeType(), 857 model->PreferredAppSignature()); 858 859 BBitmap* bitmap = lazyBitmap->Get(); 860 GetTrackerResources()->GetIconResource(R_FileIcon, size, bitmap); 861 entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size); 862 863 if (mode != kNormalIcon) { 864 entry->ConstructBitmap(mode, size, lazyBitmap); 865 entry->SetIcon(lazyBitmap->Adopt(), mode, size); 866 } 867 868 ASSERT(entry->HaveIconBitmap(mode, size)); 869 return entry; 870 } 871 872 873 IconCacheEntry* 874 IconCache::Preload(AutoLock<SimpleIconCache>* nodeCacheLocker, 875 AutoLock<SimpleIconCache>* sharedCacheLocker, 876 AutoLock<SimpleIconCache>** resultingCache, 877 Model* model, IconDrawMode mode, icon_size size, 878 bool permanent) 879 { 880 IconCacheEntry* entry = NULL; 881 882 AutoLock<SimpleIconCache>* resultingOpenCache = NULL; 883 // resultingOpenCache is the locker that points to the cache that 884 // ended with a hit and will be used for the drawing 885 886 { // scope for modelOpener 887 888 ModelNodeLazyOpener modelOpener(model); 889 // this opener takes care of opening the model and possibly 890 // closing it when we are done 891 LazyBitmapAllocator lazyBitmap(size); 892 // lazyBitmap manages bitmap allocation and freeing if needed 893 894 IconSource source = model->IconFrom(); 895 if (source == kUnknownSource || source == kUnknownNotFromNode) { 896 897 // fish for special first models and handle them appropriately 898 if (model->IsVolume()) { 899 // volume may use specialized icon in the volume node 900 entry = GetNodeIcon(&modelOpener, nodeCacheLocker, 901 &resultingOpenCache, model, source, mode, size, 902 &lazyBitmap, entry, permanent); 903 904 if (!entry || !entry->HaveIconBitmap(mode, size)) 905 // look for volume defined icon 906 entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker, 907 &resultingOpenCache, model, source, mode, 908 size, &lazyBitmap); 909 910 } else if (model->IsRoot()) { 911 912 entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker, 913 &resultingOpenCache, model, source, mode, size, &lazyBitmap); 914 ASSERT(entry); 915 916 } else { 917 if (source == kUnknownSource) 918 // look for node icons first 919 entry = GetNodeIcon(&modelOpener, nodeCacheLocker, 920 &resultingOpenCache, model, source, 921 mode, size, &lazyBitmap, entry, permanent); 922 923 924 if (!entry) { 925 // no node icon, look for file type based one 926 modelOpener.OpenNode(); 927 // use file types to get the icon 928 resultingOpenCache = sharedCacheLocker; 929 resultingOpenCache->Lock(); 930 931 entry = GetIconFromFileTypes(&modelOpener, source, mode, size, 932 &lazyBitmap, 0); 933 934 if (!entry) // we don't have an icon, go with the generic 935 entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache, 936 model, source, mode, size, &lazyBitmap, entry); 937 } 938 } 939 940 // update the icon source 941 model->SetIconFrom(source); 942 } else { 943 // we already know where the icon should come from, 944 // use shortcuts to get it 945 switch (source) { 946 case kNode: 947 resultingOpenCache = nodeCacheLocker; 948 resultingOpenCache->Lock(); 949 950 entry = GetNodeIcon(&modelOpener, nodeCacheLocker, 951 &resultingOpenCache, model, source, mode, 952 size, &lazyBitmap, entry, permanent); 953 954 if (entry) { 955 entry = IconCacheEntry::ResolveIfAlias(&fSharedCache, entry); 956 if (!entry->HaveIconBitmap(mode, size) 957 && entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) { 958 entry->ConstructBitmap(mode, size, &lazyBitmap); 959 entry->SetIcon(lazyBitmap.Adopt(), mode, size); 960 } 961 ASSERT(entry->HaveIconBitmap(mode, size)); 962 } 963 break; 964 case kTrackerSupplied: 965 if (model->IsRoot()) { 966 entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker, 967 &resultingOpenCache, model, source, mode, size, 968 &lazyBitmap); 969 break; 970 } else { 971 entry = GetWellKnownIcon(nodeCacheLocker, sharedCacheLocker, 972 &resultingOpenCache, model, source, mode, size, 973 &lazyBitmap); 974 975 if (entry) 976 break; 977 } 978 // fall through 979 case kTrackerDefault: 980 case kVolume: 981 if (model->IsVolume()) { 982 entry = GetNodeIcon(&modelOpener, nodeCacheLocker, 983 &resultingOpenCache, model, source, 984 mode, size, &lazyBitmap, entry, permanent); 985 if (!entry || !entry->HaveIconBitmap(mode, size)) 986 entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker, 987 &resultingOpenCache, model, source, mode, size, 988 &lazyBitmap); 989 break; 990 } 991 // fall through 992 case kMetaMime: 993 case kPreferredAppForType: 994 case kPreferredAppForNode: 995 resultingOpenCache = sharedCacheLocker; 996 resultingOpenCache->Lock(); 997 998 entry = GetIconFromFileTypes(&modelOpener, source, mode, size, 999 &lazyBitmap, 0); 1000 ASSERT(!entry || entry->HaveIconBitmap(mode, size)); 1001 1002 if (!entry || !entry->HaveIconBitmap(mode, size)) 1003 // we don't have an icon, go with the generic 1004 entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache, 1005 model, source, mode, size, &lazyBitmap, entry); 1006 1007 model->SetIconFrom(source); 1008 // the source shouldn't change in this case; if it does though we 1009 // might never be hitting the correct icon and instead keep leaking 1010 // entries after each miss 1011 // this now happens if an app defines an icon but a GetIconForType 1012 // fails and we fall back to generic icon 1013 // ToDo: 1014 // fix this and add an assert to the effect 1015 1016 ASSERT(entry); 1017 ASSERT(entry->HaveIconBitmap(mode, size)); 1018 break; 1019 1020 default: 1021 TRESPASS(); 1022 } 1023 } 1024 1025 if (!entry || !entry->HaveIconBitmap(mode, size)) { 1026 // we don't have an icon, go with the generic 1027 PRINT( 1028 ("icon cache complete miss, falling back on generic icon " 1029 "for %s\n", model->Name())); 1030 entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache, 1031 model, source, mode, size, &lazyBitmap, entry); 1032 1033 // we don't even have generic, something is really broken, 1034 // go with hardcoded generic icon 1035 if (!entry || !entry->HaveIconBitmap(mode, size)) { 1036 PRINT( 1037 ("icon cache complete miss, falling back on generic " 1038 "icon for %s\n", model->Name())); 1039 entry = GetFallbackIcon(sharedCacheLocker, 1040 &resultingOpenCache, model, mode, size, &lazyBitmap, 1041 entry); 1042 } 1043 1044 // force icon pick up next time around because we probably just 1045 // hit a node in transition 1046 model->SetIconFrom(kUnknownSource); 1047 } 1048 } 1049 1050 ASSERT(entry && entry->HaveIconBitmap(mode, size)); 1051 1052 if (resultingCache) 1053 *resultingCache = resultingOpenCache; 1054 1055 return entry; 1056 } 1057 1058 1059 void 1060 IconCache::Draw(Model* model, BView* view, BPoint where, IconDrawMode mode, 1061 icon_size size, bool async) 1062 { 1063 // the following does not actually lock the caches, we are using the 1064 // lockLater mode; we will decide which of the two to lock down depending 1065 // on where we get the icon from 1066 AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false); 1067 AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false); 1068 1069 AutoLock<SimpleIconCache>* resultingCacheLocker; 1070 IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker, 1071 &resultingCacheLocker, model, mode, size, false); 1072 // Preload finds/creates the appropriate entry, locking down the 1073 // cache it is in and returns the whole state back to here 1074 1075 if (!entry) 1076 return; 1077 1078 ASSERT(entry); 1079 ASSERT(entry->HaveIconBitmap(mode, size)); 1080 // got the entry, now draw it 1081 resultingCacheLocker->LockedItem()->Draw(entry, view, where, mode, 1082 size, async); 1083 1084 // either of the two cache lockers that got locked down by this call get 1085 // unlocked at this point 1086 } 1087 1088 1089 void 1090 IconCache::SyncDraw(Model* model, BView* view, BPoint where, 1091 IconDrawMode mode, icon_size size, 1092 void (*blitFunc)(BView*, BPoint, BBitmap*, void*), 1093 void* passThruState) 1094 { 1095 AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false); 1096 AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false); 1097 1098 AutoLock<SimpleIconCache>* resultingCacheLocker; 1099 IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker, 1100 &resultingCacheLocker, model, mode, size, false); 1101 1102 if (!entry) 1103 return; 1104 1105 ASSERT(entry); 1106 ASSERT(entry->HaveIconBitmap(mode, size)); 1107 resultingCacheLocker->LockedItem()->Draw(entry, view, where, 1108 mode, size, blitFunc, passThruState); 1109 } 1110 1111 1112 void 1113 IconCache::Preload(Model* model, IconDrawMode mode, icon_size size, 1114 bool permanent) 1115 { 1116 AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false); 1117 AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false); 1118 1119 Preload(&nodeCacheLocker, &sharedCacheLocker, 0, model, mode, size, 1120 permanent); 1121 } 1122 1123 1124 status_t 1125 IconCache::Preload(const char* fileType, IconDrawMode mode, icon_size size) 1126 { 1127 AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache); 1128 LazyBitmapAllocator lazyBitmap(size); 1129 1130 BMimeType mime(fileType); 1131 char preferredAppSig[B_MIME_TYPE_LENGTH]; 1132 status_t result = mime.GetPreferredApp(preferredAppSig); 1133 if (result != B_OK) 1134 return result; 1135 1136 // try getting the icon from the preferred app for the signature 1137 IconCacheEntry* entry = GetIconForPreferredApp(fileType, preferredAppSig, 1138 mode, size, &lazyBitmap, 0); 1139 if (entry) 1140 return B_OK; 1141 1142 // try getting the icon directly from the metamime 1143 result = mime.GetIcon(lazyBitmap.Get(), size); 1144 1145 if (result != B_OK) 1146 return result; 1147 1148 entry = fSharedCache.AddItem(fileType); 1149 BBitmap* bitmap = lazyBitmap.Adopt(); 1150 entry->SetIcon(bitmap, kNormalIcon, size); 1151 if (mode != kNormalIcon) { 1152 entry->ConstructBitmap(mode, size, &lazyBitmap); 1153 entry->SetIcon(lazyBitmap.Adopt(), mode, size); 1154 } 1155 1156 return B_OK; 1157 } 1158 1159 1160 void 1161 IconCache::Deleting(const Model* model) 1162 { 1163 AutoLock<SimpleIconCache> lock(&fNodeCache); 1164 1165 if (model->IconFrom() == kNode) 1166 fNodeCache.Deleting(model->NodeRef()); 1167 1168 // don't care if the node uses the shared cache 1169 } 1170 1171 1172 void 1173 IconCache::Removing(const Model* model) 1174 { 1175 AutoLock<SimpleIconCache> lock(&fNodeCache); 1176 1177 if (model->IconFrom() == kNode) 1178 fNodeCache.Removing(model->NodeRef()); 1179 } 1180 1181 1182 void 1183 IconCache::Deleting(const BView* view) 1184 { 1185 AutoLock<SimpleIconCache> lock(&fNodeCache); 1186 fNodeCache.Deleting(view); 1187 } 1188 1189 1190 void 1191 IconCache::IconChanged(Model* model) 1192 { 1193 AutoLock<SimpleIconCache> lock(&fNodeCache); 1194 1195 if (model->IconFrom() == kNode || model->IconFrom() == kVolume) 1196 fNodeCache.Deleting(model->NodeRef()); 1197 1198 model->ResetIconFrom(); 1199 } 1200 1201 1202 void 1203 IconCache::IconChanged(const char* mimeType, const char* appSignature) 1204 { 1205 AutoLock<SimpleIconCache> sharedLock(&fSharedCache); 1206 SharedCacheEntry* entry = fSharedCache.FindItem(mimeType, appSignature); 1207 if (!entry) 1208 return; 1209 1210 AutoLock<SimpleIconCache> nodeLock(&fNodeCache); 1211 1212 entry = (SharedCacheEntry*)fSharedCache.ResolveIfAlias(entry); 1213 ASSERT(entry); 1214 int32 index = fSharedCache.EntryIndex(entry); 1215 1216 fNodeCache.RemoveAliasesTo(index); 1217 fSharedCache.RemoveAliasesTo(index); 1218 1219 fSharedCache.IconChanged(entry); 1220 } 1221 1222 1223 BBitmap* 1224 IconCache::MakeSelectedIcon(const BBitmap* normal, icon_size size, 1225 LazyBitmapAllocator* lazyBitmap) 1226 { 1227 return MakeTransformedIcon(normal, size, fHiliteTable, lazyBitmap); 1228 } 1229 1230 #if xDEBUG 1231 1232 static void 1233 DumpBitmap(const BBitmap* bitmap) 1234 { 1235 if (!bitmap){ 1236 printf("NULL bitmap passed to DumpBitmap\n"); 1237 return; 1238 } 1239 int32 length = bitmap->BitsLength(); 1240 1241 printf("data length %ld \n", length); 1242 1243 int32 columns = (int32)bitmap->Bounds().Width() + 1; 1244 const unsigned char* bitPtr = (const unsigned char*)bitmap->Bits(); 1245 for (; length >= 0; length--) { 1246 for (int32 columnIndex = 0; columnIndex < columns; 1247 columnIndex++, length--) 1248 printf("%c%c", "0123456789ABCDEF"[(*bitPtr)/0x10], 1249 "0123456789ABCDEF"[(*bitPtr++)%0x10]); 1250 1251 printf("\n"); 1252 } 1253 printf("\n"); 1254 } 1255 1256 #endif 1257 1258 void 1259 IconCache::InitHiliteTable() 1260 { 1261 // build the color transform tables for different icon modes 1262 BScreen screen(B_MAIN_SCREEN_ID); 1263 rgb_color color; 1264 for (int32 index = 0; index < kColorTransformTableSize; index++) { 1265 color = screen.ColorForIndex((uchar)index); 1266 fHiliteTable[index] = screen.IndexForColor(tint_color(color, 1.3f)); 1267 } 1268 1269 fHiliteTable[B_TRANSPARENT_8_BIT] = B_TRANSPARENT_8_BIT; 1270 fInitHiliteTable = false; 1271 } 1272 1273 1274 BBitmap* 1275 IconCache::MakeTransformedIcon(const BBitmap* source, icon_size /*size*/, 1276 int32 colorTransformTable[], LazyBitmapAllocator* lazyBitmap) 1277 { 1278 if (fInitHiliteTable) 1279 InitHiliteTable(); 1280 1281 BBitmap* result = lazyBitmap->Get(); 1282 uint8* src = (uint8*)source->Bits(); 1283 uint8* dst = (uint8*)result->Bits(); 1284 1285 // ASSERT(result->ColorSpace() == source->ColorSpace() 1286 // && result->Bounds() == source->Bounds()); 1287 if (result->ColorSpace() != source->ColorSpace() 1288 || result->Bounds() != source->Bounds()) { 1289 printf("IconCache::MakeTransformedIcon() - " 1290 "bitmap format mismatch!\n"); 1291 return NULL; 1292 } 1293 1294 switch (result->ColorSpace()) { 1295 case B_RGB32: 1296 case B_RGBA32: { 1297 uint32 width = source->Bounds().IntegerWidth() + 1; 1298 uint32 height = source->Bounds().IntegerHeight() + 1; 1299 uint32 srcBPR = source->BytesPerRow(); 1300 uint32 dstBPR = result->BytesPerRow(); 1301 for (uint32 y = 0; y < height; y++) { 1302 uint8* d = dst; 1303 uint8* s = src; 1304 for (uint32 x = 0; x < width; x++) { 1305 // 66% brightness 1306 d[0] = (int)s[0] * 168 >> 8; 1307 d[1] = (int)s[1] * 168 >> 8; 1308 d[2] = (int)s[2] * 168 >> 8; 1309 d[3] = s[3]; 1310 d += 4; 1311 s += 4; 1312 } 1313 dst += dstBPR; 1314 src += srcBPR; 1315 } 1316 break; 1317 } 1318 1319 case B_CMAP8: { 1320 int32 bitsLength = result->BitsLength(); 1321 for (int32 i = 0; i < bitsLength; i++) 1322 *dst++ = (uint8)colorTransformTable[*src++]; 1323 break; 1324 } 1325 1326 default: 1327 memset(dst, 0, result->BitsLength()); 1328 // unkown colorspace, no tinting for you 1329 // "black" should make the problem stand out 1330 break; 1331 } 1332 1333 return result; 1334 } 1335 1336 1337 bool 1338 IconCache::IconHitTest(BPoint where, const Model* model, IconDrawMode mode, 1339 icon_size size) 1340 { 1341 AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false); 1342 AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false); 1343 1344 AutoLock<SimpleIconCache>* resultingCacheLocker; 1345 IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker, 1346 &resultingCacheLocker, const_cast<Model*>(model), mode, size, false); 1347 // Preload finds/creates the appropriate entry, locking down the 1348 // cache it is in and returns the whole state back to here 1349 1350 if (entry) 1351 return entry->IconHitTest(where, mode, size); 1352 1353 return false; 1354 } 1355 1356 1357 void 1358 IconCacheEntry::RetireIcons(BObjectList<BBitmap>* retiredBitmapList) 1359 { 1360 if (fLargeIcon) { 1361 retiredBitmapList->AddItem(fLargeIcon); 1362 fLargeIcon = NULL; 1363 } 1364 if (fMiniIcon) { 1365 retiredBitmapList->AddItem(fMiniIcon); 1366 fMiniIcon = NULL; 1367 } 1368 if (fHilitedLargeIcon) { 1369 retiredBitmapList->AddItem(fHilitedLargeIcon); 1370 fHilitedLargeIcon = NULL; 1371 } 1372 if (fHilitedMiniIcon) { 1373 retiredBitmapList->AddItem(fHilitedMiniIcon); 1374 fHilitedMiniIcon = NULL; 1375 } 1376 1377 int32 count = retiredBitmapList->CountItems(); 1378 if (count > 10 * 1024) { 1379 PRINT(("nuking old icons from the retired bitmap list\n")); 1380 for (count = 512; count > 0; count--) 1381 delete retiredBitmapList->RemoveItemAt(0); 1382 } 1383 } 1384 1385 1386 // #pragma mark - 1387 1388 1389 // In debug mode keep the hash table sizes small so that they grow a lot and 1390 // execercise the resizing code a lot. In release mode allocate them large 1391 // up-front for better performance 1392 SharedIconCache::SharedIconCache() 1393 #if DEBUG 1394 : SimpleIconCache("Shared Icon cache aka \"The Dead-Locker\""), 1395 fHashTable(20), 1396 fElementArray(20), 1397 fRetiredBitmaps(20, true) 1398 #else 1399 : SimpleIconCache("Tracker shared icon cache"), 1400 fHashTable(1000), 1401 fElementArray(1024), 1402 fRetiredBitmaps(256, true) 1403 #endif 1404 { 1405 fHashTable.SetElementVector(&fElementArray); 1406 } 1407 1408 1409 void 1410 SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where, 1411 IconDrawMode mode, icon_size size, bool async) 1412 { 1413 ((SharedCacheEntry*)entry)->Draw(view, where, mode, size, async); 1414 } 1415 1416 1417 void 1418 SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where, 1419 IconDrawMode mode, icon_size size, void (*blitFunc)(BView*, BPoint, 1420 BBitmap*, void*), void* passThruState) 1421 { 1422 ((SharedCacheEntry*)entry)->Draw(view, where, mode, size, 1423 blitFunc, passThruState); 1424 } 1425 1426 1427 SharedCacheEntry* 1428 SharedIconCache::FindItem(const char* fileType, 1429 const char* appSignature) const 1430 { 1431 ASSERT(fileType); 1432 if (!fileType) 1433 fileType = B_FILE_MIMETYPE; 1434 1435 SharedCacheEntry* result 1436 = fHashTable.FindFirst(SharedCacheEntry::Hash(fileType, 1437 appSignature)); 1438 1439 if (!result) 1440 return NULL; 1441 1442 for(;;) { 1443 if (result->fFileType == fileType 1444 && result->fAppSignature == appSignature) { 1445 return result; 1446 } 1447 1448 if (result->fNext < 0) 1449 break; 1450 1451 result 1452 = const_cast<SharedCacheEntry*>(&fElementArray.At(result->fNext)); 1453 } 1454 1455 return NULL; 1456 } 1457 1458 1459 SharedCacheEntry* 1460 SharedIconCache::AddItem(const char* fileType, const char* appSignature) 1461 { 1462 ASSERT(fileType); 1463 if (!fileType) 1464 fileType = B_FILE_MIMETYPE; 1465 1466 SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType, 1467 appSignature)); 1468 result->SetTo(fileType, appSignature); 1469 return result; 1470 } 1471 1472 1473 SharedCacheEntry* 1474 SharedIconCache::AddItem(SharedCacheEntry** outstandingEntry, 1475 const char* fileType, const char* appSignature) 1476 { 1477 int32 entryToken = fHashTable.ElementIndex(*outstandingEntry); 1478 ASSERT(entryToken >= 0); 1479 1480 ASSERT(fileType); 1481 if (!fileType) 1482 fileType = B_FILE_MIMETYPE; 1483 1484 SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType, 1485 appSignature)); 1486 result->SetTo(fileType, appSignature); 1487 *outstandingEntry = fHashTable.ElementAt(entryToken); 1488 1489 return result; 1490 } 1491 1492 1493 void 1494 SharedIconCache::IconChanged(SharedCacheEntry* entry) 1495 { 1496 // by now there should be no aliases to entry, just remove entry 1497 // itself 1498 ASSERT(entry->fAliasForIndex == -1); 1499 entry->RetireIcons(&fRetiredBitmaps); 1500 fHashTable.Remove(entry); 1501 } 1502 1503 1504 void 1505 SharedIconCache::RemoveAliasesTo(int32 aliasIndex) 1506 { 1507 int32 count = fHashTable.VectorSize(); 1508 for (int32 index = 0; index < count; index++) { 1509 SharedCacheEntry* entry = fHashTable.ElementAt(index); 1510 if (entry->fAliasForIndex == aliasIndex) 1511 fHashTable.Remove(entry); 1512 } 1513 } 1514 1515 1516 void 1517 SharedIconCache::SetAliasFor(IconCacheEntry* alias, 1518 const SharedCacheEntry* original) const 1519 { 1520 alias->fAliasForIndex = fHashTable.ElementIndex(original); 1521 } 1522 1523 1524 SharedCacheEntry::SharedCacheEntry() 1525 : fNext(-1) 1526 { 1527 } 1528 1529 1530 SharedCacheEntry::SharedCacheEntry(const char* fileType, 1531 const char* appSignature) 1532 : fNext(-1), 1533 fFileType(fileType), 1534 fAppSignature(appSignature) 1535 { 1536 } 1537 1538 1539 void 1540 SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode, 1541 icon_size size, bool async) 1542 { 1543 BBitmap* bitmap = IconForMode(mode, size); 1544 ASSERT(bitmap); 1545 1546 drawing_mode oldMode = view->DrawingMode(); 1547 1548 if (bitmap->ColorSpace() == B_RGBA32) { 1549 if (oldMode != B_OP_ALPHA) { 1550 view->SetDrawingMode(B_OP_ALPHA); 1551 view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 1552 } 1553 } else { 1554 view->SetDrawingMode(B_OP_OVER); 1555 } 1556 1557 if (async) 1558 view->DrawBitmapAsync(bitmap, where); 1559 else 1560 view->DrawBitmap(bitmap, where); 1561 1562 view->SetDrawingMode(oldMode); 1563 } 1564 1565 1566 void 1567 SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode, 1568 icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*), 1569 void* passThruState) 1570 { 1571 BBitmap* bitmap = IconForMode(mode, size); 1572 if (!bitmap) 1573 return; 1574 1575 // if (bitmap->ColorSpace() == B_RGBA32) { 1576 // view->SetDrawingMode(B_OP_ALPHA); 1577 // view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 1578 // } else { 1579 // view->SetDrawingMode(B_OP_OVER); 1580 // } 1581 1582 (blitFunc)(view, where, bitmap, passThruState); 1583 } 1584 1585 1586 uint32 1587 SharedCacheEntry::Hash(const char* fileType, const char* appSignature) 1588 { 1589 uint32 hash = HashString(fileType, 0); 1590 if (appSignature && appSignature[0]) 1591 hash = HashString(appSignature, hash); 1592 1593 return hash; 1594 } 1595 1596 1597 uint32 1598 SharedCacheEntry::Hash() const 1599 { 1600 uint32 hash = HashString(fFileType.String(), 0); 1601 if (fAppSignature.Length()) 1602 hash = HashString(fAppSignature.String(), hash); 1603 1604 return hash; 1605 } 1606 1607 1608 bool 1609 SharedCacheEntry::operator==(const SharedCacheEntry &entry) const 1610 { 1611 return fFileType == entry.FileType() 1612 && fAppSignature == entry.AppSignature(); 1613 } 1614 1615 1616 void 1617 SharedCacheEntry::SetTo(const char* fileType, const char* appSignature) 1618 { 1619 fFileType = fileType; 1620 fAppSignature = appSignature; 1621 } 1622 1623 1624 SharedCacheEntryArray::SharedCacheEntryArray(int32 initialSize) 1625 : OpenHashElementArray<SharedCacheEntry>(initialSize) 1626 { 1627 } 1628 1629 1630 SharedCacheEntry* 1631 SharedCacheEntryArray::Add() 1632 { 1633 return OpenHashElementArray<SharedCacheEntry>::Add(); 1634 } 1635 1636 1637 // #pragma mark - 1638 1639 1640 NodeCacheEntry::NodeCacheEntry(bool permanent) 1641 : fNext(-1), 1642 fPermanent(permanent) 1643 { 1644 } 1645 1646 1647 NodeCacheEntry::NodeCacheEntry(const node_ref* node, bool permanent) 1648 : fNext(-1), 1649 fRef(*node), 1650 fPermanent(permanent) 1651 { 1652 } 1653 1654 1655 void 1656 NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode, 1657 icon_size size, bool async) 1658 { 1659 BBitmap* bitmap = IconForMode(mode, size); 1660 if (!bitmap) 1661 return; 1662 1663 drawing_mode oldMode = view->DrawingMode(); 1664 1665 if (bitmap->ColorSpace() == B_RGBA32) { 1666 if (oldMode != B_OP_ALPHA) { 1667 view->SetDrawingMode(B_OP_ALPHA); 1668 view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 1669 } 1670 } else { 1671 view->SetDrawingMode(B_OP_OVER); 1672 } 1673 1674 if (false && async) { 1675 TRESPASS(); 1676 // need to copy the bits first in here 1677 view->DrawBitmapAsync(bitmap, where); 1678 } else 1679 view->DrawBitmap(bitmap, where); 1680 1681 view->SetDrawingMode(oldMode); 1682 } 1683 1684 1685 void 1686 NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode, 1687 icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*), 1688 void* passThruState) 1689 { 1690 BBitmap* bitmap = IconForMode(mode, size); 1691 if (!bitmap) 1692 return; 1693 1694 // if (bitmap->ColorSpace() == B_RGBA32) { 1695 // view->SetDrawingMode(B_OP_ALPHA); 1696 // view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 1697 // } else { 1698 // view->SetDrawingMode(B_OP_OVER); 1699 // } 1700 1701 (blitFunc)(view, where, bitmap, passThruState); 1702 } 1703 1704 1705 const node_ref* 1706 NodeCacheEntry::Node() const 1707 { 1708 return &fRef; 1709 } 1710 1711 1712 uint32 1713 NodeCacheEntry::Hash() const 1714 { 1715 return Hash(&fRef); 1716 } 1717 1718 1719 uint32 1720 NodeCacheEntry::Hash(const node_ref* node) 1721 { 1722 return node->device ^ ((uint32*)&node->node)[0] 1723 ^ ((uint32*)&node->node)[1]; 1724 } 1725 1726 1727 bool 1728 NodeCacheEntry::operator==(const NodeCacheEntry &entry) const 1729 { 1730 return fRef == entry.fRef; 1731 } 1732 1733 1734 void 1735 NodeCacheEntry::SetTo(const node_ref* node) 1736 { 1737 fRef = *node; 1738 } 1739 1740 1741 bool 1742 NodeCacheEntry::Permanent() const 1743 { 1744 return fPermanent; 1745 } 1746 1747 1748 void 1749 NodeCacheEntry::MakePermanent() 1750 { 1751 fPermanent = true; 1752 } 1753 1754 1755 // #pragma mark - 1756 1757 1758 NodeIconCache::NodeIconCache() 1759 #if DEBUG 1760 : SimpleIconCache("Node Icon cache aka \"The Dead-Locker\""), 1761 fHashTable(20), 1762 fElementArray(20) 1763 #else 1764 : SimpleIconCache("Tracker node icon cache"), 1765 fHashTable(100), 1766 fElementArray(100) 1767 #endif 1768 { 1769 fHashTable.SetElementVector(&fElementArray); 1770 } 1771 1772 1773 void 1774 NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where, 1775 IconDrawMode mode, icon_size size, bool async) 1776 { 1777 1778 ((NodeCacheEntry*)entry)->Draw(view, where, mode, size, async); 1779 } 1780 1781 1782 void 1783 NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where, 1784 IconDrawMode mode, icon_size size, void (*blitFunc)(BView*, BPoint, 1785 BBitmap*, void*), void* passThruState) 1786 { 1787 ((NodeCacheEntry*)entry)->Draw(view, where, mode, size, 1788 blitFunc, passThruState); 1789 } 1790 1791 1792 NodeCacheEntry* 1793 NodeIconCache::FindItem(const node_ref* node) const 1794 { 1795 NodeCacheEntry* result = fHashTable.FindFirst(NodeCacheEntry::Hash(node)); 1796 1797 if (!result) 1798 return NULL; 1799 1800 for(;;) { 1801 if (*result->Node() == *node) 1802 return result; 1803 1804 if (result->fNext < 0) 1805 break; 1806 1807 result 1808 = const_cast<NodeCacheEntry*>(&fElementArray.At(result->fNext)); 1809 } 1810 1811 return NULL; 1812 } 1813 1814 1815 NodeCacheEntry* 1816 NodeIconCache::AddItem(const node_ref* node, bool permanent) 1817 { 1818 NodeCacheEntry* result = fHashTable.Add(NodeCacheEntry::Hash(node)); 1819 result->SetTo(node); 1820 if (permanent) 1821 result->MakePermanent(); 1822 1823 return result; 1824 } 1825 1826 1827 NodeCacheEntry* 1828 NodeIconCache::AddItem(NodeCacheEntry** outstandingEntry, 1829 const node_ref* node) 1830 { 1831 int32 entryToken = fHashTable.ElementIndex(*outstandingEntry); 1832 1833 NodeCacheEntry* result = fHashTable.Add(NodeCacheEntry::Hash(node)); 1834 result->SetTo(node); 1835 *outstandingEntry = fHashTable.ElementAt(entryToken); 1836 1837 return result; 1838 } 1839 1840 1841 void 1842 NodeIconCache::Deleting(const node_ref* node) 1843 { 1844 NodeCacheEntry* entry = FindItem(node); 1845 ASSERT(entry); 1846 if (!entry || entry->Permanent()) 1847 return; 1848 1849 fHashTable.Remove(entry); 1850 } 1851 1852 1853 void 1854 NodeIconCache::Removing(const node_ref* node) 1855 { 1856 NodeCacheEntry* entry = FindItem(node); 1857 ASSERT(entry); 1858 if (!entry) 1859 return; 1860 1861 fHashTable.Remove(entry); 1862 } 1863 1864 1865 void 1866 NodeIconCache::Deleting(const BView*) 1867 { 1868 #ifdef NODE_CACHE_ASYNC_DRAWS 1869 TRESPASS(); 1870 #endif 1871 } 1872 1873 1874 void 1875 NodeIconCache::IconChanged(const Model* model) 1876 { 1877 Deleting(model->NodeRef()); 1878 } 1879 1880 1881 void 1882 NodeIconCache::RemoveAliasesTo(int32 aliasIndex) 1883 { 1884 int32 count = fHashTable.VectorSize(); 1885 for (int32 index = 0; index < count; index++) { 1886 NodeCacheEntry* entry = fHashTable.ElementAt(index); 1887 if (entry->fAliasForIndex == aliasIndex) 1888 fHashTable.Remove(entry); 1889 } 1890 } 1891 1892 1893 // #pragma mark - 1894 1895 1896 NodeCacheEntryArray::NodeCacheEntryArray(int32 initialSize) 1897 : OpenHashElementArray<NodeCacheEntry>(initialSize) 1898 { 1899 } 1900 1901 1902 NodeCacheEntry* 1903 NodeCacheEntryArray::Add() 1904 { 1905 return OpenHashElementArray<NodeCacheEntry>::Add(); 1906 } 1907 1908 1909 // #pragma mark - 1910 1911 1912 SimpleIconCache::SimpleIconCache(const char* name) 1913 : fLock(name) 1914 { 1915 } 1916 1917 1918 void 1919 SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode, 1920 icon_size, bool) 1921 { 1922 TRESPASS(); 1923 // pure virtual, do nothing 1924 } 1925 1926 1927 void 1928 SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode, 1929 icon_size, void(*)(BView*, BPoint, BBitmap*, void*), void*) 1930 { 1931 TRESPASS(); 1932 // pure virtual, do nothing 1933 } 1934 1935 1936 bool 1937 SimpleIconCache::Lock() 1938 { 1939 return fLock.Lock(); 1940 } 1941 1942 1943 void 1944 SimpleIconCache::Unlock() 1945 { 1946 fLock.Unlock(); 1947 } 1948 1949 1950 bool 1951 SimpleIconCache::IsLocked() const 1952 { 1953 return fLock.IsLocked(); 1954 } 1955 1956 1957 // #pragma mark - 1958 1959 1960 LazyBitmapAllocator::LazyBitmapAllocator(icon_size size, 1961 color_space colorSpace, bool preallocate) 1962 : fBitmap(NULL), 1963 fSize(size), 1964 fColorSpace(colorSpace) 1965 { 1966 if (preallocate) 1967 Get(); 1968 } 1969 1970 1971 LazyBitmapAllocator::~LazyBitmapAllocator() 1972 { 1973 delete fBitmap; 1974 } 1975 1976 1977 BBitmap* 1978 LazyBitmapAllocator::Get() 1979 { 1980 if (!fBitmap) 1981 fBitmap = new BBitmap(BRect(0, 0, fSize - 1, fSize - 1), fColorSpace); 1982 1983 return fBitmap; 1984 } 1985 1986 1987 BBitmap* 1988 LazyBitmapAllocator::Adopt() 1989 { 1990 if (!fBitmap) 1991 Get(); 1992 1993 BBitmap* result = fBitmap; 1994 fBitmap = NULL; 1995 return result; 1996 } 1997 1998 1999 IconCache* IconCache::sIconCache; 2000