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