xref: /haiku/src/kits/tracker/IconCache.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
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
110 icon_size_for(BSize size)
111 {
112 	ASSERT(size.Width() == size.Height());
113 	return (icon_size)(size.IntegerWidth() + 1);
114 }
115 
116 
117 IconCacheEntry::IconCacheEntry()
118 	:
119 	fLargeIcon(NULL),
120 	fHighlightedLargeIcon(NULL),
121 	fMiniIcon(NULL),
122 	fHighlightedMiniIcon(NULL),
123 	fAliasTo(NULL)
124 {
125 }
126 
127 
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
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*
156 IconCacheEntry::ResolveIfAlias(const SharedIconCache* sharedCache)
157 {
158 	return sharedCache->ResolveIfAlias(this);
159 }
160 
161 
162 IconCacheEntry*
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
174 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
186 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*
206 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
228 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*
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*
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
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
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 
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*
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*
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*
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*
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*
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*
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*
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*
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*
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*
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
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
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
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
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
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
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
1221 IconCache::Deleting(const BView* view)
1222 {
1223 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1224 	fNodeCache.Deleting(view);
1225 }
1226 
1227 
1228 void
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
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*
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
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
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*
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
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
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 
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
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
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*
1454 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*
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
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
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
1506 SharedIconCache::SetAliasFor(IconCacheEntry* entry,
1507 	const SharedCacheEntry* original) const
1508 {
1509 	entry->fAliasTo = original;
1510 }
1511 
1512 
1513 SharedCacheEntry::SharedCacheEntry()
1514 	:
1515 	fNext(NULL)
1516 {
1517 }
1518 
1519 
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
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
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
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
1582 SharedCacheEntry::Hash() const
1583 {
1584 	return Hash(TypeAndSignature(fFileType.String(), fAppSignature.String()));
1585 }
1586 
1587 
1588 bool
1589 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 
1599 NodeCacheEntry::NodeCacheEntry(bool permanent)
1600 	:
1601 	fNext(NULL),
1602 	fPermanent(permanent)
1603 {
1604 }
1605 
1606 
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
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
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*
1659 NodeCacheEntry::Node() const
1660 {
1661 	return &fRef;
1662 }
1663 
1664 
1665 size_t
1666 NodeCacheEntry::Hash() const
1667 {
1668 	return Hash(&fRef);
1669 }
1670 
1671 
1672 /* static */ size_t
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
1681 NodeCacheEntry::operator==(const node_ref* node) const
1682 {
1683 	return fRef == *node;
1684 }
1685 
1686 
1687 bool
1688 NodeCacheEntry::Permanent() const
1689 {
1690 	return fPermanent;
1691 }
1692 
1693 
1694 //	#pragma mark - NodeIconCache
1695 
1696 
1697 NodeIconCache::NodeIconCache()
1698 	:
1699 	SimpleIconCache("Tracker node icon cache")
1700 {
1701 	fHashTable.Init(100);
1702 }
1703 
1704 
1705 void
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
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*
1724 NodeIconCache::FindItem(const node_ref* node) const
1725 {
1726 	return fHashTable.Lookup(node);
1727 }
1728 
1729 
1730 NodeCacheEntry*
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
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
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
1766 NodeIconCache::Deleting(const BView*)
1767 {
1768 #ifdef NODE_CACHE_ASYNC_DRAWS
1769 	TRESPASS();
1770 #endif
1771 }
1772 
1773 
1774 void
1775 NodeIconCache::IconChanged(const Model* model)
1776 {
1777 	Deleting(model->NodeRef());
1778 }
1779 
1780 
1781 void
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 
1796 SimpleIconCache::SimpleIconCache(const char* name)
1797 	:
1798 	fLock(name)
1799 {
1800 }
1801 
1802 
1803 void
1804 SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1805 	BSize, bool)
1806 {
1807 	TRESPASS();
1808 	// pure virtual, do nothing
1809 }
1810 
1811 
1812 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
1822 SimpleIconCache::Lock()
1823 {
1824 	return fLock.Lock();
1825 }
1826 
1827 
1828 void
1829 SimpleIconCache::Unlock()
1830 {
1831 	fLock.Unlock();
1832 }
1833 
1834 
1835 bool
1836 SimpleIconCache::IsLocked() const
1837 {
1838 	return fLock.IsLocked();
1839 }
1840 
1841 
1842 //	#pragma mark - LazyBitmapAllocator
1843 
1844 
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 
1857 LazyBitmapAllocator::~LazyBitmapAllocator()
1858 {
1859 	delete fBitmap;
1860 }
1861 
1862 
1863 BBitmap*
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*
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