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