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