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