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