1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All 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
104IconCacheEntry::IconCacheEntry()
105	:
106	fLargeIcon(NULL),
107	fHighlightedLargeIcon(NULL),
108	fMiniIcon(NULL),
109	fHighlightedMiniIcon(NULL),
110	fAliasForIndex(-1)
111{
112}
113
114
115IconCacheEntry::~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
133void
134IconCacheEntry::SetAliasFor(const SharedIconCache* sharedCache,
135	const SharedCacheEntry* entry)
136{
137	sharedCache->SetAliasFor(this, entry);
138	ASSERT(fAliasForIndex >= 0);
139}
140
141
142IconCacheEntry*
143IconCacheEntry::ResolveIfAlias(const SharedIconCache* sharedCache)
144{
145	return sharedCache->ResolveIfAlias(this);
146}
147
148
149IconCacheEntry*
150IconCacheEntry::ResolveIfAlias(const SharedIconCache* sharedCache,
151	IconCacheEntry* entry)
152{
153	if (entry == NULL)
154		return NULL;
155
156	return sharedCache->ResolveIfAlias(entry);
157}
158
159
160bool
161IconCacheEntry::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
172bool
173IconCacheEntry::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
192BBitmap*
193IconCacheEntry::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
214bool
215IconCacheEntry::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
247BBitmap*
248IconCacheEntry::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
264BBitmap*
265IconCacheEntry::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
276bool
277IconCacheEntry::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
290void
291IconCacheEntry::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
309IconCache::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
326IconCacheEntry*
327IconCache::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
383IconCacheEntry*
384IconCache::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
466IconCacheEntry*
467IconCache::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
556IconCacheEntry*
557IconCache::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
634IconCacheEntry*
635IconCache::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
650IconCacheEntry*
651IconCache::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	BString type("tracker/active_");
663	type += wellKnownEntry->name;
664
665	*resultingOpenCache = sharedCacheLocker;
666	(*resultingOpenCache)->Lock();
667
668	source = kTrackerSupplied;
669
670	IconCacheEntry* entry = fSharedCache.FindItem(type.String());
671	if (entry != NULL) {
672		entry = entry->ResolveIfAlias(&fSharedCache, entry);
673		if (entry->HaveIconBitmap(mode, size))
674			return entry;
675	}
676
677	if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
678		// match up well known entries in the file system with specialized
679		// icons stored in Tracker's resources
680		int32 resourceId = -1;
681		switch ((uint32)wellKnownEntry->which) {
682			case B_BOOT_DISK:
683				resourceId = R_BootVolumeIcon;
684				break;
685
686			case B_BEOS_DIRECTORY:
687				resourceId = R_BeosFolderIcon;
688				break;
689
690			case B_USER_DIRECTORY:
691				resourceId = R_HomeDirIcon;
692				break;
693
694			case B_SYSTEM_FONTS_DIRECTORY:
695			case B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY:
696			case B_USER_FONTS_DIRECTORY:
697			case B_USER_NONPACKAGED_FONTS_DIRECTORY:
698				resourceId = R_FontDirIcon;
699				break;
700
701			case B_BEOS_APPS_DIRECTORY:
702			case B_APPS_DIRECTORY:
703			case B_USER_DESKBAR_APPS_DIRECTORY:
704				resourceId = R_AppsDirIcon;
705				break;
706
707			case B_BEOS_PREFERENCES_DIRECTORY:
708			case B_PREFERENCES_DIRECTORY:
709			case B_USER_DESKBAR_PREFERENCES_DIRECTORY:
710				resourceId = R_PrefsDirIcon;
711				break;
712
713			case B_USER_MAIL_DIRECTORY:
714				resourceId = R_MailDirIcon;
715				break;
716
717			case B_USER_QUERIES_DIRECTORY:
718				resourceId = R_QueryDirIcon;
719				break;
720
721			case B_SYSTEM_DEVELOP_DIRECTORY:
722			case B_SYSTEM_NONPACKAGED_DEVELOP_DIRECTORY:
723			case B_USER_DESKBAR_DEVELOP_DIRECTORY:
724				resourceId = R_DevelopDirIcon;
725				break;
726
727			case B_USER_CONFIG_DIRECTORY:
728				resourceId = R_ConfigDirIcon;
729				break;
730
731			case B_USER_PEOPLE_DIRECTORY:
732				resourceId = R_PersonDirIcon;
733				break;
734
735			case B_USER_DOWNLOADS_DIRECTORY:
736				resourceId = R_DownloadDirIcon;
737				break;
738
739			default:
740				return NULL;
741		}
742
743		entry = fSharedCache.AddItem(type.String());
744
745		BBitmap* bitmap = lazyBitmap->Get();
746		GetTrackerResources()->GetIconResource(resourceId, size, bitmap);
747		entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size);
748	}
749
750	if (mode != kNormalIcon
751		&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
752		entry->ConstructBitmap(mode, size, lazyBitmap);
753		entry->SetIcon(lazyBitmap->Adopt(), mode, size);
754	}
755
756	ASSERT(entry->HaveIconBitmap(mode, size));
757
758	return entry;
759}
760
761
762IconCacheEntry*
763IconCache::GetNodeIcon(ModelNodeLazyOpener* modelOpener,
764	AutoLock<SimpleIconCache>* nodeCacheLocker,
765	AutoLock<SimpleIconCache>** resultingOpenCache,
766	Model* model, IconSource& source,
767	IconDrawMode mode, icon_size size,
768	LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry, bool permanent)
769{
770	*resultingOpenCache = nodeCacheLocker;
771	(*resultingOpenCache)->Lock();
772
773	entry = fNodeCache.FindItem(model->NodeRef());
774	if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
775		modelOpener->OpenNode();
776
777		BFile* file = NULL;
778
779		// if we are dealing with an application, use the BAppFileInfo
780		// superset of node; this makes GetIcon grab the proper icon for
781		// an app
782		if (model->IsExecutable())
783			file = dynamic_cast<BFile*>(model->Node());
784
785		PRINT_DISK_HITS(("File %s; Line %d # hitting disk for node %s\n",
786			__FILE__, __LINE__, model->Name()));
787
788		status_t result = file != NULL
789			? GetAppIconFromAttr(file, lazyBitmap->Get(), size)
790			: GetFileIconFromAttr(model->Node(), lazyBitmap->Get(), size);
791
792		if (result == B_OK) {
793			// node has its own icon, use it
794			BBitmap* bitmap = lazyBitmap->Adopt();
795			PRINT_ADD_ITEM(("File %s; Line %d # adding entry for model %s\n",
796				__FILE__, __LINE__, model->Name()));
797			entry = fNodeCache.AddItem(model->NodeRef(), permanent);
798			ASSERT(entry != NULL);
799			entry->SetIcon(bitmap, kNormalIcon, size);
800			if (mode != kNormalIcon) {
801				entry->ConstructBitmap(mode, size, lazyBitmap);
802				entry->SetIcon(lazyBitmap->Adopt(), mode, size);
803			}
804			source = kNode;
805		}
806	}
807
808	if (entry == NULL) {
809		(*resultingOpenCache)->Unlock();
810		*resultingOpenCache = NULL;
811	} else if (!entry->HaveIconBitmap(mode, size)
812		&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
813		entry->ConstructBitmap(mode, size, lazyBitmap);
814		entry->SetIcon(lazyBitmap->Adopt(), mode, size);
815		ASSERT(entry->HaveIconBitmap(mode, size));
816	}
817
818	return entry;
819}
820
821
822IconCacheEntry*
823IconCache::GetGenericIcon(AutoLock<SimpleIconCache>* sharedCacheLocker,
824	AutoLock<SimpleIconCache>** resultingOpenCache,
825	Model* model, IconSource &source,
826	IconDrawMode mode, icon_size size,
827	LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
828{
829	*resultingOpenCache = sharedCacheLocker;
830	(*resultingOpenCache)->Lock();
831
832	entry = GetIconFromMetaMime(B_FILE_MIMETYPE, mode, size, lazyBitmap, 0);
833	if (entry == NULL)
834		return NULL;
835
836	// make an aliased entry so that the next time we get a
837	// hit and substitute a generic icon right away
838	PRINT_ADD_ITEM(
839		("File %s; Line %d # adding entry for preferredApp %s, type %s\n",
840		__FILE__, __LINE__, model->PreferredAppSignature(),
841		model->MimeType()));
842	IconCacheEntry* aliasedEntry = fSharedCache.AddItem(
843		(SharedCacheEntry**)&entry, model->MimeType(),
844		model->PreferredAppSignature());
845	aliasedEntry->SetAliasFor(&fSharedCache, (SharedCacheEntry*)entry);
846
847	source = kMetaMime;
848
849	ASSERT(entry->HaveIconBitmap(mode, size));
850
851	return entry;
852}
853
854
855IconCacheEntry*
856IconCache::GetFallbackIcon(AutoLock<SimpleIconCache>* sharedCacheLocker,
857	AutoLock<SimpleIconCache>** resultingOpenCache,
858	Model* model, IconDrawMode mode, icon_size size,
859	LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
860{
861	*resultingOpenCache = sharedCacheLocker;
862	(*resultingOpenCache)->Lock();
863
864	entry = fSharedCache.AddItem(model->MimeType(),
865		model->PreferredAppSignature());
866
867	BBitmap* bitmap = lazyBitmap->Get();
868	GetTrackerResources()->GetIconResource(R_FileIcon, size, bitmap);
869	entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size);
870
871	if (mode != kNormalIcon) {
872		entry->ConstructBitmap(mode, size, lazyBitmap);
873		entry->SetIcon(lazyBitmap->Adopt(), mode, size);
874	}
875
876	ASSERT(entry->HaveIconBitmap(mode, size));
877
878	return entry;
879}
880
881
882IconCacheEntry*
883IconCache::Preload(AutoLock<SimpleIconCache>* nodeCacheLocker,
884	AutoLock<SimpleIconCache>* sharedCacheLocker,
885	AutoLock<SimpleIconCache>** resultingCache,
886	Model* model, IconDrawMode mode, icon_size size,
887	bool permanent)
888{
889	IconCacheEntry* entry = NULL;
890
891	AutoLock<SimpleIconCache>* resultingOpenCache = NULL;
892		// resultingOpenCache is the locker that points to the cache that
893		// ended with a hit and will be used for the drawing
894
895	{
896		// scope for modelOpener
897
898		ModelNodeLazyOpener modelOpener(model);
899			// this opener takes care of opening the model and possibly
900			// closing it when we are done
901		LazyBitmapAllocator lazyBitmap(size);
902			// lazyBitmap manages bitmap allocation and freeing if needed
903
904		IconSource source = model->IconFrom();
905		if (source == kUnknownSource || source == kUnknownNotFromNode) {
906			// fish for special first models and handle them appropriately
907			if (model->IsVolume()) {
908				// volume may use specialized icon in the volume node
909				entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
910					&resultingOpenCache, model, source, mode, size,
911					&lazyBitmap, entry, permanent);
912				if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
913					// look for volume defined icon
914					entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker,
915						&resultingOpenCache, model, source, mode,
916						size, &lazyBitmap);
917				}
918			} else if (model->IsRoot()) {
919				entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker,
920					&resultingOpenCache, model, source, mode, size,
921						&lazyBitmap);
922				ASSERT(entry != NULL);
923			} else {
924				if (source == kUnknownSource) {
925					// look for node icons first
926					entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
927						&resultingOpenCache, model, source,
928						mode, size, &lazyBitmap, entry, permanent);
929				}
930
931				if (entry == NULL) {
932					// no node icon, look for file type based one
933					modelOpener.OpenNode();
934					// use file types to get the icon
935					resultingOpenCache = sharedCacheLocker;
936					resultingOpenCache->Lock();
937
938					entry = GetIconFromFileTypes(&modelOpener, source, mode,
939						size, &lazyBitmap, 0);
940					if (entry == NULL) {
941						// we don't have an icon, go with the generic
942						entry = GetGenericIcon(sharedCacheLocker,
943							&resultingOpenCache, model, source, mode,
944							size, &lazyBitmap, entry);
945					}
946				}
947			}
948
949			// update the icon source
950			model->SetIconFrom(source);
951		} else {
952			// we already know where the icon should come from,
953			// use shortcuts to get it
954			switch (source) {
955				case kNode:
956					resultingOpenCache = nodeCacheLocker;
957					resultingOpenCache->Lock();
958
959					entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
960						&resultingOpenCache, model, source, mode,
961						size, &lazyBitmap, entry, permanent);
962					if (entry != NULL) {
963						entry = IconCacheEntry::ResolveIfAlias(&fSharedCache,
964							entry);
965						if (!entry->HaveIconBitmap(mode, size)
966							&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
967							entry->ConstructBitmap(mode, size, &lazyBitmap);
968							entry->SetIcon(lazyBitmap.Adopt(), mode, size);
969						}
970						ASSERT(entry->HaveIconBitmap(mode, size));
971					}
972					break;
973				case kTrackerSupplied:
974					if (model->IsRoot()) {
975						entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker,
976							&resultingOpenCache, model, source, mode, size,
977							&lazyBitmap);
978						break;
979					} else {
980						entry = GetWellKnownIcon(nodeCacheLocker,
981							sharedCacheLocker, &resultingOpenCache, model,
982							source, mode, size, &lazyBitmap);
983						if (entry != NULL)
984							break;
985					}
986					// fall through
987				case kTrackerDefault:
988				case kVolume:
989					if (model->IsVolume()) {
990						entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
991							&resultingOpenCache, model, source,
992							mode, size, &lazyBitmap, entry, permanent);
993						if (entry == NULL
994							|| !entry->HaveIconBitmap(mode, size)) {
995							entry = GetVolumeIcon(nodeCacheLocker,
996								sharedCacheLocker, &resultingOpenCache, model,
997								source, mode, size, &lazyBitmap);
998						}
999						break;
1000					}
1001					// fall through
1002				case kMetaMime:
1003				case kPreferredAppForType:
1004				case kPreferredAppForNode:
1005					resultingOpenCache = sharedCacheLocker;
1006					resultingOpenCache->Lock();
1007
1008					entry = GetIconFromFileTypes(&modelOpener, source, mode,
1009						size, &lazyBitmap, 0);
1010
1011					if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1012						// we don't have an icon, go with the generic
1013						entry = GetGenericIcon(sharedCacheLocker,
1014							&resultingOpenCache, model, source, mode, size,
1015							&lazyBitmap, entry);
1016					}
1017
1018					model->SetIconFrom(source);
1019						// The source shouldn't change in this case; if it does
1020						// though we might never be hitting the correct icon and
1021						// instead keep leaking entries after each miss this now
1022						// happens if an app defines an icon but a
1023						// GetIconForType() fails and we fall back to generic
1024						// icon.
1025						// ToDo: fix this and add an assert to the effect
1026
1027					ASSERT(entry != NULL);
1028					ASSERT(entry->HaveIconBitmap(mode, size));
1029					break;
1030
1031				default:
1032					TRESPASS();
1033					break;
1034			}
1035		}
1036
1037		if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1038			// we don't have an icon, go with the generic
1039			PRINT(
1040				("icon cache complete miss, falling back on generic icon "
1041				 "for %s\n", model->Name()));
1042			entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
1043				model, source, mode, size, &lazyBitmap, entry);
1044
1045			// we don't even have generic, something is really broken,
1046			// go with hardcoded generic icon
1047			if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1048				PRINT(
1049				("icon cache complete miss, falling back on generic "
1050				 "icon for %s\n", model->Name()));
1051				entry = GetFallbackIcon(sharedCacheLocker,
1052					&resultingOpenCache, model, mode, size, &lazyBitmap,
1053					entry);
1054			}
1055
1056			// force icon pick up next time around because we probably just
1057			// hit a node in transition
1058			model->SetIconFrom(kUnknownSource);
1059		}
1060	}
1061
1062	ASSERT(entry != NULL && entry->HaveIconBitmap(mode, size));
1063
1064	if (resultingCache != NULL)
1065		*resultingCache = resultingOpenCache;
1066
1067	return entry;
1068}
1069
1070
1071void
1072IconCache::Draw(Model* model, BView* view, BPoint where, IconDrawMode mode,
1073	icon_size size, bool async)
1074{
1075	// the following does not actually lock the caches, we are using the
1076	// lockLater mode; we will decide which of the two to lock down depending
1077	// on where we get the icon from
1078	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1079	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1080
1081	AutoLock<SimpleIconCache>* resultingCacheLocker;
1082	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1083		&resultingCacheLocker, model, mode, size, false);
1084		// Preload finds/creates the appropriate entry, locking down the
1085		// cache it is in and returns the whole state back to here
1086
1087	if (entry == NULL)
1088		return;
1089
1090	ASSERT(entry != NULL);
1091	ASSERT(entry->HaveIconBitmap(mode, size));
1092	// got the entry, now draw it
1093	resultingCacheLocker->LockedItem()->Draw(entry, view, where, mode,
1094		size, async);
1095
1096	// either of the two cache lockers that got locked down by this call get
1097	// unlocked at this point
1098}
1099
1100
1101void
1102IconCache::SyncDraw(Model* model, BView* view, BPoint where,
1103	IconDrawMode mode, icon_size size,
1104	void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1105	void* passThruState)
1106{
1107	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1108	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1109
1110	AutoLock<SimpleIconCache>* resultingCacheLocker;
1111	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1112		&resultingCacheLocker, model, mode, size, false);
1113
1114	if (entry == NULL)
1115		return;
1116
1117	ASSERT(entry != NULL);
1118	ASSERT(entry->HaveIconBitmap(mode, size));
1119	resultingCacheLocker->LockedItem()->Draw(entry, view, where,
1120		mode, size, blitFunc, passThruState);
1121}
1122
1123
1124void
1125IconCache::Preload(Model* model, IconDrawMode mode, icon_size size,
1126	bool permanent)
1127{
1128	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1129	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1130
1131	Preload(&nodeCacheLocker, &sharedCacheLocker, 0, model, mode, size,
1132		permanent);
1133}
1134
1135
1136status_t
1137IconCache::Preload(const char* fileType, IconDrawMode mode, icon_size size)
1138{
1139	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache);
1140	LazyBitmapAllocator lazyBitmap(size);
1141
1142	BMimeType mime(fileType);
1143	char preferredAppSig[B_MIME_TYPE_LENGTH];
1144	status_t result = mime.GetPreferredApp(preferredAppSig);
1145	if (result != B_OK)
1146		return result;
1147
1148	// try getting the icon from the preferred app for the signature
1149	IconCacheEntry* entry = GetIconForPreferredApp(fileType, preferredAppSig,
1150		mode, size, &lazyBitmap, 0);
1151	if (entry != NULL)
1152		return B_OK;
1153
1154	// try getting the icon directly from the metamime
1155	result = mime.GetIcon(lazyBitmap.Get(), size);
1156
1157	if (result != B_OK)
1158		return result;
1159
1160	entry = fSharedCache.AddItem(fileType);
1161	BBitmap* bitmap = lazyBitmap.Adopt();
1162	entry->SetIcon(bitmap, kNormalIcon, size);
1163	if (mode != kNormalIcon) {
1164		entry->ConstructBitmap(mode, size, &lazyBitmap);
1165		entry->SetIcon(lazyBitmap.Adopt(), mode, size);
1166	}
1167
1168	return B_OK;
1169}
1170
1171
1172void
1173IconCache::Deleting(const Model* model)
1174{
1175	AutoLock<SimpleIconCache> lock(&fNodeCache);
1176
1177	if (model->IconFrom() == kNode)
1178		fNodeCache.Deleting(model->NodeRef());
1179
1180	// don't care if the node uses the shared cache
1181}
1182
1183
1184void
1185IconCache::Removing(const Model* model)
1186{
1187	AutoLock<SimpleIconCache> lock(&fNodeCache);
1188
1189	if (model->IconFrom() == kNode)
1190		fNodeCache.Removing(model->NodeRef());
1191}
1192
1193
1194void
1195IconCache::Deleting(const BView* view)
1196{
1197	AutoLock<SimpleIconCache> lock(&fNodeCache);
1198	fNodeCache.Deleting(view);
1199}
1200
1201
1202void
1203IconCache::IconChanged(Model* model)
1204{
1205	AutoLock<SimpleIconCache> lock(&fNodeCache);
1206
1207	if (model->IconFrom() == kNode || model->IconFrom() == kVolume)
1208		fNodeCache.Deleting(model->NodeRef());
1209
1210	model->ResetIconFrom();
1211}
1212
1213
1214void
1215IconCache::IconChanged(const char* mimeType, const char* appSignature)
1216{
1217	AutoLock<SimpleIconCache> sharedLock(&fSharedCache);
1218	SharedCacheEntry* entry = fSharedCache.FindItem(mimeType, appSignature);
1219	if (entry == NULL)
1220		return;
1221
1222	AutoLock<SimpleIconCache> nodeLock(&fNodeCache);
1223
1224	entry = (SharedCacheEntry*)fSharedCache.ResolveIfAlias(entry);
1225	ASSERT(entry != NULL);
1226	int32 index = fSharedCache.EntryIndex(entry);
1227
1228	fNodeCache.RemoveAliasesTo(index);
1229	fSharedCache.RemoveAliasesTo(index);
1230
1231	fSharedCache.IconChanged(entry);
1232}
1233
1234
1235BBitmap*
1236IconCache::MakeSelectedIcon(const BBitmap* normal, icon_size size,
1237	LazyBitmapAllocator* lazyBitmap)
1238{
1239	return MakeTransformedIcon(normal, size, fHighlightTable, lazyBitmap);
1240}
1241
1242
1243#if xDEBUG
1244static void
1245DumpBitmap(const BBitmap* bitmap)
1246{
1247	if (bitmap == NULL) {
1248		printf("NULL bitmap passed to DumpBitmap\n");
1249		return;
1250	}
1251	int32 length = bitmap->BitsLength();
1252
1253	printf("data length %ld \n", length);
1254
1255	int32 columns = (int32)bitmap->Bounds().Width() + 1;
1256	const unsigned char* bitPtr = (const unsigned char*)bitmap->Bits();
1257	for (; length >= 0; length--) {
1258		for (int32 columnIndex = 0; columnIndex < columns;
1259			columnIndex++, length--)
1260			printf("%c%c", "0123456789ABCDEF"[(*bitPtr)/0x10],
1261				"0123456789ABCDEF"[(*bitPtr++)%0x10]);
1262
1263		printf("\n");
1264	}
1265	printf("\n");
1266}
1267#endif
1268
1269
1270void
1271IconCache::InitHighlightTable()
1272{
1273	// build the color transform tables for different icon modes
1274	BScreen screen(B_MAIN_SCREEN_ID);
1275	rgb_color color;
1276	for (int32 index = 0; index < kColorTransformTableSize; index++) {
1277		color = screen.ColorForIndex((uchar)index);
1278		fHighlightTable[index] = screen.IndexForColor(tint_color(color, 1.3f));
1279	}
1280
1281	fHighlightTable[B_TRANSPARENT_8_BIT] = B_TRANSPARENT_8_BIT;
1282	fInitHighlightTable = false;
1283}
1284
1285
1286BBitmap*
1287IconCache::MakeTransformedIcon(const BBitmap* source, icon_size /*size*/,
1288	int32 colorTransformTable[], LazyBitmapAllocator* lazyBitmap)
1289{
1290	if (fInitHighlightTable)
1291		InitHighlightTable();
1292
1293	BBitmap* result = lazyBitmap->Get();
1294	uint8* src = (uint8*)source->Bits();
1295	uint8* dst = (uint8*)result->Bits();
1296
1297//	ASSERT(result->ColorSpace() == source->ColorSpace()
1298//		&& result->Bounds() == source->Bounds());
1299	if (result->ColorSpace() != source->ColorSpace()
1300		|| result->Bounds() != source->Bounds()) {
1301		printf("IconCache::MakeTransformedIcon() - "
1302					"bitmap format mismatch!\n");
1303		return NULL;
1304	}
1305
1306	switch (result->ColorSpace()) {
1307		case B_RGB32:
1308		case B_RGBA32: {
1309			uint32 width = source->Bounds().IntegerWidth() + 1;
1310			uint32 height = source->Bounds().IntegerHeight() + 1;
1311			uint32 srcBPR = source->BytesPerRow();
1312			uint32 dstBPR = result->BytesPerRow();
1313			for (uint32 y = 0; y < height; y++) {
1314				uint8* d = dst;
1315				uint8* s = src;
1316				for (uint32 x = 0; x < width; x++) {
1317					// 66% brightness
1318					d[0] = (int)s[0] * 168 >> 8;
1319					d[1] = (int)s[1] * 168 >> 8;
1320					d[2] = (int)s[2] * 168 >> 8;
1321					d[3] = s[3];
1322					d += 4;
1323					s += 4;
1324				}
1325				dst += dstBPR;
1326				src += srcBPR;
1327			}
1328			break;
1329		}
1330
1331		case B_CMAP8: {
1332			int32 bitsLength = result->BitsLength();
1333			for (int32 i = 0; i < bitsLength; i++)
1334				*dst++ = (uint8)colorTransformTable[*src++];
1335			break;
1336		}
1337
1338		default:
1339			memset(dst, 0, result->BitsLength());
1340			// unkown colorspace, no tinting for you
1341			// "black" should make the problem stand out
1342			break;
1343	}
1344
1345	return result;
1346}
1347
1348
1349bool
1350IconCache::IconHitTest(BPoint where, const Model* model, IconDrawMode mode,
1351	icon_size size)
1352{
1353	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1354	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1355
1356	AutoLock<SimpleIconCache>* resultingCacheLocker;
1357	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1358		&resultingCacheLocker, const_cast<Model*>(model), mode, size, false);
1359		// Preload finds/creates the appropriate entry, locking down the
1360		// cache it is in and returns the whole state back to here
1361
1362	if (entry != NULL)
1363		return entry->IconHitTest(where, mode, size);
1364
1365	return false;
1366}
1367
1368
1369void
1370IconCacheEntry::RetireIcons(BObjectList<BBitmap>* retiredBitmapList)
1371{
1372	if (fLargeIcon != NULL) {
1373		retiredBitmapList->AddItem(fLargeIcon);
1374		fLargeIcon = NULL;
1375	}
1376	if (fHighlightedLargeIcon != NULL) {
1377		retiredBitmapList->AddItem(fHighlightedLargeIcon);
1378		fHighlightedLargeIcon = NULL;
1379	}
1380	if (fMiniIcon != NULL) {
1381		retiredBitmapList->AddItem(fMiniIcon);
1382		fMiniIcon = NULL;
1383	}
1384	if (fHighlightedMiniIcon != NULL) {
1385		retiredBitmapList->AddItem(fHighlightedMiniIcon);
1386		fHighlightedMiniIcon = NULL;
1387	}
1388
1389	int32 count = retiredBitmapList->CountItems();
1390	if (count > 10 * 1024) {
1391		PRINT(("nuking old icons from the retired bitmap list\n"));
1392		for (count = 512; count > 0; count--)
1393			delete retiredBitmapList->RemoveItemAt(0);
1394	}
1395}
1396
1397
1398//	#pragma mark - SharedIconCache
1399
1400
1401// In debug mode keep the hash table sizes small so that they grow a lot and
1402// execercise the resizing code a lot. In release mode allocate them large
1403// up-front for better performance
1404SharedIconCache::SharedIconCache()
1405	:
1406#if DEBUG
1407	SimpleIconCache("Shared icon cache aka \"The Dead-Locker\""),
1408	fElementArray(20),
1409	fHashTable(20),
1410	fRetiredBitmaps(20, true)
1411#else
1412	SimpleIconCache("Tracker shared icon cache"),
1413	fElementArray(1024),
1414	fHashTable(1000),
1415	fRetiredBitmaps(256, true)
1416#endif
1417{
1418	fHashTable.SetElementVector(&fElementArray);
1419}
1420
1421
1422void
1423SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1424	IconDrawMode mode, icon_size size, bool async)
1425{
1426	((SharedCacheEntry*)entry)->Draw(view, where, mode, size, async);
1427}
1428
1429
1430void
1431SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1432	IconDrawMode mode, icon_size size, void (*blitFunc)(BView*, BPoint,
1433	BBitmap*, void*), void* passThruState)
1434{
1435	((SharedCacheEntry*)entry)->Draw(view, where, mode, size,
1436		blitFunc, passThruState);
1437}
1438
1439
1440SharedCacheEntry*
1441SharedIconCache::FindItem(const char* fileType,
1442	const char* appSignature) const
1443{
1444	ASSERT(fileType);
1445	if (!fileType)
1446		fileType = B_FILE_MIMETYPE;
1447
1448	SharedCacheEntry* result
1449		= fHashTable.FindFirst(SharedCacheEntry::Hash(fileType,
1450			appSignature));
1451
1452	if (result == NULL)
1453		return NULL;
1454
1455	for(;;) {
1456		if (result->fFileType == fileType
1457			&& result->fAppSignature == appSignature) {
1458			return result;
1459		}
1460
1461		if (result->fNext < 0)
1462			break;
1463
1464		result = const_cast<SharedCacheEntry*>(&fElementArray.At(
1465			result->fNext));
1466	}
1467
1468	return NULL;
1469}
1470
1471
1472SharedCacheEntry*
1473SharedIconCache::AddItem(const char* fileType, const char* appSignature)
1474{
1475	ASSERT(fileType != NULL);
1476	if (fileType == NULL)
1477		fileType = B_FILE_MIMETYPE;
1478
1479	SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType,
1480		appSignature));
1481	result->SetTo(fileType, appSignature);
1482
1483	return result;
1484}
1485
1486
1487SharedCacheEntry*
1488SharedIconCache::AddItem(SharedCacheEntry** outstandingEntry,
1489	const char* fileType, const char* appSignature)
1490{
1491	int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
1492	ASSERT(entryToken >= 0);
1493
1494	ASSERT(fileType != NULL);
1495	if (fileType == NULL)
1496		fileType = B_FILE_MIMETYPE;
1497
1498	SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType,
1499		appSignature));
1500	result->SetTo(fileType, appSignature);
1501	*outstandingEntry = fHashTable.ElementAt(entryToken);
1502
1503	return result;
1504}
1505
1506
1507void
1508SharedIconCache::IconChanged(SharedCacheEntry* entry)
1509{
1510	// by now there should be no aliases to entry, just remove entry
1511	// itself
1512	ASSERT(entry->fAliasForIndex == -1);
1513	entry->RetireIcons(&fRetiredBitmaps);
1514	fHashTable.Remove(entry);
1515}
1516
1517
1518void
1519SharedIconCache::RemoveAliasesTo(int32 aliasIndex)
1520{
1521	int32 count = fHashTable.VectorSize();
1522	for (int32 index = 0; index < count; index++) {
1523		SharedCacheEntry* entry = fHashTable.ElementAt(index);
1524		if (entry->fAliasForIndex == aliasIndex)
1525			fHashTable.Remove(entry);
1526	}
1527}
1528
1529
1530void
1531SharedIconCache::SetAliasFor(IconCacheEntry* entry,
1532	const SharedCacheEntry* original) const
1533{
1534	entry->fAliasForIndex = fHashTable.ElementIndex(original);
1535}
1536
1537
1538SharedCacheEntry::SharedCacheEntry()
1539	:
1540	fNext(-1)
1541{
1542}
1543
1544
1545SharedCacheEntry::SharedCacheEntry(const char* fileType,
1546	const char* appSignature)
1547	:
1548	fNext(-1),
1549	fFileType(fileType),
1550	fAppSignature(appSignature)
1551{
1552}
1553
1554
1555void
1556SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1557	icon_size size, bool async)
1558{
1559	BBitmap* bitmap = IconForMode(mode, size);
1560	ASSERT(bitmap != NULL);
1561
1562	drawing_mode oldMode = view->DrawingMode();
1563
1564	if (bitmap->ColorSpace() == B_RGBA32) {
1565		if (oldMode != B_OP_ALPHA) {
1566			view->SetDrawingMode(B_OP_ALPHA);
1567			view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1568		}
1569	} else
1570		view->SetDrawingMode(B_OP_OVER);
1571
1572	if (async)
1573		view->DrawBitmapAsync(bitmap, where);
1574	else
1575		view->DrawBitmap(bitmap, where);
1576
1577	view->SetDrawingMode(oldMode);
1578}
1579
1580
1581void
1582SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1583	icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1584	void* passThruState)
1585{
1586	BBitmap* bitmap = IconForMode(mode, size);
1587	if (bitmap == NULL)
1588		return;
1589
1590	(blitFunc)(view, where, bitmap, passThruState);
1591}
1592
1593
1594uint32
1595SharedCacheEntry::Hash(const char* fileType, const char* appSignature)
1596{
1597	uint32 hash = HashString(fileType, 0);
1598	if (appSignature != NULL && *appSignature != '\0')
1599		hash = HashString(appSignature, hash);
1600
1601	return hash;
1602}
1603
1604
1605uint32
1606SharedCacheEntry::Hash() const
1607{
1608	uint32 hash = HashString(fFileType.String(), 0);
1609	if (fAppSignature.Length() > 0)
1610		hash = HashString(fAppSignature.String(), hash);
1611
1612	return hash;
1613}
1614
1615
1616bool
1617SharedCacheEntry::operator==(const SharedCacheEntry &entry) const
1618{
1619	return fFileType == entry.FileType()
1620		&& fAppSignature == entry.AppSignature();
1621}
1622
1623
1624void
1625SharedCacheEntry::SetTo(const char* fileType, const char* appSignature)
1626{
1627	fFileType = fileType;
1628	fAppSignature = appSignature;
1629}
1630
1631
1632SharedCacheEntryArray::SharedCacheEntryArray(int32 initialSize)
1633	:
1634	OpenHashElementArray<SharedCacheEntry>(initialSize)
1635{
1636}
1637
1638
1639SharedCacheEntry*
1640SharedCacheEntryArray::Add()
1641{
1642	return OpenHashElementArray<SharedCacheEntry>::Add();
1643}
1644
1645
1646//	#pragma mark - NodeCacheEntry
1647
1648
1649NodeCacheEntry::NodeCacheEntry(bool permanent)
1650	:
1651	fNext(-1),
1652	fPermanent(permanent)
1653{
1654}
1655
1656
1657NodeCacheEntry::NodeCacheEntry(const node_ref* node, bool permanent)
1658	:
1659	fNext(-1),
1660	fRef(*node),
1661	fPermanent(permanent)
1662{
1663}
1664
1665
1666void
1667NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1668	icon_size size, bool async)
1669{
1670	BBitmap* bitmap = IconForMode(mode, size);
1671	if (bitmap == NULL)
1672		return;
1673
1674	drawing_mode oldMode = view->DrawingMode();
1675
1676	if (bitmap->ColorSpace() == B_RGBA32) {
1677		if (oldMode != B_OP_ALPHA) {
1678			view->SetDrawingMode(B_OP_ALPHA);
1679			view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1680		}
1681	} else
1682		view->SetDrawingMode(B_OP_OVER);
1683
1684	if (false && async) {
1685		TRESPASS();
1686		// need to copy the bits first in here
1687		view->DrawBitmapAsync(bitmap, where);
1688	} else
1689		view->DrawBitmap(bitmap, where);
1690
1691	view->SetDrawingMode(oldMode);
1692}
1693
1694
1695void
1696NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1697	icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1698	void* passThruState)
1699{
1700	BBitmap* bitmap = IconForMode(mode, size);
1701	if (bitmap == NULL)
1702		return;
1703
1704	(blitFunc)(view, where, bitmap, passThruState);
1705}
1706
1707
1708const node_ref*
1709NodeCacheEntry::Node() const
1710{
1711	return &fRef;
1712}
1713
1714
1715uint32
1716NodeCacheEntry::Hash() const
1717{
1718	return Hash(&fRef);
1719}
1720
1721
1722uint32
1723NodeCacheEntry::Hash(const node_ref* node)
1724{
1725	return node->device ^ ((uint32*)&node->node)[0]
1726		^ ((uint32*)&node->node)[1];
1727}
1728
1729
1730bool
1731NodeCacheEntry::operator==(const NodeCacheEntry &entry) const
1732{
1733	return fRef == entry.fRef;
1734}
1735
1736
1737void
1738NodeCacheEntry::SetTo(const node_ref* node)
1739{
1740	fRef = *node;
1741}
1742
1743
1744bool
1745NodeCacheEntry::Permanent() const
1746{
1747	return fPermanent;
1748}
1749
1750
1751void
1752NodeCacheEntry::MakePermanent()
1753{
1754	fPermanent = true;
1755}
1756
1757
1758//	#pragma mark - NodeIconCache
1759
1760
1761NodeIconCache::NodeIconCache()
1762	:
1763#if DEBUG
1764	SimpleIconCache("Node icon cache aka \"The Dead-Locker\""),
1765	fElementArray(20),
1766	fHashTable(20)
1767#else
1768	SimpleIconCache("Tracker node icon cache"),
1769	fElementArray(100),
1770	fHashTable(100)
1771#endif
1772{
1773	fHashTable.SetElementVector(&fElementArray);
1774}
1775
1776
1777void
1778NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1779	IconDrawMode mode, icon_size size, bool async)
1780{
1781	((NodeCacheEntry*)entry)->Draw(view, where, mode, size, async);
1782}
1783
1784
1785void
1786NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1787	IconDrawMode mode, icon_size size,
1788	void (*blitFunc)(BView*, BPoint, BBitmap*, void*), void* passThruState)
1789{
1790	((NodeCacheEntry*)entry)->Draw(view, where, mode, size,
1791		blitFunc, passThruState);
1792}
1793
1794
1795NodeCacheEntry*
1796NodeIconCache::FindItem(const node_ref* node) const
1797{
1798	NodeCacheEntry* entry = fHashTable.FindFirst(NodeCacheEntry::Hash(node));
1799	if (entry == NULL)
1800		return NULL;
1801
1802	for(;;) {
1803		if (*entry->Node() == *node)
1804			return entry;
1805
1806		if (entry->fNext < 0)
1807			break;
1808
1809		entry = const_cast<NodeCacheEntry*>(&fElementArray.At(entry->fNext));
1810	}
1811
1812	return NULL;
1813}
1814
1815
1816NodeCacheEntry*
1817NodeIconCache::AddItem(const node_ref* node, bool permanent)
1818{
1819	NodeCacheEntry* entry = fHashTable.Add(NodeCacheEntry::Hash(node));
1820	entry->SetTo(node);
1821	if (permanent)
1822		entry->MakePermanent();
1823
1824	return entry;
1825}
1826
1827
1828NodeCacheEntry*
1829NodeIconCache::AddItem(NodeCacheEntry** outstandingEntry,
1830	const node_ref* node)
1831{
1832	int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
1833
1834	NodeCacheEntry* entry = fHashTable.Add(NodeCacheEntry::Hash(node));
1835	entry->SetTo(node);
1836	*outstandingEntry = fHashTable.ElementAt(entryToken);
1837
1838	return entry;
1839}
1840
1841
1842void
1843NodeIconCache::Deleting(const node_ref* node)
1844{
1845	NodeCacheEntry* entry = FindItem(node);
1846	ASSERT(entry != NULL);
1847	if (entry == NULL || entry->Permanent())
1848		return;
1849
1850	fHashTable.Remove(entry);
1851}
1852
1853
1854void
1855NodeIconCache::Removing(const node_ref* node)
1856{
1857	NodeCacheEntry* entry = FindItem(node);
1858	ASSERT(entry != NULL);
1859	if (entry == NULL)
1860		return;
1861
1862	fHashTable.Remove(entry);
1863}
1864
1865
1866void
1867NodeIconCache::Deleting(const BView*)
1868{
1869#ifdef NODE_CACHE_ASYNC_DRAWS
1870	TRESPASS();
1871#endif
1872}
1873
1874
1875void
1876NodeIconCache::IconChanged(const Model* model)
1877{
1878	Deleting(model->NodeRef());
1879}
1880
1881
1882void
1883NodeIconCache::RemoveAliasesTo(int32 aliasIndex)
1884{
1885	int32 count = fHashTable.VectorSize();
1886	for (int32 index = 0; index < count; index++) {
1887		NodeCacheEntry* entry = fHashTable.ElementAt(index);
1888		if (entry->fAliasForIndex == aliasIndex)
1889			fHashTable.Remove(entry);
1890	}
1891}
1892
1893
1894//	#pragma mark - NodeCacheEntryArray
1895
1896
1897NodeCacheEntryArray::NodeCacheEntryArray(int32 initialSize)
1898	:
1899	OpenHashElementArray<NodeCacheEntry>(initialSize)
1900{
1901}
1902
1903
1904NodeCacheEntry*
1905NodeCacheEntryArray::Add()
1906{
1907	return OpenHashElementArray<NodeCacheEntry>::Add();
1908}
1909
1910
1911//	#pragma mark - SimpleIconCache
1912
1913
1914SimpleIconCache::SimpleIconCache(const char* name)
1915	:
1916	fLock(name)
1917{
1918}
1919
1920
1921void
1922SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1923	icon_size, bool)
1924{
1925	TRESPASS();
1926	// pure virtual, do nothing
1927}
1928
1929
1930void
1931SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1932	icon_size, void(*)(BView*, BPoint, BBitmap*, void*), void*)
1933{
1934	TRESPASS();
1935	// pure virtual, do nothing
1936}
1937
1938
1939bool
1940SimpleIconCache::Lock()
1941{
1942	return fLock.Lock();
1943}
1944
1945
1946void
1947SimpleIconCache::Unlock()
1948{
1949	fLock.Unlock();
1950}
1951
1952
1953bool
1954SimpleIconCache::IsLocked() const
1955{
1956	return fLock.IsLocked();
1957}
1958
1959
1960//	#pragma mark - LazyBitmapAllocator
1961
1962
1963LazyBitmapAllocator::LazyBitmapAllocator(icon_size size,
1964	color_space colorSpace, bool preallocate)
1965	:
1966	fBitmap(NULL),
1967	fSize(size),
1968	fColorSpace(colorSpace)
1969{
1970	if (preallocate)
1971		Get();
1972}
1973
1974
1975LazyBitmapAllocator::~LazyBitmapAllocator()
1976{
1977	delete fBitmap;
1978}
1979
1980
1981BBitmap*
1982LazyBitmapAllocator::Get()
1983{
1984	if (fBitmap == NULL)
1985		fBitmap = new BBitmap(BRect(0, 0, fSize - 1, fSize - 1), fColorSpace);
1986
1987	return fBitmap;
1988}
1989
1990
1991BBitmap*
1992LazyBitmapAllocator::Adopt()
1993{
1994	if (fBitmap == NULL)
1995		Get();
1996
1997	BBitmap* bitmap = fBitmap;
1998	fBitmap = NULL;
1999
2000	return bitmap;
2001}
2002
2003
2004IconCache* IconCache::sIconCache;
2005