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