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