xref: /haiku/src/kits/tracker/IconCache.cpp (revision 991dadd6324f7b7a68e94743a39ebae789823228)
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 	{	// scope for modelOpener
889 
890 		ModelNodeLazyOpener modelOpener(model);
891 			// this opener takes care of opening the model and possibly
892 			// closing it when we are done
893 		LazyBitmapAllocator lazyBitmap(size);
894 			// lazyBitmap manages bitmap allocation and freeing if needed
895 
896 		IconSource source = model->IconFrom();
897 		if (source == kUnknownSource || source == kUnknownNotFromNode) {
898 
899 			// fish for special first models and handle them appropriately
900 			if (model->IsVolume()) {
901 				// volume may use specialized icon in the volume node
902 				entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
903 					&resultingOpenCache, model, source, mode, size,
904 					&lazyBitmap, entry, permanent);
905 
906 				if (!entry || !entry->HaveIconBitmap(mode, size))
907 					// look for volume defined icon
908 					entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker,
909 						&resultingOpenCache, model, source, mode,
910 						size, &lazyBitmap);
911 
912 			} else if (model->IsRoot()) {
913 
914 				entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker,
915 					&resultingOpenCache, model, source, mode, size, &lazyBitmap);
916 				ASSERT(entry);
917 
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) {
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, size,
934 						&lazyBitmap, 0);
935 
936 					if (!entry) // we don't have an icon, go with the generic
937 						entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
938 							model, source, mode, size, &lazyBitmap, entry);
939 				}
940 			}
941 
942 			// update the icon source
943 			model->SetIconFrom(source);
944 		} else {
945 			// we already know where the icon should come from,
946 			// use shortcuts to get it
947 			switch (source) {
948 				case kNode:
949 					resultingOpenCache = nodeCacheLocker;
950 					resultingOpenCache->Lock();
951 
952 					entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
953 						&resultingOpenCache, model, source, mode,
954 						size, &lazyBitmap, entry, permanent);
955 
956 					if (entry) {
957 						entry = IconCacheEntry::ResolveIfAlias(&fSharedCache, entry);
958 						if (!entry->HaveIconBitmap(mode, size)
959 							&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
960 							entry->ConstructBitmap(mode, size, &lazyBitmap);
961 							entry->SetIcon(lazyBitmap.Adopt(), mode, size);
962 						}
963 						ASSERT(entry->HaveIconBitmap(mode, size));
964 					}
965 					break;
966 				case kTrackerSupplied:
967 					if (model->IsRoot()) {
968 						entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker,
969 							&resultingOpenCache, model, source, mode, size,
970 							&lazyBitmap);
971 						break;
972 					} else {
973 						entry = GetWellKnownIcon(nodeCacheLocker, sharedCacheLocker,
974 							&resultingOpenCache, model, source, mode, size,
975 							&lazyBitmap);
976 
977 						if (entry)
978 							break;
979 					}
980 					// fall through
981 				case kTrackerDefault:
982 				case kVolume:
983 					if (model->IsVolume()) {
984 						entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
985 							&resultingOpenCache, model, source,
986 							mode, size, &lazyBitmap, entry, permanent);
987 						if (!entry || !entry->HaveIconBitmap(mode, size))
988 							entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker,
989 								&resultingOpenCache, model, source, mode, size,
990 								&lazyBitmap);
991 						break;
992 					}
993 					// fall through
994 				case kMetaMime:
995 				case kPreferredAppForType:
996 				case kPreferredAppForNode:
997 					resultingOpenCache = sharedCacheLocker;
998 					resultingOpenCache->Lock();
999 
1000 					entry = GetIconFromFileTypes(&modelOpener, source, mode, size,
1001 						&lazyBitmap, 0);
1002 					ASSERT(!entry || entry->HaveIconBitmap(mode, size));
1003 
1004 					if (!entry || !entry->HaveIconBitmap(mode, size))
1005 						// we don't have an icon, go with the generic
1006 						entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
1007 							model, source, mode, size, &lazyBitmap, entry);
1008 
1009 					model->SetIconFrom(source);
1010 						// the source shouldn't change in this case; if it does though we
1011 						// might never be hitting the correct icon and instead keep leaking
1012 						// entries after each miss
1013 						// this now happens if an app defines an icon but a GetIconForType
1014 						// fails and we fall back to generic icon
1015 						// ToDo:
1016 						// fix this and add an assert to the effect
1017 
1018 					ASSERT(entry);
1019 					ASSERT(entry->HaveIconBitmap(mode, size));
1020 					break;
1021 
1022 				default:
1023 					TRESPASS();
1024 			}
1025 		}
1026 
1027 		if (!entry || !entry->HaveIconBitmap(mode, size)) {
1028 			// we don't have an icon, go with the generic
1029 			PRINT(
1030 				("icon cache complete miss, falling back on generic icon "
1031 				 "for %s\n", model->Name()));
1032 			entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
1033 				model, source, mode, size, &lazyBitmap, entry);
1034 
1035 			// we don't even have generic, something is really broken,
1036 			// go with hardcoded generic icon
1037 			if (!entry || !entry->HaveIconBitmap(mode, size)) {
1038 				PRINT(
1039 				("icon cache complete miss, falling back on generic "
1040 				 "icon for %s\n", model->Name()));
1041 				entry = GetFallbackIcon(sharedCacheLocker,
1042 					&resultingOpenCache, model, mode, size, &lazyBitmap,
1043 					entry);
1044 			}
1045 
1046 			// force icon pick up next time around because we probably just
1047 			// hit a node in transition
1048 			model->SetIconFrom(kUnknownSource);
1049 		}
1050 	}
1051 
1052 	ASSERT(entry && entry->HaveIconBitmap(mode, size));
1053 
1054 	if (resultingCache)
1055 		*resultingCache = resultingOpenCache;
1056 
1057 	return entry;
1058 }
1059 
1060 
1061 void
1062 IconCache::Draw(Model* model, BView* view, BPoint where, IconDrawMode mode,
1063 	icon_size size, bool async)
1064 {
1065 	// the following does not actually lock the caches, we are using the
1066 	// lockLater mode; we will decide which of the two to lock down depending
1067 	// on where we get the icon from
1068 	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1069 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1070 
1071 	AutoLock<SimpleIconCache>* resultingCacheLocker;
1072 	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1073 		&resultingCacheLocker, model, mode, size, false);
1074 		// Preload finds/creates the appropriate entry, locking down the
1075 		// cache it is in and returns the whole state back to here
1076 
1077 	if (!entry)
1078 		return;
1079 
1080 	ASSERT(entry);
1081 	ASSERT(entry->HaveIconBitmap(mode, size));
1082 	// got the entry, now draw it
1083 	resultingCacheLocker->LockedItem()->Draw(entry, view, where, mode,
1084 		size, async);
1085 
1086 	// either of the two cache lockers that got locked down by this call get
1087 	// unlocked at this point
1088 }
1089 
1090 
1091 void
1092 IconCache::SyncDraw(Model* model, BView* view, BPoint where,
1093 	IconDrawMode mode, icon_size size,
1094 	void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1095 	void* passThruState)
1096 {
1097 	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1098 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1099 
1100 	AutoLock<SimpleIconCache>* resultingCacheLocker;
1101 	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1102 		&resultingCacheLocker, model, mode, size, false);
1103 
1104 	if (!entry)
1105 		return;
1106 
1107 	ASSERT(entry);
1108 	ASSERT(entry->HaveIconBitmap(mode, size));
1109 	resultingCacheLocker->LockedItem()->Draw(entry, view, where,
1110 		mode, size, blitFunc, passThruState);
1111 }
1112 
1113 
1114 void
1115 IconCache::Preload(Model* model, IconDrawMode mode, icon_size size,
1116 	bool permanent)
1117 {
1118 	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1119 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1120 
1121 	Preload(&nodeCacheLocker, &sharedCacheLocker, 0, model, mode, size,
1122 		permanent);
1123 }
1124 
1125 
1126 status_t
1127 IconCache::Preload(const char* fileType, IconDrawMode mode, icon_size size)
1128 {
1129 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache);
1130 	LazyBitmapAllocator lazyBitmap(size);
1131 
1132 	BMimeType mime(fileType);
1133 	char preferredAppSig[B_MIME_TYPE_LENGTH];
1134 	status_t result = mime.GetPreferredApp(preferredAppSig);
1135 	if (result != B_OK)
1136 		return result;
1137 
1138 	// try getting the icon from the preferred app for the signature
1139 	IconCacheEntry* entry = GetIconForPreferredApp(fileType, preferredAppSig,
1140 		mode, size, &lazyBitmap, 0);
1141 	if (entry)
1142 		return B_OK;
1143 
1144 	// try getting the icon directly from the metamime
1145 	result = mime.GetIcon(lazyBitmap.Get(), size);
1146 
1147 	if (result != B_OK)
1148 		return result;
1149 
1150 	entry = fSharedCache.AddItem(fileType);
1151 	BBitmap* bitmap = lazyBitmap.Adopt();
1152 	entry->SetIcon(bitmap, kNormalIcon, size);
1153 	if (mode != kNormalIcon) {
1154 		entry->ConstructBitmap(mode, size, &lazyBitmap);
1155 		entry->SetIcon(lazyBitmap.Adopt(), mode, size);
1156 	}
1157 
1158 	return B_OK;
1159 }
1160 
1161 
1162 void
1163 IconCache::Deleting(const Model* model)
1164 {
1165 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1166 
1167 	if (model->IconFrom() == kNode)
1168 		fNodeCache.Deleting(model->NodeRef());
1169 
1170 	// don't care if the node uses the shared cache
1171 }
1172 
1173 
1174 void
1175 IconCache::Removing(const Model* model)
1176 {
1177 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1178 
1179 	if (model->IconFrom() == kNode)
1180 		fNodeCache.Removing(model->NodeRef());
1181 }
1182 
1183 
1184 void
1185 IconCache::Deleting(const BView* view)
1186 {
1187 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1188 	fNodeCache.Deleting(view);
1189 }
1190 
1191 
1192 void
1193 IconCache::IconChanged(Model* model)
1194 {
1195 	AutoLock<SimpleIconCache> lock(&fNodeCache);
1196 
1197 	if (model->IconFrom() == kNode || model->IconFrom() == kVolume)
1198 		fNodeCache.Deleting(model->NodeRef());
1199 
1200 	model->ResetIconFrom();
1201 }
1202 
1203 
1204 void
1205 IconCache::IconChanged(const char* mimeType, const char* appSignature)
1206 {
1207 	AutoLock<SimpleIconCache> sharedLock(&fSharedCache);
1208 	SharedCacheEntry* entry = fSharedCache.FindItem(mimeType, appSignature);
1209 	if (!entry)
1210 		return;
1211 
1212 	AutoLock<SimpleIconCache> nodeLock(&fNodeCache);
1213 
1214 	entry = (SharedCacheEntry*)fSharedCache.ResolveIfAlias(entry);
1215 	ASSERT(entry);
1216 	int32 index = fSharedCache.EntryIndex(entry);
1217 
1218 	fNodeCache.RemoveAliasesTo(index);
1219 	fSharedCache.RemoveAliasesTo(index);
1220 
1221 	fSharedCache.IconChanged(entry);
1222 }
1223 
1224 
1225 BBitmap*
1226 IconCache::MakeSelectedIcon(const BBitmap* normal, icon_size size,
1227 	LazyBitmapAllocator* lazyBitmap)
1228 {
1229 	return MakeTransformedIcon(normal, size, fHiliteTable, lazyBitmap);
1230 }
1231 
1232 #if xDEBUG
1233 
1234 static void
1235 DumpBitmap(const BBitmap* bitmap)
1236 {
1237 	if (!bitmap){
1238 		printf("NULL bitmap passed to DumpBitmap\n");
1239 		return;
1240 	}
1241 	int32 length = bitmap->BitsLength();
1242 
1243 	printf("data length %ld \n", length);
1244 
1245 	int32 columns = (int32)bitmap->Bounds().Width() + 1;
1246 	const unsigned char* bitPtr = (const unsigned char*)bitmap->Bits();
1247 	for (; length >= 0; length--) {
1248 		for (int32 columnIndex = 0; columnIndex < columns;
1249 			columnIndex++, length--)
1250 			printf("%c%c", "0123456789ABCDEF"[(*bitPtr)/0x10],
1251 				"0123456789ABCDEF"[(*bitPtr++)%0x10]);
1252 
1253 		printf("\n");
1254 	}
1255 	printf("\n");
1256 }
1257 
1258 #endif
1259 
1260 void
1261 IconCache::InitHiliteTable()
1262 {
1263 	// build the color transform tables for different icon modes
1264 	BScreen screen(B_MAIN_SCREEN_ID);
1265 	rgb_color color;
1266 	for (int32 index = 0; index < kColorTransformTableSize; index++) {
1267 		color = screen.ColorForIndex((uchar)index);
1268 		fHiliteTable[index] = screen.IndexForColor(tint_color(color, 1.3f));
1269 	}
1270 
1271 	fHiliteTable[B_TRANSPARENT_8_BIT] = B_TRANSPARENT_8_BIT;
1272 	fInitHiliteTable = false;
1273 }
1274 
1275 
1276 BBitmap*
1277 IconCache::MakeTransformedIcon(const BBitmap* source, icon_size /*size*/,
1278 	int32 colorTransformTable[], LazyBitmapAllocator* lazyBitmap)
1279 {
1280 	if (fInitHiliteTable)
1281 		InitHiliteTable();
1282 
1283 	BBitmap* result = lazyBitmap->Get();
1284 	uint8* src = (uint8*)source->Bits();
1285 	uint8* dst = (uint8*)result->Bits();
1286 
1287 //	ASSERT(result->ColorSpace() == source->ColorSpace()
1288 //		&& result->Bounds() == source->Bounds());
1289 	if (result->ColorSpace() != source->ColorSpace()
1290 		|| result->Bounds() != source->Bounds()) {
1291 		printf("IconCache::MakeTransformedIcon() - "
1292 					"bitmap format mismatch!\n");
1293 		return NULL;
1294 	}
1295 
1296 	switch (result->ColorSpace()) {
1297 		case B_RGB32:
1298 		case B_RGBA32: {
1299 			uint32 width = source->Bounds().IntegerWidth() + 1;
1300 			uint32 height = source->Bounds().IntegerHeight() + 1;
1301 			uint32 srcBPR = source->BytesPerRow();
1302 			uint32 dstBPR = result->BytesPerRow();
1303 			for (uint32 y = 0; y < height; y++) {
1304 				uint8* d = dst;
1305 				uint8* s = src;
1306 				for (uint32 x = 0; x < width; x++) {
1307 					// 66% brightness
1308 					d[0] = (int)s[0] * 168 >> 8;
1309 					d[1] = (int)s[1] * 168 >> 8;
1310 					d[2] = (int)s[2] * 168 >> 8;
1311 					d[3] = s[3];
1312 					d += 4;
1313 					s += 4;
1314 				}
1315 				dst += dstBPR;
1316 				src += srcBPR;
1317 			}
1318 			break;
1319 		}
1320 
1321 		case B_CMAP8: {
1322 			int32 bitsLength = result->BitsLength();
1323 			for (int32 i = 0; i < bitsLength; i++)
1324 				*dst++ = (uint8)colorTransformTable[*src++];
1325 			break;
1326 		}
1327 
1328 		default:
1329 			memset(dst, 0, result->BitsLength());
1330 			// unkown colorspace, no tinting for you
1331 			// "black" should make the problem stand out
1332 			break;
1333 	}
1334 
1335 	return result;
1336 }
1337 
1338 
1339 bool
1340 IconCache::IconHitTest(BPoint where, const Model* model, IconDrawMode mode,
1341 	icon_size size)
1342 {
1343 	AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
1344 	AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
1345 
1346 	AutoLock<SimpleIconCache>* resultingCacheLocker;
1347 	IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
1348 		&resultingCacheLocker, const_cast<Model*>(model), mode, size, false);
1349 		// Preload finds/creates the appropriate entry, locking down the
1350 		// cache it is in and returns the whole state back to here
1351 
1352 	if (entry)
1353 		return entry->IconHitTest(where, mode, size);
1354 
1355 	return false;
1356 }
1357 
1358 
1359 void
1360 IconCacheEntry::RetireIcons(BObjectList<BBitmap>* retiredBitmapList)
1361 {
1362 	if (fLargeIcon) {
1363 		retiredBitmapList->AddItem(fLargeIcon);
1364 		fLargeIcon = NULL;
1365 	}
1366 	if (fMiniIcon) {
1367 		retiredBitmapList->AddItem(fMiniIcon);
1368 		fMiniIcon = NULL;
1369 	}
1370 	if (fHilitedLargeIcon) {
1371 		retiredBitmapList->AddItem(fHilitedLargeIcon);
1372 		fHilitedLargeIcon = NULL;
1373 	}
1374 	if (fHilitedMiniIcon) {
1375 		retiredBitmapList->AddItem(fHilitedMiniIcon);
1376 		fHilitedMiniIcon = NULL;
1377 	}
1378 
1379 	int32 count = retiredBitmapList->CountItems();
1380 	if (count > 10 * 1024) {
1381 		PRINT(("nuking old icons from the retired bitmap list\n"));
1382 		for (count = 512; count > 0; count--)
1383 			delete retiredBitmapList->RemoveItemAt(0);
1384 	}
1385 }
1386 
1387 
1388 //	#pragma mark -
1389 
1390 
1391 // In debug mode keep the hash table sizes small so that they grow a lot and
1392 // execercise the resizing code a lot. In release mode allocate them large
1393 // up-front for better performance
1394 SharedIconCache::SharedIconCache()
1395 #if DEBUG
1396 	:	SimpleIconCache("Shared Icon cache aka \"The Dead-Locker\""),
1397 		fHashTable(20),
1398 		fElementArray(20),
1399 		fRetiredBitmaps(20, true)
1400 #else
1401 	:	SimpleIconCache("Tracker shared icon cache"),
1402 		fHashTable(1000),
1403 		fElementArray(1024),
1404 		fRetiredBitmaps(256, true)
1405 #endif
1406 {
1407 	fHashTable.SetElementVector(&fElementArray);
1408 }
1409 
1410 
1411 void
1412 SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1413 	IconDrawMode mode, icon_size size, bool async)
1414 {
1415 	((SharedCacheEntry*)entry)->Draw(view, where, mode, size, async);
1416 }
1417 
1418 
1419 void
1420 SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1421 	IconDrawMode mode, icon_size size, void (*blitFunc)(BView*, BPoint,
1422 	BBitmap*, void*), void* passThruState)
1423 {
1424 	((SharedCacheEntry*)entry)->Draw(view, where, mode, size,
1425 		blitFunc, passThruState);
1426 }
1427 
1428 
1429 SharedCacheEntry*
1430 SharedIconCache::FindItem(const char* fileType,
1431 	const char* appSignature) const
1432 {
1433 	ASSERT(fileType);
1434 	if (!fileType)
1435 		fileType = B_FILE_MIMETYPE;
1436 
1437 	SharedCacheEntry* result
1438 		= fHashTable.FindFirst(SharedCacheEntry::Hash(fileType,
1439 			appSignature));
1440 
1441 	if (!result)
1442 		return NULL;
1443 
1444 	for(;;) {
1445 		if (result->fFileType == fileType
1446 			&& result->fAppSignature == appSignature) {
1447 			return result;
1448 		}
1449 
1450 		if (result->fNext < 0)
1451 			break;
1452 
1453 		result
1454 			= const_cast<SharedCacheEntry*>(&fElementArray.At(result->fNext));
1455 	}
1456 
1457 	return NULL;
1458 }
1459 
1460 
1461 SharedCacheEntry*
1462 SharedIconCache::AddItem(const char* fileType, const char* appSignature)
1463 {
1464 	ASSERT(fileType);
1465 	if (!fileType)
1466 		fileType = B_FILE_MIMETYPE;
1467 
1468 	SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType,
1469 		appSignature));
1470 	result->SetTo(fileType, appSignature);
1471 	return result;
1472 }
1473 
1474 
1475 SharedCacheEntry*
1476 SharedIconCache::AddItem(SharedCacheEntry** outstandingEntry,
1477 	const char* fileType, const char* appSignature)
1478 {
1479 	int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
1480 	ASSERT(entryToken >= 0);
1481 
1482 	ASSERT(fileType);
1483 	if (!fileType)
1484 		fileType = B_FILE_MIMETYPE;
1485 
1486 	SharedCacheEntry* result = fHashTable.Add(SharedCacheEntry::Hash(fileType,
1487 		appSignature));
1488 	result->SetTo(fileType, appSignature);
1489 	*outstandingEntry = fHashTable.ElementAt(entryToken);
1490 
1491 	return result;
1492 }
1493 
1494 
1495 void
1496 SharedIconCache::IconChanged(SharedCacheEntry* entry)
1497 {
1498 	// by now there should be no aliases to entry, just remove entry
1499 	// itself
1500 	ASSERT(entry->fAliasForIndex == -1);
1501 	entry->RetireIcons(&fRetiredBitmaps);
1502 	fHashTable.Remove(entry);
1503 }
1504 
1505 
1506 void
1507 SharedIconCache::RemoveAliasesTo(int32 aliasIndex)
1508 {
1509 	int32 count = fHashTable.VectorSize();
1510 	for (int32 index = 0; index < count; index++) {
1511 		SharedCacheEntry* entry = fHashTable.ElementAt(index);
1512 		if (entry->fAliasForIndex == aliasIndex)
1513 			fHashTable.Remove(entry);
1514 	}
1515 }
1516 
1517 
1518 void
1519 SharedIconCache::SetAliasFor(IconCacheEntry* alias,
1520 	const SharedCacheEntry* original) const
1521 {
1522 	alias->fAliasForIndex = fHashTable.ElementIndex(original);
1523 }
1524 
1525 
1526 SharedCacheEntry::SharedCacheEntry()
1527 	:	fNext(-1)
1528 {
1529 }
1530 
1531 
1532 SharedCacheEntry::SharedCacheEntry(const char* fileType,
1533 	const char* appSignature)
1534 	:	fNext(-1),
1535 		fFileType(fileType),
1536 		fAppSignature(appSignature)
1537 {
1538 }
1539 
1540 
1541 void
1542 SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1543 	icon_size size, bool async)
1544 {
1545 	BBitmap* bitmap = IconForMode(mode, size);
1546 	ASSERT(bitmap);
1547 
1548 	drawing_mode oldMode = view->DrawingMode();
1549 
1550 	if (bitmap->ColorSpace() == B_RGBA32) {
1551 		if (oldMode != B_OP_ALPHA) {
1552 			view->SetDrawingMode(B_OP_ALPHA);
1553 			view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1554 		}
1555 	} else {
1556 		view->SetDrawingMode(B_OP_OVER);
1557 	}
1558 
1559 	if (async)
1560 		view->DrawBitmapAsync(bitmap, where);
1561 	else
1562 		view->DrawBitmap(bitmap, where);
1563 
1564 	view->SetDrawingMode(oldMode);
1565 }
1566 
1567 
1568 void
1569 SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1570 	icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1571 	void* passThruState)
1572 {
1573 	BBitmap* bitmap = IconForMode(mode, size);
1574 	if (!bitmap)
1575 		return;
1576 
1577 //	if (bitmap->ColorSpace() == B_RGBA32) {
1578 //		view->SetDrawingMode(B_OP_ALPHA);
1579 //		view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1580 //	} else {
1581 //		view->SetDrawingMode(B_OP_OVER);
1582 //	}
1583 
1584 	(blitFunc)(view, where, bitmap, passThruState);
1585 }
1586 
1587 
1588 uint32
1589 SharedCacheEntry::Hash(const char* fileType, const char* appSignature)
1590 {
1591 	uint32 hash = HashString(fileType, 0);
1592 	if (appSignature && appSignature[0])
1593 		hash = HashString(appSignature, hash);
1594 
1595 	return hash;
1596 }
1597 
1598 
1599 uint32
1600 SharedCacheEntry::Hash() const
1601 {
1602 	uint32 hash = HashString(fFileType.String(), 0);
1603 	if (fAppSignature.Length())
1604 		hash = HashString(fAppSignature.String(), hash);
1605 
1606 	return hash;
1607 }
1608 
1609 
1610 bool
1611 SharedCacheEntry::operator==(const SharedCacheEntry &entry) const
1612 {
1613 	return fFileType == entry.FileType()
1614 		&& fAppSignature == entry.AppSignature();
1615 }
1616 
1617 
1618 void
1619 SharedCacheEntry::SetTo(const char* fileType, const char* appSignature)
1620 {
1621 	fFileType = fileType;
1622 	fAppSignature = appSignature;
1623 }
1624 
1625 
1626 SharedCacheEntryArray::SharedCacheEntryArray(int32 initialSize)
1627 	:	OpenHashElementArray<SharedCacheEntry>(initialSize)
1628 {
1629 }
1630 
1631 
1632 SharedCacheEntry*
1633 SharedCacheEntryArray::Add()
1634 {
1635 	return OpenHashElementArray<SharedCacheEntry>::Add();
1636 }
1637 
1638 
1639 //	#pragma mark -
1640 
1641 
1642 NodeCacheEntry::NodeCacheEntry(bool permanent)
1643 	:	fNext(-1),
1644 		fPermanent(permanent)
1645 {
1646 }
1647 
1648 
1649 NodeCacheEntry::NodeCacheEntry(const node_ref* node, bool permanent)
1650 	:	fNext(-1),
1651 		fRef(*node),
1652 		fPermanent(permanent)
1653 {
1654 }
1655 
1656 
1657 void
1658 NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1659 	icon_size size, bool async)
1660 {
1661 	BBitmap* bitmap = IconForMode(mode, size);
1662 	if (!bitmap)
1663 		return;
1664 
1665 	drawing_mode oldMode = view->DrawingMode();
1666 
1667 	if (bitmap->ColorSpace() == B_RGBA32) {
1668 		if (oldMode != B_OP_ALPHA) {
1669 			view->SetDrawingMode(B_OP_ALPHA);
1670 			view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1671 		}
1672 	} else {
1673 		view->SetDrawingMode(B_OP_OVER);
1674 	}
1675 
1676 	if (false && async) {
1677 		TRESPASS();
1678 		// need to copy the bits first in here
1679 		view->DrawBitmapAsync(bitmap, where);
1680 	} else
1681 		view->DrawBitmap(bitmap, where);
1682 
1683 	view->SetDrawingMode(oldMode);
1684 }
1685 
1686 
1687 void
1688 NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
1689 	icon_size size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
1690 	void* passThruState)
1691 {
1692 	BBitmap* bitmap = IconForMode(mode, size);
1693 	if (!bitmap)
1694 		return;
1695 
1696 //	if (bitmap->ColorSpace() == B_RGBA32) {
1697 //		view->SetDrawingMode(B_OP_ALPHA);
1698 //		view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
1699 //	} else {
1700 //		view->SetDrawingMode(B_OP_OVER);
1701 //	}
1702 
1703 	(blitFunc)(view, where, bitmap, passThruState);
1704 }
1705 
1706 
1707 const node_ref*
1708 NodeCacheEntry::Node() const
1709 {
1710 	return &fRef;
1711 }
1712 
1713 
1714 uint32
1715 NodeCacheEntry::Hash() const
1716 {
1717 	return Hash(&fRef);
1718 }
1719 
1720 
1721 uint32
1722 NodeCacheEntry::Hash(const node_ref* node)
1723 {
1724 	return node->device ^ ((uint32*)&node->node)[0]
1725 		^ ((uint32*)&node->node)[1];
1726 }
1727 
1728 
1729 bool
1730 NodeCacheEntry::operator==(const NodeCacheEntry &entry) const
1731 {
1732 	return fRef == entry.fRef;
1733 }
1734 
1735 
1736 void
1737 NodeCacheEntry::SetTo(const node_ref* node)
1738 {
1739 	fRef = *node;
1740 }
1741 
1742 
1743 bool
1744 NodeCacheEntry::Permanent() const
1745 {
1746 	return fPermanent;
1747 }
1748 
1749 
1750 void
1751 NodeCacheEntry::MakePermanent()
1752 {
1753 	fPermanent = true;
1754 }
1755 
1756 
1757 //	#pragma mark -
1758 
1759 
1760 NodeIconCache::NodeIconCache()
1761 #if DEBUG
1762 	:	SimpleIconCache("Node Icon cache aka \"The Dead-Locker\""),
1763 		fHashTable(20),
1764 		fElementArray(20)
1765 #else
1766 	:	SimpleIconCache("Tracker node icon cache"),
1767 		fHashTable(100),
1768 		fElementArray(100)
1769 #endif
1770 {
1771 	fHashTable.SetElementVector(&fElementArray);
1772 }
1773 
1774 
1775 void
1776 NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1777 	IconDrawMode mode, icon_size size, bool async)
1778 {
1779 
1780 	((NodeCacheEntry*)entry)->Draw(view, where, mode, size, async);
1781 }
1782 
1783 
1784 void
1785 NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
1786 	IconDrawMode mode, icon_size size, void (*blitFunc)(BView*, BPoint,
1787 	BBitmap*, void*), void* passThruState)
1788 {
1789 	((NodeCacheEntry*)entry)->Draw(view, where, mode, size,
1790 		blitFunc, passThruState);
1791 }
1792 
1793 
1794 NodeCacheEntry*
1795 NodeIconCache::FindItem(const node_ref* node) const
1796 {
1797 	NodeCacheEntry* result = fHashTable.FindFirst(NodeCacheEntry::Hash(node));
1798 
1799 	if (!result)
1800 		return NULL;
1801 
1802 	for(;;) {
1803 		if (*result->Node() == *node)
1804 			return result;
1805 
1806 		if (result->fNext < 0)
1807 			break;
1808 
1809 		result
1810 			= const_cast<NodeCacheEntry*>(&fElementArray.At(result->fNext));
1811 	}
1812 
1813 	return NULL;
1814 }
1815 
1816 
1817 NodeCacheEntry*
1818 NodeIconCache::AddItem(const node_ref* node, bool permanent)
1819 {
1820 	NodeCacheEntry* result = fHashTable.Add(NodeCacheEntry::Hash(node));
1821 	result->SetTo(node);
1822 	if (permanent)
1823 		result->MakePermanent();
1824 
1825 	return result;
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* result = fHashTable.Add(NodeCacheEntry::Hash(node));
1836 	result->SetTo(node);
1837 	*outstandingEntry = fHashTable.ElementAt(entryToken);
1838 
1839 	return result;
1840 }
1841 
1842 
1843 void
1844 NodeIconCache::Deleting(const node_ref* node)
1845 {
1846 	NodeCacheEntry* entry = FindItem(node);
1847 	ASSERT(entry);
1848 	if (!entry || 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);
1860 	if (!entry)
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 -
1896 
1897 
1898 NodeCacheEntryArray::NodeCacheEntryArray(int32 initialSize)
1899 	:	OpenHashElementArray<NodeCacheEntry>(initialSize)
1900 {
1901 }
1902 
1903 
1904 NodeCacheEntry*
1905 NodeCacheEntryArray::Add()
1906 {
1907 	return OpenHashElementArray<NodeCacheEntry>::Add();
1908 }
1909 
1910 
1911 //	#pragma mark -
1912 
1913 
1914 SimpleIconCache::SimpleIconCache(const char* name)
1915 	:	fLock(name)
1916 {
1917 }
1918 
1919 
1920 void
1921 SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1922 	icon_size, bool)
1923 {
1924 	TRESPASS();
1925 	// pure virtual, do nothing
1926 }
1927 
1928 
1929 void
1930 SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
1931 	icon_size, void(*)(BView*, BPoint, BBitmap*, void*), void*)
1932 {
1933 	TRESPASS();
1934 	// pure virtual, do nothing
1935 }
1936 
1937 
1938 bool
1939 SimpleIconCache::Lock()
1940 {
1941 	return fLock.Lock();
1942 }
1943 
1944 
1945 void
1946 SimpleIconCache::Unlock()
1947 {
1948 	fLock.Unlock();
1949 }
1950 
1951 
1952 bool
1953 SimpleIconCache::IsLocked() const
1954 {
1955 	return fLock.IsLocked();
1956 }
1957 
1958 
1959 //	#pragma mark -
1960 
1961 
1962 LazyBitmapAllocator::LazyBitmapAllocator(icon_size size,
1963 	color_space colorSpace, bool preallocate)
1964 	:	fBitmap(NULL),
1965 		fSize(size),
1966 		fColorSpace(colorSpace)
1967 {
1968 	if (preallocate)
1969 		Get();
1970 }
1971 
1972 
1973 LazyBitmapAllocator::~LazyBitmapAllocator()
1974 {
1975 	delete fBitmap;
1976 }
1977 
1978 
1979 BBitmap*
1980 LazyBitmapAllocator::Get()
1981 {
1982 	if (!fBitmap)
1983 		fBitmap = new BBitmap(BRect(0, 0, fSize - 1, fSize - 1), fColorSpace);
1984 
1985 	return fBitmap;
1986 }
1987 
1988 
1989 BBitmap*
1990 LazyBitmapAllocator::Adopt()
1991 {
1992 	if (!fBitmap)
1993 		Get();
1994 
1995 	BBitmap* result = fBitmap;
1996 	fBitmap = NULL;
1997 	return result;
1998 }
1999 
2000 
2001 IconCache* IconCache::sIconCache;
2002