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					ASSERT(entry != NULL || entry->HaveIconBitmap(mode, size));
1011
1012					if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1013						// we don't have an icon, go with the generic
1014						entry = GetGenericIcon(sharedCacheLocker,
1015							&resultingOpenCache, model, source, mode, size,
1016							&lazyBitmap, entry);
1017					}
1018
1019					model->SetIconFrom(source);
1020						// The source shouldn't change in this case; if it does
1021						// though we might never be hitting the correct icon and
1022						// instead keep leaking entries after each miss this now
1023						// happens if an app defines an icon but a
1024						// GetIconForType() fails and we fall back to generic
1025						// icon.
1026						// ToDo: fix this and add an assert to the effect
1027
1028					ASSERT(entry != NULL);
1029					ASSERT(entry->HaveIconBitmap(mode, size));
1030					break;
1031
1032				default:
1033					TRESPASS();
1034					break;
1035			}
1036		}
1037
1038		if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1039			// we don't have an icon, go with the generic
1040			PRINT(
1041				("icon cache complete miss, falling back on generic icon "
1042				 "for %s\n", model->Name()));
1043			entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
1044				model, source, mode, size, &lazyBitmap, entry);
1045
1046			// we don't even have generic, something is really broken,
1047			// go with hardcoded generic icon
1048			if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1049				PRINT(
1050				("icon cache complete miss, falling back on generic "
1051				 "icon for %s\n", model->Name()));
1052				entry = GetFallbackIcon(sharedCacheLocker,
1053					&resultingOpenCache, model, mode, size, &lazyBitmap,
1054					entry);
1055			}
1056
1057			// force icon pick up next time around because we probably just
1058			// hit a node in transition
1059			model->SetIconFrom(kUnknownSource);
1060		}
1061	}
1062
1063	ASSERT(entry != NULL && entry->HaveIconBitmap(mode, size));
1064
1065	if (resultingCache != NULL)
1066		*resultingCache = resultingOpenCache;
1067
1068	return entry;
1069}
1070
1071
1072void
1073IconCache::Draw(Model* model, BView* view, BPoint where, IconDrawMode mode,
1074	icon_size size, bool async)
1075{
1076	// the following does not actually lock the caches, we are using the
1077	// lockLater mode; we will decide which of the two to lock down depending
1078	// on where we get the icon from
1079	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1080	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1081
1082	AutoLock<SimpleIconCache>* resultingCacheLocker;
1083	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1084		&resultingCacheLocker, model, mode, size, false);
1085		// Preload finds/creates the appropriate entry, locking down the
1086		// cache it is in and returns the whole state back to here
1087
1088	if (entry == NULL)
1089		return;
1090
1091	ASSERT(entry != NULL);
1092	ASSERT(entry->HaveIconBitmap(mode, size));
1093	// got the entry, now draw it
1094	resultingCacheLocker->LockedItem()->Draw(entry, view, where, mode,
1095		size, async);
1096
1097	// either of the two cache lockers that got locked down by this call get
1098	// unlocked at this point
1099}
1100
1101
1102void
1103IconCache::SyncDraw(Model* model, BView* view, BPoint where,
1104	IconDrawMode mode, icon_size size,
1105	void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1106	void* passThruState)
1107{
1108	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1109	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1110
1111	AutoLock<SimpleIconCache>* resultingCacheLocker;
1112	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1113		&resultingCacheLocker, model, mode, size, false);
1114
1115	if (entry == NULL)
1116		return;
1117
1118	ASSERT(entry != NULL);
1119	ASSERT(entry->HaveIconBitmap(mode, size));
1120	resultingCacheLocker->LockedItem()->Draw(entry, view, where,
1121		mode, size, blitFunc, passThruState);
1122}
1123
1124
1125void
1126IconCache::Preload(Model* model, IconDrawMode mode, icon_size size,
1127	bool permanent)
1128{
1129	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1130	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1131
1132	Preload(&nodeCacheLocker, &sharedCacheLocker, 0, model, mode, size,
1133		permanent);
1134}
1135
1136
1137status_t
1138IconCache::Preload(const char* fileType, IconDrawMode mode, icon_size size)
1139{
1140	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache);
1141	LazyBitmapAllocator lazyBitmap(size);
1142
1143	BMimeType mime(fileType);
1144	char preferredAppSig[B_MIME_TYPE_LENGTH];
1145	status_t result = mime.GetPreferredApp(preferredAppSig);
1146	if (result != B_OK)
1147		return result;
1148
1149	// try getting the icon from the preferred app for the signature
1150	IconCacheEntry* entry = GetIconForPreferredApp(fileType, preferredAppSig,
1151		mode, size, &lazyBitmap, 0);
1152	if (entry != NULL)
1153		return B_OK;
1154
1155	// try getting the icon directly from the metamime
1156	result = mime.GetIcon(lazyBitmap.Get(), size);
1157
1158	if (result != B_OK)
1159		return result;
1160
1161	entry = fSharedCache.AddItem(fileType);
1162	BBitmap* bitmap = lazyBitmap.Adopt();
1163	entry->SetIcon(bitmap, kNormalIcon, size);
1164	if (mode != kNormalIcon) {
1165		entry->ConstructBitmap(mode, size, &lazyBitmap);
1166		entry->SetIcon(lazyBitmap.Adopt(), mode, size);
1167	}
1168
1169	return B_OK;
1170}
1171
1172
1173void
1174IconCache::Deleting(const Model* model)
1175{
1176	AutoLock<SimpleIconCache> lock(&fNodeCache);
1177
1178	if (model->IconFrom() == kNode)
1179		fNodeCache.Deleting(model->NodeRef());
1180
1181	// don't care if the node uses the shared cache
1182}
1183
1184
1185void
1186IconCache::Removing(const Model* model)
1187{
1188	AutoLock<SimpleIconCache> lock(&fNodeCache);
1189
1190	if (model->IconFrom() == kNode)
1191		fNodeCache.Removing(model->NodeRef());
1192}
1193
1194
1195void
1196IconCache::Deleting(const BView* view)
1197{
1198	AutoLock<SimpleIconCache> lock(&fNodeCache);
1199	fNodeCache.Deleting(view);
1200}
1201
1202
1203void
1204IconCache::IconChanged(Model* model)
1205{
1206	AutoLock<SimpleIconCache> lock(&fNodeCache);
1207
1208	if (model->IconFrom() == kNode || model->IconFrom() == kVolume)
1209		fNodeCache.Deleting(model->NodeRef());
1210
1211	model->ResetIconFrom();
1212}
1213
1214
1215void
1216IconCache::IconChanged(const char* mimeType, const char* appSignature)
1217{
1218	AutoLock<SimpleIconCache> sharedLock(&fSharedCache);
1219	SharedCacheEntry* entry = fSharedCache.FindItem(mimeType, appSignature);
1220	if (entry == NULL)
1221		return;
1222
1223	AutoLock<SimpleIconCache> nodeLock(&fNodeCache);
1224
1225	entry = (SharedCacheEntry*)fSharedCache.ResolveIfAlias(entry);
1226	ASSERT(entry != NULL);
1227	int32 index = fSharedCache.EntryIndex(entry);
1228
1229	fNodeCache.RemoveAliasesTo(index);
1230	fSharedCache.RemoveAliasesTo(index);
1231
1232	fSharedCache.IconChanged(entry);
1233}
1234
1235
1236BBitmap*
1237IconCache::MakeSelectedIcon(const BBitmap* normal, icon_size size,
1238	LazyBitmapAllocator* lazyBitmap)
1239{
1240	return MakeTransformedIcon(normal, size, fHighlightTable, lazyBitmap);
1241}
1242
1243
1244#if xDEBUG
1245static void
1246DumpBitmap(const BBitmap* bitmap)
1247{
1248	if (bitmap == NULL) {
1249		printf("NULL bitmap passed to DumpBitmap\n");
1250		return;
1251	}
1252	int32 length = bitmap->BitsLength();
1253
1254	printf("data length %ld \n", length);
1255
1256	int32 columns = (int32)bitmap->Bounds().Width() + 1;
1257	const unsigned char* bitPtr = (const unsigned char*)bitmap->Bits();
1258	for (; length >= 0; length--) {
1259		for (int32 columnIndex = 0; columnIndex < columns;
1260			columnIndex++, length--)
1261			printf("%c%c", "0123456789ABCDEF"[(*bitPtr)/0x10],
1262				"0123456789ABCDEF"[(*bitPtr++)%0x10]);
1263
1264		printf("\n");
1265	}
1266	printf("\n");
1267}
1268#endif
1269
1270
1271void
1272IconCache::InitHighlightTable()
1273{
1274	// build the color transform tables for different icon modes
1275	BScreen screen(B_MAIN_SCREEN_ID);
1276	rgb_color color;
1277	for (int32 index = 0; index < kColorTransformTableSize; index++) {
1278		color = screen.ColorForIndex((uchar)index);
1279		fHighlightTable[index] = screen.IndexForColor(tint_color(color, 1.3f));
1280	}
1281
1282	fHighlightTable[B_TRANSPARENT_8_BIT] = B_TRANSPARENT_8_BIT;
1283	fInitHighlightTable = false;
1284}
1285
1286
1287BBitmap*
1288IconCache::MakeTransformedIcon(const BBitmap* source, icon_size /*size*/,
1289	int32 colorTransformTable[], LazyBitmapAllocator* lazyBitmap)
1290{
1291	if (fInitHighlightTable)
1292		InitHighlightTable();
1293
1294	BBitmap* result = lazyBitmap->Get();
1295	uint8* src = (uint8*)source->Bits();
1296	uint8* dst = (uint8*)result->Bits();
1297
1298//	ASSERT(result->ColorSpace() == source->ColorSpace()
1299//		&& result->Bounds() == source->Bounds());
1300	if (result->ColorSpace() != source->ColorSpace()
1301		|| result->Bounds() != source->Bounds()) {
1302		printf("IconCache::MakeTransformedIcon() - "
1303					"bitmap format mismatch!\n");
1304		return NULL;
1305	}
1306
1307	switch (result->ColorSpace()) {
1308		case B_RGB32:
1309		case B_RGBA32: {
1310			uint32 width = source->Bounds().IntegerWidth() + 1;
1311			uint32 height = source->Bounds().IntegerHeight() + 1;
1312			uint32 srcBPR = source->BytesPerRow();
1313			uint32 dstBPR = result->BytesPerRow();
1314			for (uint32 y = 0; y < height; y++) {
1315				uint8* d = dst;
1316				uint8* s = src;
1317				for (uint32 x = 0; x < width; x++) {
1318					// 66% brightness
1319					d[0] = (int)s[0] * 168 >> 8;
1320					d[1] = (int)s[1] * 168 >> 8;
1321					d[2] = (int)s[2] * 168 >> 8;
1322					d[3] = s[3];
1323					d += 4;
1324					s += 4;
1325				}
1326				dst += dstBPR;
1327				src += srcBPR;
1328			}
1329			break;
1330		}
1331
1332		case B_CMAP8: {
1333			int32 bitsLength = result->BitsLength();
1334			for (int32 i = 0; i < bitsLength; i++)
1335				*dst++ = (uint8)colorTransformTable[*src++];
1336			break;
1337		}
1338
1339		default:
1340			memset(dst, 0, result->BitsLength());
1341			// unkown colorspace, no tinting for you
1342			// "black" should make the problem stand out
1343			break;
1344	}
1345
1346	return result;
1347}
1348
1349
1350bool
1351IconCache::IconHitTest(BPoint where, const Model* model, IconDrawMode mode,
1352	icon_size size)
1353{
1354	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1355	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1356
1357	AutoLock<SimpleIconCache>* resultingCacheLocker;
1358	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1359		&resultingCacheLocker, const_cast<Model*>(model), mode, size, false);
1360		// Preload finds/creates the appropriate entry, locking down the
1361		// cache it is in and returns the whole state back to here
1362
1363	if (entry != NULL)
1364		return entry->IconHitTest(where, mode, size);
1365
1366	return false;
1367}
1368
1369
1370void
1371IconCacheEntry::RetireIcons(BObjectList<BBitmap>* retiredBitmapList)
1372{
1373	if (fLargeIcon != NULL) {
1374		retiredBitmapList->AddItem(fLargeIcon);
1375		fLargeIcon = NULL;
1376	}
1377	if (fHighlightedLargeIcon != NULL) {
1378		retiredBitmapList->AddItem(fHighlightedLargeIcon);
1379		fHighlightedLargeIcon = NULL;
1380	}
1381	if (fMiniIcon != NULL) {
1382		retiredBitmapList->AddItem(fMiniIcon);
1383		fMiniIcon = NULL;
1384	}
1385	if (fHighlightedMiniIcon != NULL) {
1386		retiredBitmapList->AddItem(fHighlightedMiniIcon);
1387		fHighlightedMiniIcon = NULL;
1388	}
1389
1390	int32 count = retiredBitmapList->CountItems();
1391	if (count > 10 * 1024) {
1392		PRINT(("nuking old icons from the retired bitmap list\n"));
1393		for (count = 512; count > 0; count--)
1394			delete retiredBitmapList->RemoveItemAt(0);
1395	}
1396}
1397
1398
1399//	#pragma mark - SharedIconCache
1400
1401
1402// In debug mode keep the hash table sizes small so that they grow a lot and
1403// execercise the resizing code a lot. In release mode allocate them large
1404// up-front for better performance
1405SharedIconCache::SharedIconCache()
1406	:
1407#if DEBUG
1408	SimpleIconCache("Shared icon cache aka \"The Dead-Locker\""),
1409	fElementArray(20),
1410	fHashTable(20),
1411	fRetiredBitmaps(20, true)
1412#else
1413	SimpleIconCache("Tracker shared icon cache"),
1414	fElementArray(1024),
1415	fHashTable(1000),
1416	fRetiredBitmaps(256, true)
1417#endif
1418{
1419	fHashTable.SetElementVector(&fElementArray);
1420}
1421
1422
1423void
1424SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1425	IconDrawMode mode, icon_size size, bool async)
1426{
1427	((SharedCacheEntry*)entry)->Draw(view, where, mode, size, async);
1428}
1429
1430
1431void
1432SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1433	IconDrawMode mode, icon_size size, void (*blitFunc)(BView*, BPoint,
1434	BBitmap*, void*), void* passThruState)
1435{
1436	((SharedCacheEntry*)entry)->Draw(view, where, mode, size,
1437		blitFunc, passThruState);
1438}
1439
1440
1441SharedCacheEntry*
1442SharedIconCache::FindItem(const char* fileType,
1443	const char* appSignature) const
1444{
1445	ASSERT(fileType);
1446	if (!fileType)
1447		fileType = B_FILE_MIMETYPE;
1448
1449	SharedCacheEntry* result
1450		= fHashTable.FindFirst(SharedCacheEntry::Hash(fileType,
1451			appSignature));
1452
1453	if (result == NULL)
1454		return NULL;
1455
1456	for(;;) {
1457		if (result->fFileType == fileType
1458			&& result->fAppSignature == appSignature) {
1459			return result;
1460		}
1461
1462		if (result->fNext < 0)
1463			break;
1464
1465		result = const_cast<SharedCacheEntry*>(&fElementArray.At(
1466			result->fNext));
1467	}
1468
1469	return NULL;
1470}
1471
1472
1473SharedCacheEntry*
1474SharedIconCache::AddItem(const char* fileType, const char* appSignature)
1475{
1476	ASSERT(fileType != NULL);
1477	if (fileType == NULL)
1478		fileType = B_FILE_MIMETYPE;
1479
1480	SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType,
1481		appSignature));
1482	result->SetTo(fileType, appSignature);
1483
1484	return result;
1485}
1486
1487
1488SharedCacheEntry*
1489SharedIconCache::AddItem(SharedCacheEntry** outstandingEntry,
1490	const char* fileType, const char* appSignature)
1491{
1492	int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
1493	ASSERT(entryToken >= 0);
1494
1495	ASSERT(fileType != NULL);
1496	if (fileType == NULL)
1497		fileType = B_FILE_MIMETYPE;
1498
1499	SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType,
1500		appSignature));
1501	result->SetTo(fileType, appSignature);
1502	*outstandingEntry = fHashTable.ElementAt(entryToken);
1503
1504	return result;
1505}
1506
1507
1508void
1509SharedIconCache::IconChanged(SharedCacheEntry* entry)
1510{
1511	// by now there should be no aliases to entry, just remove entry
1512	// itself
1513	ASSERT(entry->fAliasForIndex == -1);
1514	entry->RetireIcons(&fRetiredBitmaps);
1515	fHashTable.Remove(entry);
1516}
1517
1518
1519void
1520SharedIconCache::RemoveAliasesTo(int32 aliasIndex)
1521{
1522	int32 count = fHashTable.VectorSize();
1523	for (int32 index = 0; index < count; index++) {
1524		SharedCacheEntry* entry = fHashTable.ElementAt(index);
1525		if (entry->fAliasForIndex == aliasIndex)
1526			fHashTable.Remove(entry);
1527	}
1528}
1529
1530
1531void
1532SharedIconCache::SetAliasFor(IconCacheEntry* entry,
1533	const SharedCacheEntry* original) const
1534{
1535	entry->fAliasForIndex = fHashTable.ElementIndex(original);
1536}
1537
1538
1539SharedCacheEntry::SharedCacheEntry()
1540	:
1541	fNext(-1)
1542{
1543}
1544
1545
1546SharedCacheEntry::SharedCacheEntry(const char* fileType,
1547	const char* appSignature)
1548	:
1549	fNext(-1),
1550	fFileType(fileType),
1551	fAppSignature(appSignature)
1552{
1553}
1554
1555
1556void
1557SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1558	icon_size size, bool async)
1559{
1560	BBitmap* bitmap = IconForMode(mode, size);
1561	ASSERT(bitmap != NULL);
1562
1563	drawing_mode oldMode = view->DrawingMode();
1564
1565	if (bitmap->ColorSpace() == B_RGBA32) {
1566		if (oldMode != B_OP_ALPHA) {
1567			view->SetDrawingMode(B_OP_ALPHA);
1568			view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1569		}
1570	} else
1571		view->SetDrawingMode(B_OP_OVER);
1572
1573	if (async)
1574		view->DrawBitmapAsync(bitmap, where);
1575	else
1576		view->DrawBitmap(bitmap, where);
1577
1578	view->SetDrawingMode(oldMode);
1579}
1580
1581
1582void
1583SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1584	icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1585	void* passThruState)
1586{
1587	BBitmap* bitmap = IconForMode(mode, size);
1588	if (bitmap == NULL)
1589		return;
1590
1591	(blitFunc)(view, where, bitmap, passThruState);
1592}
1593
1594
1595uint32
1596SharedCacheEntry::Hash(const char* fileType, const char* appSignature)
1597{
1598	uint32 hash = HashString(fileType, 0);
1599	if (appSignature != NULL && *appSignature != '\0')
1600		hash = HashString(appSignature, hash);
1601
1602	return hash;
1603}
1604
1605
1606uint32
1607SharedCacheEntry::Hash() const
1608{
1609	uint32 hash = HashString(fFileType.String(), 0);
1610	if (fAppSignature.Length() > 0)
1611		hash = HashString(fAppSignature.String(), hash);
1612
1613	return hash;
1614}
1615
1616
1617bool
1618SharedCacheEntry::operator==(const SharedCacheEntry &entry) const
1619{
1620	return fFileType == entry.FileType()
1621		&& fAppSignature == entry.AppSignature();
1622}
1623
1624
1625void
1626SharedCacheEntry::SetTo(const char* fileType, const char* appSignature)
1627{
1628	fFileType = fileType;
1629	fAppSignature = appSignature;
1630}
1631
1632
1633SharedCacheEntryArray::SharedCacheEntryArray(int32 initialSize)
1634	:
1635	OpenHashElementArray<SharedCacheEntry>(initialSize)
1636{
1637}
1638
1639
1640SharedCacheEntry*
1641SharedCacheEntryArray::Add()
1642{
1643	return OpenHashElementArray<SharedCacheEntry>::Add();
1644}
1645
1646
1647//	#pragma mark - NodeCacheEntry
1648
1649
1650NodeCacheEntry::NodeCacheEntry(bool permanent)
1651	:
1652	fNext(-1),
1653	fPermanent(permanent)
1654{
1655}
1656
1657
1658NodeCacheEntry::NodeCacheEntry(const node_ref* node, bool permanent)
1659	:
1660	fNext(-1),
1661	fRef(*node),
1662	fPermanent(permanent)
1663{
1664}
1665
1666
1667void
1668NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1669	icon_size size, bool async)
1670{
1671	BBitmap* bitmap = IconForMode(mode, size);
1672	if (bitmap == NULL)
1673		return;
1674
1675	drawing_mode oldMode = view->DrawingMode();
1676
1677	if (bitmap->ColorSpace() == B_RGBA32) {
1678		if (oldMode != B_OP_ALPHA) {
1679			view->SetDrawingMode(B_OP_ALPHA);
1680			view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1681		}
1682	} else
1683		view->SetDrawingMode(B_OP_OVER);
1684
1685	if (false && async) {
1686		TRESPASS();
1687		// need to copy the bits first in here
1688		view->DrawBitmapAsync(bitmap, where);
1689	} else
1690		view->DrawBitmap(bitmap, where);
1691
1692	view->SetDrawingMode(oldMode);
1693}
1694
1695
1696void
1697NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1698	icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1699	void* passThruState)
1700{
1701	BBitmap* bitmap = IconForMode(mode, size);
1702	if (bitmap == NULL)
1703		return;
1704
1705	(blitFunc)(view, where, bitmap, passThruState);
1706}
1707
1708
1709const node_ref*
1710NodeCacheEntry::Node() const
1711{
1712	return &fRef;
1713}
1714
1715
1716uint32
1717NodeCacheEntry::Hash() const
1718{
1719	return Hash(&fRef);
1720}
1721
1722
1723uint32
1724NodeCacheEntry::Hash(const node_ref* node)
1725{
1726	return node->device ^ ((uint32*)&node->node)[0]
1727		^ ((uint32*)&node->node)[1];
1728}
1729
1730
1731bool
1732NodeCacheEntry::operator==(const NodeCacheEntry &entry) const
1733{
1734	return fRef == entry.fRef;
1735}
1736
1737
1738void
1739NodeCacheEntry::SetTo(const node_ref* node)
1740{
1741	fRef = *node;
1742}
1743
1744
1745bool
1746NodeCacheEntry::Permanent() const
1747{
1748	return fPermanent;
1749}
1750
1751
1752void
1753NodeCacheEntry::MakePermanent()
1754{
1755	fPermanent = true;
1756}
1757
1758
1759//	#pragma mark - NodeIconCache
1760
1761
1762NodeIconCache::NodeIconCache()
1763	:
1764#if DEBUG
1765	SimpleIconCache("Node icon cache aka \"The Dead-Locker\""),
1766	fElementArray(20),
1767	fHashTable(20)
1768#else
1769	SimpleIconCache("Tracker node icon cache"),
1770	fElementArray(100),
1771	fHashTable(100)
1772#endif
1773{
1774	fHashTable.SetElementVector(&fElementArray);
1775}
1776
1777
1778void
1779NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1780	IconDrawMode mode, icon_size size, bool async)
1781{
1782	((NodeCacheEntry*)entry)->Draw(view, where, mode, size, async);
1783}
1784
1785
1786void
1787NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1788	IconDrawMode mode, icon_size size,
1789	void (*blitFunc)(BView*, BPoint, BBitmap*, void*), void* passThruState)
1790{
1791	((NodeCacheEntry*)entry)->Draw(view, where, mode, size,
1792		blitFunc, passThruState);
1793}
1794
1795
1796NodeCacheEntry*
1797NodeIconCache::FindItem(const node_ref* node) const
1798{
1799	NodeCacheEntry* entry = fHashTable.FindFirst(NodeCacheEntry::Hash(node));
1800	if (entry == NULL)
1801		return NULL;
1802
1803	for(;;) {
1804		if (*entry->Node() == *node)
1805			return entry;
1806
1807		if (entry->fNext < 0)
1808			break;
1809
1810		entry = const_cast<NodeCacheEntry*>(&fElementArray.At(entry->fNext));
1811	}
1812
1813	return NULL;
1814}
1815
1816
1817NodeCacheEntry*
1818NodeIconCache::AddItem(const node_ref* node, bool permanent)
1819{
1820	NodeCacheEntry* entry = fHashTable.Add(NodeCacheEntry::Hash(node));
1821	entry->SetTo(node);
1822	if (permanent)
1823		entry->MakePermanent();
1824
1825	return entry;
1826}
1827
1828
1829NodeCacheEntry*
1830NodeIconCache::AddItem(NodeCacheEntry** outstandingEntry,
1831	const node_ref* node)
1832{
1833	int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
1834
1835	NodeCacheEntry* entry = fHashTable.Add(NodeCacheEntry::Hash(node));
1836	entry->SetTo(node);
1837	*outstandingEntry = fHashTable.ElementAt(entryToken);
1838
1839	return entry;
1840}
1841
1842
1843void
1844NodeIconCache::Deleting(const node_ref* node)
1845{
1846	NodeCacheEntry* entry = FindItem(node);
1847	ASSERT(entry != NULL);
1848	if (entry == NULL || entry->Permanent())
1849		return;
1850
1851	fHashTable.Remove(entry);
1852}
1853
1854
1855void
1856NodeIconCache::Removing(const node_ref* node)
1857{
1858	NodeCacheEntry* entry = FindItem(node);
1859	ASSERT(entry != NULL);
1860	if (entry == NULL)
1861		return;
1862
1863	fHashTable.Remove(entry);
1864}
1865
1866
1867void
1868NodeIconCache::Deleting(const BView*)
1869{
1870#ifdef NODE_CACHE_ASYNC_DRAWS
1871	TRESPASS();
1872#endif
1873}
1874
1875
1876void
1877NodeIconCache::IconChanged(const Model* model)
1878{
1879	Deleting(model->NodeRef());
1880}
1881
1882
1883void
1884NodeIconCache::RemoveAliasesTo(int32 aliasIndex)
1885{
1886	int32 count = fHashTable.VectorSize();
1887	for (int32 index = 0; index < count; index++) {
1888		NodeCacheEntry* entry = fHashTable.ElementAt(index);
1889		if (entry->fAliasForIndex == aliasIndex)
1890			fHashTable.Remove(entry);
1891	}
1892}
1893
1894
1895//	#pragma mark - NodeCacheEntryArray
1896
1897
1898NodeCacheEntryArray::NodeCacheEntryArray(int32 initialSize)
1899	:
1900	OpenHashElementArray<NodeCacheEntry>(initialSize)
1901{
1902}
1903
1904
1905NodeCacheEntry*
1906NodeCacheEntryArray::Add()
1907{
1908	return OpenHashElementArray<NodeCacheEntry>::Add();
1909}
1910
1911
1912//	#pragma mark - SimpleIconCache
1913
1914
1915SimpleIconCache::SimpleIconCache(const char* name)
1916	:
1917	fLock(name)
1918{
1919}
1920
1921
1922void
1923SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1924	icon_size, bool)
1925{
1926	TRESPASS();
1927	// pure virtual, do nothing
1928}
1929
1930
1931void
1932SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1933	icon_size, void(*)(BView*, BPoint, BBitmap*, void*), void*)
1934{
1935	TRESPASS();
1936	// pure virtual, do nothing
1937}
1938
1939
1940bool
1941SimpleIconCache::Lock()
1942{
1943	return fLock.Lock();
1944}
1945
1946
1947void
1948SimpleIconCache::Unlock()
1949{
1950	fLock.Unlock();
1951}
1952
1953
1954bool
1955SimpleIconCache::IsLocked() const
1956{
1957	return fLock.IsLocked();
1958}
1959
1960
1961//	#pragma mark - LazyBitmapAllocator
1962
1963
1964LazyBitmapAllocator::LazyBitmapAllocator(icon_size size,
1965	color_space colorSpace, bool preallocate)
1966	:
1967	fBitmap(NULL),
1968	fSize(size),
1969	fColorSpace(colorSpace)
1970{
1971	if (preallocate)
1972		Get();
1973}
1974
1975
1976LazyBitmapAllocator::~LazyBitmapAllocator()
1977{
1978	delete fBitmap;
1979}
1980
1981
1982BBitmap*
1983LazyBitmapAllocator::Get()
1984{
1985	if (fBitmap == NULL)
1986		fBitmap = new BBitmap(BRect(0, 0, fSize - 1, fSize - 1), fColorSpace);
1987
1988	return fBitmap;
1989}
1990
1991
1992BBitmap*
1993LazyBitmapAllocator::Adopt()
1994{
1995	if (fBitmap == NULL)
1996		Get();
1997
1998	BBitmap* bitmap = fBitmap;
1999	fBitmap = NULL;
2000
2001	return bitmap;
2002}
2003
2004
2005IconCache* IconCache::sIconCache;
2006