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