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