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