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