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