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