xref: /haiku/src/kits/tracker/IconCache.cpp (revision 12dba4e70f831d6d27a7f769cc9dab19c19a155d)
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 	fAliasForIndex(-1)
111 {
112 }
113 
114 
115 IconCacheEntry::~IconCacheEntry()
116 {
117 	if (fAliasForIndex < 0) {
118 		delete fLargeIcon;
119 		delete fHighlightedLargeIcon;
120 		delete fMiniIcon;
121 		delete fHighlightedMiniIcon;
122 
123 		// clean up a bit to leave the hash table entry in an initialized state
124 		fLargeIcon = NULL;
125 		fHighlightedLargeIcon = NULL;
126 		fMiniIcon = NULL;
127 		fHighlightedMiniIcon = NULL;
128 	}
129 	fAliasForIndex = -1;
130 }
131 
132 
133 void
134 IconCacheEntry::SetAliasFor(const SharedIconCache* sharedCache,
135 	const SharedCacheEntry* entry)
136 {
137 	sharedCache->SetAliasFor(this, entry);
138 	ASSERT(fAliasForIndex >= 0);
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(&aliasTo, fileType);
431 				entry->SetAliasFor(&fSharedCache, aliasTo);
432 			}
433 			ASSERT(aliasTo->HaveIconBitmap(mode, size));
434 			return aliasTo;
435 		}
436 
437 		// at this point, we've found an icon for the MIME type
438 		BBitmap* bitmap = lazyBitmap->Adopt();
439 		if (entry == NULL) {
440 			PRINT_ADD_ITEM(("File %s; Line %d # adding entry for type %s\n",
441 				__FILE__, __LINE__, fileType));
442 			entry = fSharedCache.AddItem(fileType);
443 		}
444 		entry->SetIcon(bitmap, kNormalIcon, size);
445 	}
446 
447 	ASSERT(entry != NULL);
448 
449 	if (mode != kNormalIcon
450 		&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
451 		entry->ConstructBitmap(mode, size, lazyBitmap);
452 		entry->SetIcon(lazyBitmap->Adopt(), mode, size);
453 	}
454 
455 #if xDEBUG
456 	if (!entry->HaveIconBitmap(mode, size))
457 		PRINT(("failing on %s, mode %ld, size %ld\n", fileType, mode, size));
458 #endif
459 
460 	ASSERT(entry->HaveIconBitmap(mode, size));
461 
462 	return entry;
463 }
464 
465 
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((SharedCacheEntry**)&entry, fileType,
535 					nodePreferredApp);
536 			aliasedEntry->SetAliasFor(&fSharedCache,
537 				(SharedCacheEntry*)entry);
538 				// OK to cast here, have a runtime check
539 			source = kPreferredAppForNode;
540 				// set source as preferred for node, so that next time we
541 				// get a hit in the initial find that uses
542 				// GetIconForPreferredApp
543 		} else
544 			source = kMetaMime;
545 
546 #if DEBUG
547 		if (!entry->HaveIconBitmap(mode, size))
548 			model->PrintToStream();
549 #endif
550 		ASSERT(entry->HaveIconBitmap(mode, size));
551 	}
552 
553 	return entry;
554 }
555 
556 IconCacheEntry*
557 IconCache::GetVolumeIcon(AutoLock<SimpleIconCache>*nodeCacheLocker,
558 	AutoLock<SimpleIconCache>* sharedCacheLocker,
559 	AutoLock<SimpleIconCache>** resultingOpenCache,
560 	Model* model, IconSource &source,
561 	IconDrawMode mode, icon_size size, LazyBitmapAllocator* lazyBitmap)
562 {
563 	*resultingOpenCache = nodeCacheLocker;
564 	nodeCacheLocker->Lock();
565 
566 	IconCacheEntry* entry = 0;
567 	if (source != kUnknownSource) {
568 		// cached in the node cache
569 		entry = fNodeCache.FindItem(model->NodeRef());
570 		if (entry != NULL) {
571 			entry = IconCacheEntry::ResolveIfAlias(&fSharedCache, entry);
572 
573 			if (source == kTrackerDefault) {
574 				// if tracker default, resolved entry is from shared cache
575 				// this could be done a little cleaner if entry had a way to
576 				// reach the cache it is in
577 				*resultingOpenCache = sharedCacheLocker;
578 				sharedCacheLocker->Lock();
579 			}
580 
581 			if (entry->HaveIconBitmap(mode, size))
582 				return entry;
583 		}
584 	}
585 
586 	// try getting using the BVolume::GetIcon call; if miss,
587 	// go for the default mime based icon
588 	if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
589 		BVolume volume(model->NodeRef()->device);
590 
591 		if (volume.IsShared()) {
592 			// check if it's a network share and give it a special icon
593 			BBitmap* bitmap = lazyBitmap->Get();
594 			GetTrackerResources()->GetIconResource(R_ShareIcon, size, bitmap);
595 			if (entry == NULL) {
596 				PRINT_ADD_ITEM(
597 					("File %s; Line %d # adding entry for model %s\n",
598 					__FILE__, __LINE__, model->Name()));
599 				entry = fNodeCache.AddItem(model->NodeRef());
600 			}
601 			entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size);
602 		} else if (volume.GetIcon(lazyBitmap->Get(), size) == B_OK) {
603 			// ask the device for an icon
604 			BBitmap* bitmap = lazyBitmap->Adopt();
605 			ASSERT(bitmap != NULL);
606 			if (entry == NULL) {
607 				PRINT_ADD_ITEM(
608 					("File %s; Line %d # adding entry for model %s\n",
609 					__FILE__, __LINE__, model->Name()));
610 				entry = fNodeCache.AddItem(model->NodeRef());
611 			}
612 			ASSERT(entry != NULL);
613 			entry->SetIcon(bitmap, kNormalIcon, size);
614 			source = kVolume;
615 		} else {
616 			*resultingOpenCache = sharedCacheLocker;
617 			sharedCacheLocker->Lock();
618 
619 			// if the volume doesnt have a device it gets the generic icon
620 			entry = GetIconFromMetaMime(B_VOLUME_MIMETYPE, mode,
621 				size, lazyBitmap, entry);
622 		}
623 	}
624 
625 	if (mode != kNormalIcon && entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
626 		entry->ConstructBitmap(mode, size, lazyBitmap);
627 		entry->SetIcon(lazyBitmap->Adopt(), mode, size);
628 	}
629 
630 	return entry;
631 }
632 
633 
634 IconCacheEntry*
635 IconCache::GetRootIcon(AutoLock<SimpleIconCache>*,
636 	AutoLock<SimpleIconCache>* sharedCacheLocker,
637 	AutoLock<SimpleIconCache>** resultingOpenCache,
638 	Model*, IconSource &source, IconDrawMode mode,
639 	icon_size size, LazyBitmapAllocator* lazyBitmap)
640 {
641 	*resultingOpenCache = sharedCacheLocker;
642 	(*resultingOpenCache)->Lock();
643 
644 	source = kTrackerSupplied;
645 
646 	return GetIconFromMetaMime(B_ROOT_MIMETYPE, mode, size, lazyBitmap, 0);
647 }
648 
649 
650 IconCacheEntry*
651 IconCache::GetWellKnownIcon(AutoLock<SimpleIconCache>*,
652 	AutoLock<SimpleIconCache>* sharedCacheLocker,
653 	AutoLock<SimpleIconCache>** resultingOpenCache,
654 	Model* model, IconSource &source, IconDrawMode mode, icon_size size,
655 	LazyBitmapAllocator* lazyBitmap)
656 {
657 	const WellKnowEntryList::WellKnownEntry* wellKnownEntry
658 		= WellKnowEntryList::MatchEntry(model->NodeRef());
659 	if (wellKnownEntry == NULL)
660 		return NULL;
661 
662 	BString type("tracker/active_");
663 	type += wellKnownEntry->name;
664 
665 	*resultingOpenCache = sharedCacheLocker;
666 	(*resultingOpenCache)->Lock();
667 
668 	source = kTrackerSupplied;
669 
670 	IconCacheEntry* entry = fSharedCache.FindItem(type.String());
671 	if (entry != NULL) {
672 		entry = entry->ResolveIfAlias(&fSharedCache, entry);
673 		if (entry->HaveIconBitmap(mode, size))
674 			return entry;
675 	}
676 
677 	if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
678 		// match up well known entries in the file system with specialized
679 		// icons stored in Tracker's resources
680 		int32 resourceId = -1;
681 		switch ((uint32)wellKnownEntry->which) {
682 			case B_BOOT_DISK:
683 				resourceId = R_BootVolumeIcon;
684 				break;
685 
686 			case B_BEOS_DIRECTORY:
687 				resourceId = R_BeosFolderIcon;
688 				break;
689 
690 			case B_USER_DIRECTORY:
691 				resourceId = R_HomeDirIcon;
692 				break;
693 
694 			case B_SYSTEM_FONTS_DIRECTORY:
695 			case B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY:
696 			case B_USER_FONTS_DIRECTORY:
697 			case B_USER_NONPACKAGED_FONTS_DIRECTORY:
698 				resourceId = R_FontDirIcon;
699 				break;
700 
701 			case B_BEOS_APPS_DIRECTORY:
702 			case B_APPS_DIRECTORY:
703 			case B_USER_DESKBAR_APPS_DIRECTORY:
704 				resourceId = R_AppsDirIcon;
705 				break;
706 
707 			case B_BEOS_PREFERENCES_DIRECTORY:
708 			case B_PREFERENCES_DIRECTORY:
709 			case B_USER_DESKBAR_PREFERENCES_DIRECTORY:
710 				resourceId = R_PrefsDirIcon;
711 				break;
712 
713 			case B_USER_MAIL_DIRECTORY:
714 				resourceId = R_MailDirIcon;
715 				break;
716 
717 			case B_USER_QUERIES_DIRECTORY:
718 				resourceId = R_QueryDirIcon;
719 				break;
720 
721 			case B_SYSTEM_DEVELOP_DIRECTORY:
722 			case B_SYSTEM_NONPACKAGED_DEVELOP_DIRECTORY:
723 			case B_USER_DESKBAR_DEVELOP_DIRECTORY:
724 				resourceId = R_DevelopDirIcon;
725 				break;
726 
727 			case B_USER_CONFIG_DIRECTORY:
728 				resourceId = R_ConfigDirIcon;
729 				break;
730 
731 			case B_USER_PEOPLE_DIRECTORY:
732 				resourceId = R_PersonDirIcon;
733 				break;
734 
735 			case B_USER_DOWNLOADS_DIRECTORY:
736 				resourceId = R_DownloadDirIcon;
737 				break;
738 
739 			default:
740 				return NULL;
741 		}
742 
743 		entry = fSharedCache.AddItem(type.String());
744 
745 		BBitmap* bitmap = lazyBitmap->Get();
746 		GetTrackerResources()->GetIconResource(resourceId, size, bitmap);
747 		entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size);
748 	}
749 
750 	if (mode != kNormalIcon
751 		&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
752 		entry->ConstructBitmap(mode, size, lazyBitmap);
753 		entry->SetIcon(lazyBitmap->Adopt(), mode, size);
754 	}
755 
756 	ASSERT(entry->HaveIconBitmap(mode, size));
757 
758 	return entry;
759 }
760 
761 
762 IconCacheEntry*
763 IconCache::GetNodeIcon(ModelNodeLazyOpener* modelOpener,
764 	AutoLock<SimpleIconCache>* nodeCacheLocker,
765 	AutoLock<SimpleIconCache>** resultingOpenCache,
766 	Model* model, IconSource& source,
767 	IconDrawMode mode, icon_size size,
768 	LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry, bool permanent)
769 {
770 	*resultingOpenCache = nodeCacheLocker;
771 	(*resultingOpenCache)->Lock();
772 
773 	entry = fNodeCache.FindItem(model->NodeRef());
774 	if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
775 		modelOpener->OpenNode();
776 
777 		BFile* file = NULL;
778 
779 		// if we are dealing with an application, use the BAppFileInfo
780 		// superset of node; this makes GetIcon grab the proper icon for
781 		// an app
782 		if (model->IsExecutable())
783 			file = dynamic_cast<BFile*>(model->Node());
784 
785 		PRINT_DISK_HITS(("File %s; Line %d # hitting disk for node %s\n",
786 			__FILE__, __LINE__, model->Name()));
787 
788 		status_t result = file != NULL
789 			? GetAppIconFromAttr(file, lazyBitmap->Get(), size)
790 			: GetFileIconFromAttr(model->Node(), lazyBitmap->Get(), size);
791 
792 		if (result == B_OK) {
793 			// node has its own icon, use it
794 			BBitmap* bitmap = lazyBitmap->Adopt();
795 			PRINT_ADD_ITEM(("File %s; Line %d # adding entry for model %s\n",
796 				__FILE__, __LINE__, model->Name()));
797 			entry = fNodeCache.AddItem(model->NodeRef(), permanent);
798 			ASSERT(entry != NULL);
799 			entry->SetIcon(bitmap, kNormalIcon, size);
800 			if (mode != kNormalIcon) {
801 				entry->ConstructBitmap(mode, size, lazyBitmap);
802 				entry->SetIcon(lazyBitmap->Adopt(), mode, size);
803 			}
804 			source = kNode;
805 		}
806 	}
807 
808 	if (entry == NULL) {
809 		(*resultingOpenCache)->Unlock();
810 		*resultingOpenCache = NULL;
811 	} else if (!entry->HaveIconBitmap(mode, size)
812 		&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
813 		entry->ConstructBitmap(mode, size, lazyBitmap);
814 		entry->SetIcon(lazyBitmap->Adopt(), mode, size);
815 		ASSERT(entry->HaveIconBitmap(mode, size));
816 	}
817 
818 	return entry;
819 }
820 
821 
822 IconCacheEntry*
823 IconCache::GetGenericIcon(AutoLock<SimpleIconCache>* sharedCacheLocker,
824 	AutoLock<SimpleIconCache>** resultingOpenCache,
825 	Model* model, IconSource &source,
826 	IconDrawMode mode, icon_size size,
827 	LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
828 {
829 	*resultingOpenCache = sharedCacheLocker;
830 	(*resultingOpenCache)->Lock();
831 
832 	entry = GetIconFromMetaMime(B_FILE_MIMETYPE, mode, size, lazyBitmap, 0);
833 	if (entry == NULL)
834 		return NULL;
835 
836 	// make an aliased entry so that the next time we get a
837 	// hit and substitute a generic icon right away
838 	PRINT_ADD_ITEM(
839 		("File %s; Line %d # adding entry for preferredApp %s, type %s\n",
840 		__FILE__, __LINE__, model->PreferredAppSignature(),
841 		model->MimeType()));
842 	IconCacheEntry* aliasedEntry = fSharedCache.AddItem(
843 		(SharedCacheEntry**)&entry, model->MimeType(),
844 		model->PreferredAppSignature());
845 	aliasedEntry->SetAliasFor(&fSharedCache, (SharedCacheEntry*)entry);
846 
847 	source = kMetaMime;
848 
849 	ASSERT(entry->HaveIconBitmap(mode, size));
850 
851 	return entry;
852 }
853 
854 
855 IconCacheEntry*
856 IconCache::GetFallbackIcon(AutoLock<SimpleIconCache>* sharedCacheLocker,
857 	AutoLock<SimpleIconCache>** resultingOpenCache,
858 	Model* model, IconDrawMode mode, icon_size size,
859 	LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
860 {
861 	*resultingOpenCache = sharedCacheLocker;
862 	(*resultingOpenCache)->Lock();
863 
864 	entry = fSharedCache.AddItem(model->MimeType(),
865 		model->PreferredAppSignature());
866 
867 	BBitmap* bitmap = lazyBitmap->Get();
868 	GetTrackerResources()->GetIconResource(R_FileIcon, size, bitmap);
869 	entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size);
870 
871 	if (mode != kNormalIcon) {
872 		entry->ConstructBitmap(mode, size, lazyBitmap);
873 		entry->SetIcon(lazyBitmap->Adopt(), mode, size);
874 	}
875 
876 	ASSERT(entry->HaveIconBitmap(mode, size));
877 
878 	return entry;
879 }
880 
881 
882 IconCacheEntry*
883 IconCache::Preload(AutoLock<SimpleIconCache>* nodeCacheLocker,
884 	AutoLock<SimpleIconCache>* sharedCacheLocker,
885 	AutoLock<SimpleIconCache>** resultingCache,
886 	Model* model, IconDrawMode mode, icon_size size,
887 	bool permanent)
888 {
889 	IconCacheEntry* entry = NULL;
890 
891 	AutoLock<SimpleIconCache>* resultingOpenCache = NULL;
892 		// resultingOpenCache is the locker that points to the cache that
893 		// ended with a hit and will be used for the drawing
894 
895 	{
896 		// scope for modelOpener
897 
898 		ModelNodeLazyOpener modelOpener(model);
899 			// this opener takes care of opening the model and possibly
900 			// closing it when we are done
901 		LazyBitmapAllocator lazyBitmap(size);
902 			// lazyBitmap manages bitmap allocation and freeing if needed
903 
904 		IconSource source = model->IconFrom();
905 		if (source == kUnknownSource || source == kUnknownNotFromNode) {
906 			// fish for special first models and handle them appropriately
907 			if (model->IsVolume()) {
908 				// volume may use specialized icon in the volume node
909 				entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
910 					&resultingOpenCache, model, source, mode, size,
911 					&lazyBitmap, entry, permanent);
912 				if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
913 					// look for volume defined icon
914 					entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker,
915 						&resultingOpenCache, model, source, mode,
916 						size, &lazyBitmap);
917 				}
918 			} else if (model->IsRoot()) {
919 				entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker,
920 					&resultingOpenCache, model, source, mode, size,
921 						&lazyBitmap);
922 				ASSERT(entry != NULL);
923 			} else {
924 				if (source == kUnknownSource) {
925 					// look for node icons first
926 					entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
927 						&resultingOpenCache, model, source,
928 						mode, size, &lazyBitmap, entry, permanent);
929 				}
930 
931 				if (entry == NULL) {
932 					// no node icon, look for file type based one
933 					modelOpener.OpenNode();
934 					// use file types to get the icon
935 					resultingOpenCache = sharedCacheLocker;
936 					resultingOpenCache->Lock();
937 
938 					entry = GetIconFromFileTypes(&modelOpener, source, mode,
939 						size, &lazyBitmap, 0);
940 					if (entry == NULL) {
941 						// we don't have an icon, go with the generic
942 						entry = GetGenericIcon(sharedCacheLocker,
943 							&resultingOpenCache, model, source, mode,
944 							size, &lazyBitmap, entry);
945 					}
946 				}
947 			}
948 
949 			// update the icon source
950 			model->SetIconFrom(source);
951 		} else {
952 			// we already know where the icon should come from,
953 			// use shortcuts to get it
954 			switch (source) {
955 				case kNode:
956 					resultingOpenCache = nodeCacheLocker;
957 					resultingOpenCache->Lock();
958 
959 					entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
960 						&resultingOpenCache, model, source, mode,
961 						size, &lazyBitmap, entry, permanent);
962 					if (entry != NULL) {
963 						entry = IconCacheEntry::ResolveIfAlias(&fSharedCache,
964 							entry);
965 						if (!entry->HaveIconBitmap(mode, size)
966 							&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
967 							entry->ConstructBitmap(mode, size, &lazyBitmap);
968 							entry->SetIcon(lazyBitmap.Adopt(), mode, size);
969 						}
970 						ASSERT(entry->HaveIconBitmap(mode, size));
971 					}
972 					break;
973 				case kTrackerSupplied:
974 					if (model->IsRoot()) {
975 						entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker,
976 							&resultingOpenCache, model, source, mode, size,
977 							&lazyBitmap);
978 						break;
979 					} else {
980 						entry = GetWellKnownIcon(nodeCacheLocker,
981 							sharedCacheLocker, &resultingOpenCache, model,
982 							source, mode, size, &lazyBitmap);
983 						if (entry != NULL)
984 							break;
985 					}
986 					// fall through
987 				case kTrackerDefault:
988 				case kVolume:
989 					if (model->IsVolume()) {
990 						entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
991 							&resultingOpenCache, model, source,
992 							mode, size, &lazyBitmap, entry, permanent);
993 						if (entry == NULL
994 							|| !entry->HaveIconBitmap(mode, size)) {
995 							entry = GetVolumeIcon(nodeCacheLocker,
996 								sharedCacheLocker, &resultingOpenCache, model,
997 								source, mode, size, &lazyBitmap);
998 						}
999 						break;
1000 					}
1001 					// fall through
1002 				case kMetaMime:
1003 				case kPreferredAppForType:
1004 				case kPreferredAppForNode:
1005 					resultingOpenCache = sharedCacheLocker;
1006 					resultingOpenCache->Lock();
1007 
1008 					entry = GetIconFromFileTypes(&modelOpener, source, mode,
1009 						size, &lazyBitmap, 0);
1010 					ASSERT(entry != NULL || entry->HaveIconBitmap(mode, size));
1011 
1012 					if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1013 						// we don't have an icon, go with the generic
1014 						entry = GetGenericIcon(sharedCacheLocker,
1015 							&resultingOpenCache, model, source, mode, size,
1016 							&lazyBitmap, entry);
1017 					}
1018 
1019 					model->SetIconFrom(source);
1020 						// The source shouldn't change in this case; if it does
1021 						// though we might never be hitting the correct icon and
1022 						// instead keep leaking entries after each miss this now
1023 						// happens if an app defines an icon but a
1024 						// GetIconForType() fails and we fall back to generic
1025 						// icon.
1026 						// ToDo: fix this and add an assert to the effect
1027 
1028 					ASSERT(entry != NULL);
1029 					ASSERT(entry->HaveIconBitmap(mode, size));
1030 					break;
1031 
1032 				default:
1033 					TRESPASS();
1034 					break;
1035 			}
1036 		}
1037 
1038 		if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1039 			// we don't have an icon, go with the generic
1040 			PRINT(
1041 				("icon cache complete miss, falling back on generic icon "
1042 				 "for %s\n", model->Name()));
1043 			entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
1044 				model, source, mode, size, &lazyBitmap, entry);
1045 
1046 			// we don't even have generic, something is really broken,
1047 			// go with hardcoded generic icon
1048 			if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
1049 				PRINT(
1050 				("icon cache complete miss, falling back on generic "
1051 				 "icon for %s\n", model->Name()));
1052 				entry = GetFallbackIcon(sharedCacheLocker,
1053 					&resultingOpenCache, model, mode, size, &lazyBitmap,
1054 					entry);
1055 			}
1056 
1057 			// force icon pick up next time around because we probably just
1058 			// hit a node in transition
1059 			model->SetIconFrom(kUnknownSource);
1060 		}
1061 	}
1062 
1063 	ASSERT(entry != NULL && entry->HaveIconBitmap(mode, size));
1064 
1065 	if (resultingCache != NULL)
1066 		*resultingCache = resultingOpenCache;
1067 
1068 	return entry;
1069 }
1070 
1071 
1072 void
1073 IconCache::Draw(Model* model, BView* view, BPoint where, IconDrawMode mode,
1074 	icon_size size, bool async)
1075 {
1076 	// the following does not actually lock the caches, we are using the
1077 	// lockLater mode; we will decide which of the two to lock down depending
1078 	// on where we get the icon from
1079 	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1080 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1081 
1082 	AutoLock<SimpleIconCache>* resultingCacheLocker;
1083 	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1084 		&resultingCacheLocker, model, mode, size, false);
1085 		// Preload finds/creates the appropriate entry, locking down the
1086 		// cache it is in and returns the whole state back to here
1087 
1088 	if (entry == NULL)
1089 		return;
1090 
1091 	ASSERT(entry != NULL);
1092 	ASSERT(entry->HaveIconBitmap(mode, size));
1093 	// got the entry, now draw it
1094 	resultingCacheLocker->LockedItem()->Draw(entry, view, where, mode,
1095 		size, async);
1096 
1097 	// either of the two cache lockers that got locked down by this call get
1098 	// unlocked at this point
1099 }
1100 
1101 
1102 void
1103 IconCache::SyncDraw(Model* model, BView* view, BPoint where,
1104 	IconDrawMode mode, icon_size size,
1105 	void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1106 	void* passThruState)
1107 {
1108 	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1109 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1110 
1111 	AutoLock<SimpleIconCache>* resultingCacheLocker;
1112 	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1113 		&resultingCacheLocker, model, mode, size, false);
1114 
1115 	if (entry == NULL)
1116 		return;
1117 
1118 	ASSERT(entry != NULL);
1119 	ASSERT(entry->HaveIconBitmap(mode, size));
1120 	resultingCacheLocker->LockedItem()->Draw(entry, view, where,
1121 		mode, size, blitFunc, passThruState);
1122 }
1123 
1124 
1125 void
1126 IconCache::Preload(Model* model, IconDrawMode mode, icon_size size,
1127 	bool permanent)
1128 {
1129 	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1130 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1131 
1132 	Preload(&nodeCacheLocker, &sharedCacheLocker, 0, model, mode, size,
1133 		permanent);
1134 }
1135 
1136 
1137 status_t
1138 IconCache::Preload(const char* fileType, IconDrawMode mode, icon_size size)
1139 {
1140 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache);
1141 	LazyBitmapAllocator lazyBitmap(size);
1142 
1143 	BMimeType mime(fileType);
1144 	char preferredAppSig[B_MIME_TYPE_LENGTH];
1145 	status_t result = mime.GetPreferredApp(preferredAppSig);
1146 	if (result != B_OK)
1147 		return result;
1148 
1149 	// try getting the icon from the preferred app for the signature
1150 	IconCacheEntry* entry = GetIconForPreferredApp(fileType, preferredAppSig,
1151 		mode, size, &lazyBitmap, 0);
1152 	if (entry != NULL)
1153 		return B_OK;
1154 
1155 	// try getting the icon directly from the metamime
1156 	result = mime.GetIcon(lazyBitmap.Get(), size);
1157 
1158 	if (result != B_OK)
1159 		return result;
1160 
1161 	entry = fSharedCache.AddItem(fileType);
1162 	BBitmap* bitmap = lazyBitmap.Adopt();
1163 	entry->SetIcon(bitmap, kNormalIcon, size);
1164 	if (mode != kNormalIcon) {
1165 		entry->ConstructBitmap(mode, size, &lazyBitmap);
1166 		entry->SetIcon(lazyBitmap.Adopt(), mode, size);
1167 	}
1168 
1169 	return B_OK;
1170 }
1171 
1172 
1173 void
1174 IconCache::Deleting(const Model* model)
1175 {
1176 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1177 
1178 	if (model->IconFrom() == kNode)
1179 		fNodeCache.Deleting(model->NodeRef());
1180 
1181 	// don't care if the node uses the shared cache
1182 }
1183 
1184 
1185 void
1186 IconCache::Removing(const Model* model)
1187 {
1188 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1189 
1190 	if (model->IconFrom() == kNode)
1191 		fNodeCache.Removing(model->NodeRef());
1192 }
1193 
1194 
1195 void
1196 IconCache::Deleting(const BView* view)
1197 {
1198 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1199 	fNodeCache.Deleting(view);
1200 }
1201 
1202 
1203 void
1204 IconCache::IconChanged(Model* model)
1205 {
1206 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1207 
1208 	if (model->IconFrom() == kNode || model->IconFrom() == kVolume)
1209 		fNodeCache.Deleting(model->NodeRef());
1210 
1211 	model->ResetIconFrom();
1212 }
1213 
1214 
1215 void
1216 IconCache::IconChanged(const char* mimeType, const char* appSignature)
1217 {
1218 	AutoLock<SimpleIconCache> sharedLock(&fSharedCache);
1219 	SharedCacheEntry* entry = fSharedCache.FindItem(mimeType, appSignature);
1220 	if (entry == NULL)
1221 		return;
1222 
1223 	AutoLock<SimpleIconCache> nodeLock(&fNodeCache);
1224 
1225 	entry = (SharedCacheEntry*)fSharedCache.ResolveIfAlias(entry);
1226 	ASSERT(entry != NULL);
1227 	int32 index = fSharedCache.EntryIndex(entry);
1228 
1229 	fNodeCache.RemoveAliasesTo(index);
1230 	fSharedCache.RemoveAliasesTo(index);
1231 
1232 	fSharedCache.IconChanged(entry);
1233 }
1234 
1235 
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 // In debug mode keep the hash table sizes small so that they grow a lot and
1403 // execercise the resizing code a lot. In release mode allocate them large
1404 // up-front for better performance
1405 SharedIconCache::SharedIconCache()
1406 	:
1407 #if DEBUG
1408 	SimpleIconCache("Shared icon cache aka \"The Dead-Locker\""),
1409 	fElementArray(20),
1410 	fHashTable(20),
1411 	fRetiredBitmaps(20, true)
1412 #else
1413 	SimpleIconCache("Tracker shared icon cache"),
1414 	fElementArray(1024),
1415 	fHashTable(1000),
1416 	fRetiredBitmaps(256, true)
1417 #endif
1418 {
1419 	fHashTable.SetElementVector(&fElementArray);
1420 }
1421 
1422 
1423 void
1424 SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1425 	IconDrawMode mode, icon_size size, bool async)
1426 {
1427 	((SharedCacheEntry*)entry)->Draw(view, where, mode, size, async);
1428 }
1429 
1430 
1431 void
1432 SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1433 	IconDrawMode mode, icon_size size, void (*blitFunc)(BView*, BPoint,
1434 	BBitmap*, void*), void* passThruState)
1435 {
1436 	((SharedCacheEntry*)entry)->Draw(view, where, mode, size,
1437 		blitFunc, passThruState);
1438 }
1439 
1440 
1441 SharedCacheEntry*
1442 SharedIconCache::FindItem(const char* fileType,
1443 	const char* appSignature) const
1444 {
1445 	ASSERT(fileType);
1446 	if (!fileType)
1447 		fileType = B_FILE_MIMETYPE;
1448 
1449 	SharedCacheEntry* result
1450 		= fHashTable.FindFirst(SharedCacheEntry::Hash(fileType,
1451 			appSignature));
1452 
1453 	if (result == NULL)
1454 		return NULL;
1455 
1456 	for(;;) {
1457 		if (result->fFileType == fileType
1458 			&& result->fAppSignature == appSignature) {
1459 			return result;
1460 		}
1461 
1462 		if (result->fNext < 0)
1463 			break;
1464 
1465 		result = const_cast<SharedCacheEntry*>(&fElementArray.At(
1466 			result->fNext));
1467 	}
1468 
1469 	return NULL;
1470 }
1471 
1472 
1473 SharedCacheEntry*
1474 SharedIconCache::AddItem(const char* fileType, const char* appSignature)
1475 {
1476 	ASSERT(fileType != NULL);
1477 	if (fileType == NULL)
1478 		fileType = B_FILE_MIMETYPE;
1479 
1480 	SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType,
1481 		appSignature));
1482 	result->SetTo(fileType, appSignature);
1483 
1484 	return result;
1485 }
1486 
1487 
1488 SharedCacheEntry*
1489 SharedIconCache::AddItem(SharedCacheEntry** outstandingEntry,
1490 	const char* fileType, const char* appSignature)
1491 {
1492 	int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
1493 	ASSERT(entryToken >= 0);
1494 
1495 	ASSERT(fileType != NULL);
1496 	if (fileType == NULL)
1497 		fileType = B_FILE_MIMETYPE;
1498 
1499 	SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType,
1500 		appSignature));
1501 	result->SetTo(fileType, appSignature);
1502 	*outstandingEntry = fHashTable.ElementAt(entryToken);
1503 
1504 	return result;
1505 }
1506 
1507 
1508 void
1509 SharedIconCache::IconChanged(SharedCacheEntry* entry)
1510 {
1511 	// by now there should be no aliases to entry, just remove entry
1512 	// itself
1513 	ASSERT(entry->fAliasForIndex == -1);
1514 	entry->RetireIcons(&fRetiredBitmaps);
1515 	fHashTable.Remove(entry);
1516 }
1517 
1518 
1519 void
1520 SharedIconCache::RemoveAliasesTo(int32 aliasIndex)
1521 {
1522 	int32 count = fHashTable.VectorSize();
1523 	for (int32 index = 0; index < count; index++) {
1524 		SharedCacheEntry* entry = fHashTable.ElementAt(index);
1525 		if (entry->fAliasForIndex == aliasIndex)
1526 			fHashTable.Remove(entry);
1527 	}
1528 }
1529 
1530 
1531 void
1532 SharedIconCache::SetAliasFor(IconCacheEntry* entry,
1533 	const SharedCacheEntry* original) const
1534 {
1535 	entry->fAliasForIndex = fHashTable.ElementIndex(original);
1536 }
1537 
1538 
1539 SharedCacheEntry::SharedCacheEntry()
1540 	:
1541 	fNext(-1)
1542 {
1543 }
1544 
1545 
1546 SharedCacheEntry::SharedCacheEntry(const char* fileType,
1547 	const char* appSignature)
1548 	:
1549 	fNext(-1),
1550 	fFileType(fileType),
1551 	fAppSignature(appSignature)
1552 {
1553 }
1554 
1555 
1556 void
1557 SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1558 	icon_size size, bool async)
1559 {
1560 	BBitmap* bitmap = IconForMode(mode, size);
1561 	ASSERT(bitmap != NULL);
1562 
1563 	drawing_mode oldMode = view->DrawingMode();
1564 
1565 	if (bitmap->ColorSpace() == B_RGBA32) {
1566 		if (oldMode != B_OP_ALPHA) {
1567 			view->SetDrawingMode(B_OP_ALPHA);
1568 			view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1569 		}
1570 	} else
1571 		view->SetDrawingMode(B_OP_OVER);
1572 
1573 	if (async)
1574 		view->DrawBitmapAsync(bitmap, where);
1575 	else
1576 		view->DrawBitmap(bitmap, where);
1577 
1578 	view->SetDrawingMode(oldMode);
1579 }
1580 
1581 
1582 void
1583 SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1584 	icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1585 	void* passThruState)
1586 {
1587 	BBitmap* bitmap = IconForMode(mode, size);
1588 	if (bitmap == NULL)
1589 		return;
1590 
1591 	(blitFunc)(view, where, bitmap, passThruState);
1592 }
1593 
1594 
1595 uint32
1596 SharedCacheEntry::Hash(const char* fileType, const char* appSignature)
1597 {
1598 	uint32 hash = HashString(fileType, 0);
1599 	if (appSignature != NULL && *appSignature != '\0')
1600 		hash = HashString(appSignature, hash);
1601 
1602 	return hash;
1603 }
1604 
1605 
1606 uint32
1607 SharedCacheEntry::Hash() const
1608 {
1609 	uint32 hash = HashString(fFileType.String(), 0);
1610 	if (fAppSignature.Length() > 0)
1611 		hash = HashString(fAppSignature.String(), hash);
1612 
1613 	return hash;
1614 }
1615 
1616 
1617 bool
1618 SharedCacheEntry::operator==(const SharedCacheEntry &entry) const
1619 {
1620 	return fFileType == entry.FileType()
1621 		&& fAppSignature == entry.AppSignature();
1622 }
1623 
1624 
1625 void
1626 SharedCacheEntry::SetTo(const char* fileType, const char* appSignature)
1627 {
1628 	fFileType = fileType;
1629 	fAppSignature = appSignature;
1630 }
1631 
1632 
1633 SharedCacheEntryArray::SharedCacheEntryArray(int32 initialSize)
1634 	:
1635 	OpenHashElementArray<SharedCacheEntry>(initialSize)
1636 {
1637 }
1638 
1639 
1640 SharedCacheEntry*
1641 SharedCacheEntryArray::Add()
1642 {
1643 	return OpenHashElementArray<SharedCacheEntry>::Add();
1644 }
1645 
1646 
1647 //	#pragma mark - NodeCacheEntry
1648 
1649 
1650 NodeCacheEntry::NodeCacheEntry(bool permanent)
1651 	:
1652 	fNext(-1),
1653 	fPermanent(permanent)
1654 {
1655 }
1656 
1657 
1658 NodeCacheEntry::NodeCacheEntry(const node_ref* node, bool permanent)
1659 	:
1660 	fNext(-1),
1661 	fRef(*node),
1662 	fPermanent(permanent)
1663 {
1664 }
1665 
1666 
1667 void
1668 NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1669 	icon_size size, bool async)
1670 {
1671 	BBitmap* bitmap = IconForMode(mode, size);
1672 	if (bitmap == NULL)
1673 		return;
1674 
1675 	drawing_mode oldMode = view->DrawingMode();
1676 
1677 	if (bitmap->ColorSpace() == B_RGBA32) {
1678 		if (oldMode != B_OP_ALPHA) {
1679 			view->SetDrawingMode(B_OP_ALPHA);
1680 			view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1681 		}
1682 	} else
1683 		view->SetDrawingMode(B_OP_OVER);
1684 
1685 	if (false && async) {
1686 		TRESPASS();
1687 		// need to copy the bits first in here
1688 		view->DrawBitmapAsync(bitmap, where);
1689 	} else
1690 		view->DrawBitmap(bitmap, where);
1691 
1692 	view->SetDrawingMode(oldMode);
1693 }
1694 
1695 
1696 void
1697 NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1698 	icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1699 	void* passThruState)
1700 {
1701 	BBitmap* bitmap = IconForMode(mode, size);
1702 	if (bitmap == NULL)
1703 		return;
1704 
1705 	(blitFunc)(view, where, bitmap, passThruState);
1706 }
1707 
1708 
1709 const node_ref*
1710 NodeCacheEntry::Node() const
1711 {
1712 	return &fRef;
1713 }
1714 
1715 
1716 uint32
1717 NodeCacheEntry::Hash() const
1718 {
1719 	return Hash(&fRef);
1720 }
1721 
1722 
1723 uint32
1724 NodeCacheEntry::Hash(const node_ref* node)
1725 {
1726 	return node->device ^ ((uint32*)&node->node)[0]
1727 		^ ((uint32*)&node->node)[1];
1728 }
1729 
1730 
1731 bool
1732 NodeCacheEntry::operator==(const NodeCacheEntry &entry) const
1733 {
1734 	return fRef == entry.fRef;
1735 }
1736 
1737 
1738 void
1739 NodeCacheEntry::SetTo(const node_ref* node)
1740 {
1741 	fRef = *node;
1742 }
1743 
1744 
1745 bool
1746 NodeCacheEntry::Permanent() const
1747 {
1748 	return fPermanent;
1749 }
1750 
1751 
1752 void
1753 NodeCacheEntry::MakePermanent()
1754 {
1755 	fPermanent = true;
1756 }
1757 
1758 
1759 //	#pragma mark - NodeIconCache
1760 
1761 
1762 NodeIconCache::NodeIconCache()
1763 	:
1764 #if DEBUG
1765 	SimpleIconCache("Node icon cache aka \"The Dead-Locker\""),
1766 	fElementArray(20),
1767 	fHashTable(20)
1768 #else
1769 	SimpleIconCache("Tracker node icon cache"),
1770 	fElementArray(100),
1771 	fHashTable(100)
1772 #endif
1773 {
1774 	fHashTable.SetElementVector(&fElementArray);
1775 }
1776 
1777 
1778 void
1779 NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1780 	IconDrawMode mode, icon_size size, bool async)
1781 {
1782 	((NodeCacheEntry*)entry)->Draw(view, where, mode, size, async);
1783 }
1784 
1785 
1786 void
1787 NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1788 	IconDrawMode mode, icon_size size,
1789 	void (*blitFunc)(BView*, BPoint, BBitmap*, void*), void* passThruState)
1790 {
1791 	((NodeCacheEntry*)entry)->Draw(view, where, mode, size,
1792 		blitFunc, passThruState);
1793 }
1794 
1795 
1796 NodeCacheEntry*
1797 NodeIconCache::FindItem(const node_ref* node) const
1798 {
1799 	NodeCacheEntry* entry = fHashTable.FindFirst(NodeCacheEntry::Hash(node));
1800 	if (entry == NULL)
1801 		return NULL;
1802 
1803 	for(;;) {
1804 		if (*entry->Node() == *node)
1805 			return entry;
1806 
1807 		if (entry->fNext < 0)
1808 			break;
1809 
1810 		entry = const_cast<NodeCacheEntry*>(&fElementArray.At(entry->fNext));
1811 	}
1812 
1813 	return NULL;
1814 }
1815 
1816 
1817 NodeCacheEntry*
1818 NodeIconCache::AddItem(const node_ref* node, bool permanent)
1819 {
1820 	NodeCacheEntry* entry = fHashTable.Add(NodeCacheEntry::Hash(node));
1821 	entry->SetTo(node);
1822 	if (permanent)
1823 		entry->MakePermanent();
1824 
1825 	return entry;
1826 }
1827 
1828 
1829 NodeCacheEntry*
1830 NodeIconCache::AddItem(NodeCacheEntry** outstandingEntry,
1831 	const node_ref* node)
1832 {
1833 	int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
1834 
1835 	NodeCacheEntry* entry = fHashTable.Add(NodeCacheEntry::Hash(node));
1836 	entry->SetTo(node);
1837 	*outstandingEntry = fHashTable.ElementAt(entryToken);
1838 
1839 	return entry;
1840 }
1841 
1842 
1843 void
1844 NodeIconCache::Deleting(const node_ref* node)
1845 {
1846 	NodeCacheEntry* entry = FindItem(node);
1847 	ASSERT(entry != NULL);
1848 	if (entry == NULL || entry->Permanent())
1849 		return;
1850 
1851 	fHashTable.Remove(entry);
1852 }
1853 
1854 
1855 void
1856 NodeIconCache::Removing(const node_ref* node)
1857 {
1858 	NodeCacheEntry* entry = FindItem(node);
1859 	ASSERT(entry != NULL);
1860 	if (entry == NULL)
1861 		return;
1862 
1863 	fHashTable.Remove(entry);
1864 }
1865 
1866 
1867 void
1868 NodeIconCache::Deleting(const BView*)
1869 {
1870 #ifdef NODE_CACHE_ASYNC_DRAWS
1871 	TRESPASS();
1872 #endif
1873 }
1874 
1875 
1876 void
1877 NodeIconCache::IconChanged(const Model* model)
1878 {
1879 	Deleting(model->NodeRef());
1880 }
1881 
1882 
1883 void
1884 NodeIconCache::RemoveAliasesTo(int32 aliasIndex)
1885 {
1886 	int32 count = fHashTable.VectorSize();
1887 	for (int32 index = 0; index < count; index++) {
1888 		NodeCacheEntry* entry = fHashTable.ElementAt(index);
1889 		if (entry->fAliasForIndex == aliasIndex)
1890 			fHashTable.Remove(entry);
1891 	}
1892 }
1893 
1894 
1895 //	#pragma mark - NodeCacheEntryArray
1896 
1897 
1898 NodeCacheEntryArray::NodeCacheEntryArray(int32 initialSize)
1899 	:
1900 	OpenHashElementArray<NodeCacheEntry>(initialSize)
1901 {
1902 }
1903 
1904 
1905 NodeCacheEntry*
1906 NodeCacheEntryArray::Add()
1907 {
1908 	return OpenHashElementArray<NodeCacheEntry>::Add();
1909 }
1910 
1911 
1912 //	#pragma mark - SimpleIconCache
1913 
1914 
1915 SimpleIconCache::SimpleIconCache(const char* name)
1916 	:
1917 	fLock(name)
1918 {
1919 }
1920 
1921 
1922 void
1923 SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1924 	icon_size, bool)
1925 {
1926 	TRESPASS();
1927 	// pure virtual, do nothing
1928 }
1929 
1930 
1931 void
1932 SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1933 	icon_size, void(*)(BView*, BPoint, BBitmap*, void*), void*)
1934 {
1935 	TRESPASS();
1936 	// pure virtual, do nothing
1937 }
1938 
1939 
1940 bool
1941 SimpleIconCache::Lock()
1942 {
1943 	return fLock.Lock();
1944 }
1945 
1946 
1947 void
1948 SimpleIconCache::Unlock()
1949 {
1950 	fLock.Unlock();
1951 }
1952 
1953 
1954 bool
1955 SimpleIconCache::IsLocked() const
1956 {
1957 	return fLock.IsLocked();
1958 }
1959 
1960 
1961 //	#pragma mark - LazyBitmapAllocator
1962 
1963 
1964 LazyBitmapAllocator::LazyBitmapAllocator(icon_size size,
1965 	color_space colorSpace, bool preallocate)
1966 	:
1967 	fBitmap(NULL),
1968 	fSize(size),
1969 	fColorSpace(colorSpace)
1970 {
1971 	if (preallocate)
1972 		Get();
1973 }
1974 
1975 
1976 LazyBitmapAllocator::~LazyBitmapAllocator()
1977 {
1978 	delete fBitmap;
1979 }
1980 
1981 
1982 BBitmap*
1983 LazyBitmapAllocator::Get()
1984 {
1985 	if (fBitmap == NULL)
1986 		fBitmap = new BBitmap(BRect(0, 0, fSize - 1, fSize - 1), fColorSpace);
1987 
1988 	return fBitmap;
1989 }
1990 
1991 
1992 BBitmap*
1993 LazyBitmapAllocator::Adopt()
1994 {
1995 	if (fBitmap == NULL)
1996 		Get();
1997 
1998 	BBitmap* bitmap = fBitmap;
1999 	fBitmap = NULL;
2000 
2001 	return bitmap;
2002 }
2003 
2004 
2005 IconCache* IconCache::sIconCache;
2006