#include <ControlLook.h>
#include <Debug.h>
#include <Screen.h>
#include <Volume.h>
#include <fs_info.h>
#include "Bitmaps.h"
#include "FSUtils.h"
#include "IconCache.h"
#include "MimeTypes.h"
#include "Model.h"
#include "Thumbnails.h"
#ifdef LOG_DISK_HITS
# define PRINT_DISK_HITS(ARGS) _debugPrintf ARGS
#else
# define PRINT_DISK_HITS(ARGS) (void)0
#endif
#ifdef LOG_ADD_ITEM
# define PRINT_ADD_ITEM(ARGS) _debugPrintf ARGS
#else
# define PRINT_ADD_ITEM(ARGS) (void)0
#endif
#undef NODE_CACHE_ASYNC_DRAWS
BSize IconCache::sMiniIconSize;
static inline icon_size
icon_size_for(BSize size)
{
ASSERT(size.Width() == size.Height());
return (icon_size)(size.IntegerWidth() + 1);
}
IconCacheEntry::IconCacheEntry()
:
fLargeIcon(NULL),
fHighlightedLargeIcon(NULL),
fMiniIcon(NULL),
fHighlightedMiniIcon(NULL),
fAliasTo(NULL)
{
}
IconCacheEntry::~IconCacheEntry()
{
if (fAliasTo == NULL) {
delete fLargeIcon;
delete fHighlightedLargeIcon;
delete fMiniIcon;
delete fHighlightedMiniIcon;
fLargeIcon = NULL;
fHighlightedLargeIcon = NULL;
fMiniIcon = NULL;
fHighlightedMiniIcon = NULL;
}
fAliasTo = NULL;
}
void
IconCacheEntry::SetAliasFor(const SharedIconCache* sharedCache,
const SharedCacheEntry* entry)
{
sharedCache->SetAliasFor(this, entry);
ASSERT(fAliasTo != NULL);
}
IconCacheEntry*
IconCacheEntry::ResolveIfAlias(const SharedIconCache* sharedCache)
{
return sharedCache->ResolveIfAlias(this);
}
IconCacheEntry*
IconCacheEntry::ResolveIfAlias(const SharedIconCache* sharedCache,
IconCacheEntry* entry)
{
if (entry == NULL)
return NULL;
return sharedCache->ResolveIfAlias(entry);
}
bool
IconCacheEntry::CanConstructBitmap(IconDrawMode mode, BSize) const
{
if (mode == kSelected) {
return true;
}
return false;
}
bool
IconCacheEntry::HaveIconBitmap(IconDrawMode mode, BSize size) const
{
ASSERT(mode == kSelected || mode == kNormalIcon);
if (mode == kNormalIcon) {
return size == IconCache::sMiniIconSize ? fMiniIcon != NULL
: fLargeIcon != NULL
&& fLargeIcon->Bounds().Size() == size;
} else if (mode == kSelected) {
return size == IconCache::sMiniIconSize ? fHighlightedMiniIcon != NULL
: fHighlightedLargeIcon != NULL
&& fHighlightedLargeIcon->Bounds().Size() == size;
}
return false;
}
BBitmap*
IconCacheEntry::IconForMode(IconDrawMode mode, BSize size) const
{
ASSERT(mode == kSelected || mode == kNormalIcon);
if (mode == kNormalIcon) {
if (size == IconCache::sMiniIconSize)
return fMiniIcon;
else
return fLargeIcon;
} else if (mode == kSelected) {
if (size == IconCache::sMiniIconSize)
return fHighlightedMiniIcon;
else
return fHighlightedLargeIcon;
}
return NULL;
}
bool
IconCacheEntry::IconHitTest(BPoint where, IconDrawMode mode,
BSize size) const
{
ASSERT(where.x < size.width && where.y < size.height);
BBitmap* bitmap = IconForMode(mode, size);
if (bitmap == NULL)
return false;
uchar* bits = (uchar*)bitmap->Bits();
ASSERT(bits != NULL);
BRect bounds(bitmap->Bounds());
bounds.InsetBy((bounds.Width() + 1.0) / 8.0, (bounds.Height() + 1.0) / 8.0);
if (bounds.Contains(where))
return true;
switch (bitmap->ColorSpace()) {
case B_RGBA32:
return *(bits + (int32)(floorf(where.y) * bitmap->BytesPerRow()
+ floorf(where.x) * 4 + 3)) > 20;
case B_CMAP8:
return *(bits + (int32)(floorf(where.y) * icon_size_for(size) + where.x))
!= B_TRANSPARENT_8_BIT;
default:
return true;
}
}
BBitmap*
IconCacheEntry::ConstructBitmap(BBitmap* constructFrom,
IconDrawMode requestedMode, IconDrawMode constructFromMode,
BSize size, LazyBitmapAllocator* lazyBitmap)
{
ASSERT(requestedMode == kSelected && constructFromMode == kNormalIcon);
if (requestedMode == kSelected && constructFromMode == kNormalIcon) {
return IconCache::sIconCache->MakeSelectedIcon(constructFrom, size,
lazyBitmap);
}
return NULL;
}
BBitmap*
IconCacheEntry::ConstructBitmap(IconDrawMode requestedMode, BSize size,
LazyBitmapAllocator* lazyBitmap)
{
BBitmap* source = (size == IconCache::sMiniIconSize) ? fMiniIcon : fLargeIcon;
ASSERT(source != NULL);
return ConstructBitmap(source, requestedMode, kNormalIcon, size,
lazyBitmap);
}
bool
IconCacheEntry::AlternateModeForIconConstructing(IconDrawMode requestedMode,
IconDrawMode &alternate, BSize)
{
if ((requestedMode & kSelected) != 0) {
alternate = kNormalIcon;
return true;
}
return false;
}
void
IconCacheEntry::SetIcon(BBitmap* bitmap, IconDrawMode mode, BSize size)
{
BBitmap** icon = NULL;
if (mode == kNormalIcon) {
if (size == IconCache::sMiniIconSize)
icon = &fMiniIcon;
else
icon = &fLargeIcon;
} else if (mode == kSelectedIcon) {
if (size == IconCache::sMiniIconSize)
icon = &fHighlightedMiniIcon;
else
icon = &fHighlightedLargeIcon;
}
if (icon == NULL)
TRESPASS();
if ((*icon) != NULL)
delete *icon;
*icon = bitmap;
}
IconCache::IconCache()
:
fInitHighlightTable(true)
{
InitHighlightTable();
sMiniIconSize = be_control_look->ComposeIconSize(B_MINI_ICON);
}
IconCacheEntry*
IconCache::GetIconForPreferredApp(const char* fileTypeSignature,
const char* preferredApp, IconDrawMode mode, BSize size,
LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
{
ASSERT(fSharedCache.IsLocked());
if (preferredApp == NULL || *preferredApp == '\0')
return NULL;
if (entry == NULL) {
entry = fSharedCache.FindItem(fileTypeSignature, preferredApp);
if (entry != NULL) {
entry = entry->ResolveIfAlias(&fSharedCache, entry);
#if xDEBUG
PRINT(("File %s; Line %d # looking for %s, type %s, found %x\n",
__FILE__, __LINE__, preferredApp, fileTypeSignature, entry));
#endif
if (entry->HaveIconBitmap(mode, size))
return entry;
}
}
if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
PRINT_DISK_HITS(
("File %s; Line %d # hitting disk for preferredApp %s, type %s\n",
__FILE__, __LINE__, preferredApp, fileTypeSignature));
BMimeType preferredAppType(preferredApp);
BString signature(fileTypeSignature);
signature.ToLower();
if (preferredAppType.GetIconForType(signature.String(),
lazyBitmap->Get(), icon_size_for(size)) != B_OK) {
return NULL;
}
BBitmap* bitmap = lazyBitmap->Adopt();
if (entry == NULL) {
PRINT_ADD_ITEM(
("File %s; Line %d # adding entry for preferredApp %s, "
"type %s\n", __FILE__, __LINE__, preferredApp,
fileTypeSignature));
entry = fSharedCache.AddItem(fileTypeSignature, preferredApp);
}
entry->SetIcon(bitmap, kNormalIcon, size);
}
if (mode != kNormalIcon
&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
entry->ConstructBitmap(mode, size, lazyBitmap);
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
}
return entry;
}
IconCacheEntry*
IconCache::GetIconFromMetaMime(const char* fileType, IconDrawMode mode,
BSize size, LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
{
ASSERT(fSharedCache.IsLocked());
if (entry == NULL)
entry = fSharedCache.FindItem(fileType);
if (entry != NULL) {
entry = entry->ResolveIfAlias(&fSharedCache, entry);
if (entry->HaveIconBitmap(mode, size))
return entry;
}
if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
PRINT_DISK_HITS(("File %s; Line %d # hitting disk for metamime %s\n",
__FILE__, __LINE__, fileType));
BMimeType mime(fileType);
if (mime.GetIcon(lazyBitmap->Get(), icon_size_for(size)) != B_OK) {
char preferredAppSig[B_MIME_TYPE_LENGTH];
if (mime.GetPreferredApp(preferredAppSig) != B_OK)
return NULL;
SharedCacheEntry* aliasTo = NULL;
if (entry != NULL) {
aliasTo
= (SharedCacheEntry*)entry->ResolveIfAlias(&fSharedCache);
}
aliasTo = (SharedCacheEntry*)GetIconForPreferredApp(fileType,
preferredAppSig, mode, size, lazyBitmap, aliasTo);
if (aliasTo == NULL)
return NULL;
if (entry == NULL) {
PRINT_ADD_ITEM(
("File %s; Line %d # adding entry as alias for type %s\n",
__FILE__, __LINE__, fileType));
entry = fSharedCache.AddItem(fileType);
entry->SetAliasFor(&fSharedCache, aliasTo);
}
ASSERT(aliasTo->HaveIconBitmap(mode, size));
return aliasTo;
}
BBitmap* bitmap = lazyBitmap->Adopt();
if (entry == NULL) {
PRINT_ADD_ITEM(("File %s; Line %d # adding entry for type %s\n",
__FILE__, __LINE__, fileType));
entry = fSharedCache.AddItem(fileType);
}
entry->SetIcon(bitmap, kNormalIcon, size);
}
ASSERT(entry != NULL);
if (mode != kNormalIcon
&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
entry->ConstructBitmap(mode, size, lazyBitmap);
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
}
#if xDEBUG
if (!entry->HaveIconBitmap(mode, size))
PRINT(("failing on %s, mode %ld, size %ld\n", fileType, mode, size));
#endif
ASSERT(entry->HaveIconBitmap(mode, size));
return entry;
}
IconCacheEntry*
IconCache::GetIconFromFileTypes(ModelNodeLazyOpener* modelOpener,
IconSource &source, IconDrawMode mode, BSize size,
LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
{
ASSERT(fSharedCache.IsLocked());
Model* model = modelOpener->TargetModel();
const char* fileType = model->MimeType();
const char* nodePreferredApp = model->PreferredAppSignature();
if (source == kUnknownSource || source == kUnknownNotFromNode
|| source == kPreferredAppForNode) {
if (nodePreferredApp[0]) {
entry = GetIconForPreferredApp(fileType, nodePreferredApp, mode,
size, lazyBitmap, entry);
#if xDEBUG
PRINT(("File %s; Line %d # looking for %s, type %s, found %x\n",
__FILE__, __LINE__, nodePreferredApp, fileType, entry));
#endif
if (entry != NULL) {
source = kPreferredAppForNode;
ASSERT(entry->HaveIconBitmap(mode, size));
return entry;
}
}
if (source == kPreferredAppForNode)
source = kUnknownSource;
}
entry = GetIconFromMetaMime(fileType, mode, size, lazyBitmap, entry);
if (entry == NULL) {
BMimeType mime(fileType);
if (!mime.IsSupertypeOnly()) {
BMimeType superType;
mime.GetSupertype(&superType);
const char* superTypeFileType = superType.Type();
if (superTypeFileType != NULL) {
entry = GetIconFromMetaMime(superTypeFileType, mode, size,
lazyBitmap, entry);
}
#if DEBUG
else {
PRINT(("File %s; Line %d # failed to get supertype for "
"type %s\n", __FILE__, __LINE__, fileType));
}
#endif
}
}
ASSERT(entry == NULL || entry->HaveIconBitmap(mode, size));
if (entry != NULL) {
if (nodePreferredApp != NULL && *nodePreferredApp != '\0') {
PRINT_ADD_ITEM(
("File %s; Line %d # adding entry as alias for "
"preferredApp %s, type %s\n",
__FILE__, __LINE__, nodePreferredApp, fileType));
IconCacheEntry* aliasedEntry
= fSharedCache.AddItem(fileType, nodePreferredApp);
aliasedEntry->SetAliasFor(&fSharedCache,
(SharedCacheEntry*)entry);
source = kPreferredAppForNode;
} else
source = kMetaMime;
#if DEBUG
if (!entry->HaveIconBitmap(mode, size))
model->PrintToStream();
#endif
ASSERT(entry->HaveIconBitmap(mode, size));
}
return entry;
}
IconCacheEntry*
IconCache::GetVolumeIcon(AutoLock<SimpleIconCache>*nodeCacheLocker,
AutoLock<SimpleIconCache>* sharedCacheLocker,
AutoLock<SimpleIconCache>** resultingOpenCache,
Model* model, IconSource &source,
IconDrawMode mode, BSize size, LazyBitmapAllocator* lazyBitmap)
{
*resultingOpenCache = nodeCacheLocker;
nodeCacheLocker->Lock();
IconCacheEntry* entry = 0;
if (source != kUnknownSource) {
entry = fNodeCache.FindItem(model->NodeRef());
if (entry != NULL) {
entry = IconCacheEntry::ResolveIfAlias(&fSharedCache, entry);
if (source == kTrackerDefault) {
*resultingOpenCache = sharedCacheLocker;
sharedCacheLocker->Lock();
}
if (entry->HaveIconBitmap(mode, size))
return entry;
}
}
if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
BVolume volume(model->NodeRef()->device);
if (volume.IsShared()) {
BBitmap* bitmap = lazyBitmap->Get();
GetTrackerResources()->GetIconResource(R_ShareIcon,
icon_size_for(size), bitmap);
if (entry == NULL) {
PRINT_ADD_ITEM(
("File %s; Line %d # adding entry for model %s\n",
__FILE__, __LINE__, model->Name()));
entry = fNodeCache.AddItem(model->NodeRef());
}
entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size);
} else if (volume.GetIcon(lazyBitmap->Get(), icon_size_for(size)) == B_OK) {
BBitmap* bitmap = lazyBitmap->Adopt();
ASSERT(bitmap != NULL);
if (entry == NULL) {
PRINT_ADD_ITEM(
("File %s; Line %d # adding entry for model %s\n",
__FILE__, __LINE__, model->Name()));
entry = fNodeCache.AddItem(model->NodeRef());
}
ASSERT(entry != NULL);
entry->SetIcon(bitmap, kNormalIcon, size);
source = kVolume;
} else {
*resultingOpenCache = sharedCacheLocker;
sharedCacheLocker->Lock();
entry = GetIconFromMetaMime(B_VOLUME_MIMETYPE, mode,
size, lazyBitmap, entry);
}
}
if (mode != kNormalIcon && entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
entry->ConstructBitmap(mode, size, lazyBitmap);
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
}
return entry;
}
IconCacheEntry*
IconCache::GetRootIcon(AutoLock<SimpleIconCache>*,
AutoLock<SimpleIconCache>* sharedCacheLocker,
AutoLock<SimpleIconCache>** resultingOpenCache,
Model*, IconSource &source, IconDrawMode mode,
BSize size, LazyBitmapAllocator* lazyBitmap)
{
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
source = kTrackerSupplied;
return GetIconFromMetaMime(B_ROOT_MIMETYPE, mode, size, lazyBitmap, 0);
}
IconCacheEntry*
IconCache::GetPrinterIcon(AutoLock<SimpleIconCache>*,
AutoLock<SimpleIconCache>* sharedCacheLocker,
AutoLock<SimpleIconCache>** resultingOpenCache,
Model*, IconSource &source, IconDrawMode mode,
BSize size, LazyBitmapAllocator* lazyBitmap)
{
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
source = kTrackerSupplied;
return GetIconFromMetaMime(B_PRINTER_MIMETYPE, mode, size, lazyBitmap, 0);
}
IconCacheEntry*
IconCache::GetWellKnownIcon(AutoLock<SimpleIconCache>*,
AutoLock<SimpleIconCache>* sharedCacheLocker,
AutoLock<SimpleIconCache>** resultingOpenCache,
Model* model, IconSource &source, IconDrawMode mode, BSize size,
LazyBitmapAllocator* lazyBitmap)
{
const WellKnowEntryList::WellKnownEntry* wellKnownEntry
= WellKnowEntryList::MatchEntry(model->NodeRef());
if (wellKnownEntry == NULL)
return NULL;
BString type("tracker/active_");
type += wellKnownEntry->name;
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
source = kTrackerSupplied;
IconCacheEntry* entry = fSharedCache.FindItem(type.String());
if (entry != NULL) {
entry = entry->ResolveIfAlias(&fSharedCache, entry);
if (entry->HaveIconBitmap(mode, size))
return entry;
}
if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
int32 resourceId = -1;
switch ((uint32)wellKnownEntry->which) {
case B_BOOT_DISK:
resourceId = R_BootVolumeIcon;
break;
case B_BEOS_DIRECTORY:
resourceId = R_BeosFolderIcon;
break;
case B_USER_DIRECTORY:
resourceId = R_HomeDirIcon;
break;
case B_SYSTEM_FONTS_DIRECTORY:
case B_SYSTEM_NONPACKAGED_FONTS_DIRECTORY:
case B_USER_FONTS_DIRECTORY:
case B_USER_NONPACKAGED_FONTS_DIRECTORY:
resourceId = R_FontDirIcon;
break;
case B_BEOS_APPS_DIRECTORY:
case B_APPS_DIRECTORY:
case B_USER_DESKBAR_APPS_DIRECTORY:
resourceId = R_AppsDirIcon;
break;
case B_BEOS_PREFERENCES_DIRECTORY:
case B_PREFERENCES_DIRECTORY:
case B_USER_DESKBAR_PREFERENCES_DIRECTORY:
resourceId = R_PrefsDirIcon;
break;
case B_USER_MAIL_DIRECTORY:
resourceId = R_MailDirIcon;
break;
case B_USER_QUERIES_DIRECTORY:
resourceId = R_QueryDirIcon;
break;
case B_SYSTEM_DEVELOP_DIRECTORY:
case B_SYSTEM_NONPACKAGED_DEVELOP_DIRECTORY:
case B_USER_DESKBAR_DEVELOP_DIRECTORY:
resourceId = R_DevelopDirIcon;
break;
case B_USER_CONFIG_DIRECTORY:
resourceId = R_ConfigDirIcon;
break;
case B_USER_PEOPLE_DIRECTORY:
resourceId = R_PersonDirIcon;
break;
case B_USER_DOWNLOADS_DIRECTORY:
resourceId = R_DownloadDirIcon;
break;
default:
return NULL;
}
entry = fSharedCache.AddItem(type.String());
BBitmap* bitmap = lazyBitmap->Get();
GetTrackerResources()->GetIconResource(resourceId,
icon_size_for(size), bitmap);
entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size);
}
if (mode != kNormalIcon
&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
entry->ConstructBitmap(mode, size, lazyBitmap);
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
}
ASSERT(entry->HaveIconBitmap(mode, size));
return entry;
}
IconCacheEntry*
IconCache::GetNodeIcon(ModelNodeLazyOpener* modelOpener,
AutoLock<SimpleIconCache>* nodeCacheLocker,
AutoLock<SimpleIconCache>** resultingOpenCache,
Model* model, IconSource& source,
IconDrawMode mode, BSize size,
LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry, bool permanent)
{
*resultingOpenCache = nodeCacheLocker;
(*resultingOpenCache)->Lock();
entry = fNodeCache.FindItem(model->NodeRef());
if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
modelOpener->OpenNode();
PRINT_DISK_HITS(("File %s; Line %d # hitting disk for node %s\n",
__FILE__, __LINE__, model->Name()));
BFile* file = NULL;
status_t result = B_ERROR;
if (model->IsExecutable()
&& (file = dynamic_cast<BFile*>(model->Node())) != NULL) {
result = GetAppIconFromAttr(file, lazyBitmap->Get(), icon_size_for(size));
} else {
result = GetThumbnailFromAttr(model, lazyBitmap->Get(), size);
if (result != B_OK && result != B_BUSY) {
result = GetFileIconFromAttr(model->Node(), lazyBitmap->Get(),
icon_size_for(size));
}
}
if (result == B_OK) {
BBitmap* bitmap = lazyBitmap->Adopt();
PRINT_ADD_ITEM(("File %s; Line %d # adding entry for model %s\n",
__FILE__, __LINE__, model->Name()));
entry = fNodeCache.AddItem(model->NodeRef(), permanent);
ASSERT(entry != NULL);
entry->SetIcon(bitmap, kNormalIcon, size);
if (mode != kNormalIcon) {
entry->ConstructBitmap(mode, size, lazyBitmap);
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
}
source = kNode;
}
}
if (entry == NULL) {
(*resultingOpenCache)->Unlock();
*resultingOpenCache = NULL;
} else if (!entry->HaveIconBitmap(mode, size)
&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
entry->ConstructBitmap(mode, size, lazyBitmap);
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
ASSERT(entry->HaveIconBitmap(mode, size));
}
return entry;
}
IconCacheEntry*
IconCache::GetGenericIcon(AutoLock<SimpleIconCache>* sharedCacheLocker,
AutoLock<SimpleIconCache>** resultingOpenCache,
Model* model, IconSource &source,
IconDrawMode mode, BSize size,
LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
{
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
entry = GetIconFromMetaMime(B_FILE_MIMETYPE, mode, size, lazyBitmap, 0);
if (entry == NULL)
return NULL;
PRINT_ADD_ITEM(
("File %s; Line %d # adding entry for preferredApp %s, type %s\n",
__FILE__, __LINE__, model->PreferredAppSignature(),
model->MimeType()));
IconCacheEntry* aliasedEntry = fSharedCache.AddItem(
model->MimeType(), model->PreferredAppSignature());
aliasedEntry->SetAliasFor(&fSharedCache, (SharedCacheEntry*)entry);
source = kMetaMime;
ASSERT(entry->HaveIconBitmap(mode, size));
return entry;
}
IconCacheEntry*
IconCache::GetFallbackIcon(AutoLock<SimpleIconCache>* sharedCacheLocker,
AutoLock<SimpleIconCache>** resultingOpenCache,
Model* model, IconDrawMode mode, BSize size,
LazyBitmapAllocator* lazyBitmap, IconCacheEntry* entry)
{
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
entry = fSharedCache.AddItem(model->MimeType(),
model->PreferredAppSignature());
BBitmap* bitmap = lazyBitmap->Get();
GetTrackerResources()->GetIconResource(R_FileIcon,
icon_size_for(size), bitmap);
entry->SetIcon(lazyBitmap->Adopt(), kNormalIcon, size);
if (mode != kNormalIcon) {
entry->ConstructBitmap(mode, size, lazyBitmap);
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
}
ASSERT(entry->HaveIconBitmap(mode, size));
return entry;
}
IconCacheEntry*
IconCache::Preload(AutoLock<SimpleIconCache>* nodeCacheLocker,
AutoLock<SimpleIconCache>* sharedCacheLocker,
AutoLock<SimpleIconCache>** resultingCache,
Model* model, IconDrawMode mode, BSize size,
bool permanent)
{
IconCacheEntry* entry = NULL;
AutoLock<SimpleIconCache>* resultingOpenCache = NULL;
{
ModelNodeLazyOpener modelOpener(model);
LazyBitmapAllocator lazyBitmap(size);
IconSource source = model->IconFrom();
if (source == kUnknownSource || source == kUnknownNotFromNode) {
if (model->IsRoot()) {
entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker, &resultingOpenCache, model,
source, mode, size, &lazyBitmap);
ASSERT(entry != NULL);
} else if (model->IsVolume()) {
entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
&resultingOpenCache, model, source, mode, size,
&lazyBitmap, entry, permanent);
if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker,
&resultingOpenCache, model, source, mode,
size, &lazyBitmap);
}
} else if (model->IsPrintersDir()) {
entry = GetPrinterIcon(nodeCacheLocker, sharedCacheLocker,
&resultingOpenCache, model, source, mode, size, &lazyBitmap);
ASSERT(entry != NULL);
} else {
if (source == kUnknownSource) {
entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
&resultingOpenCache, model, source,
mode, size, &lazyBitmap, entry, permanent);
}
if (entry == NULL) {
modelOpener.OpenNode();
resultingOpenCache = sharedCacheLocker;
resultingOpenCache->Lock();
entry = GetIconFromFileTypes(&modelOpener, source, mode,
size, &lazyBitmap, 0);
if (entry == NULL) {
entry = GetGenericIcon(sharedCacheLocker,
&resultingOpenCache, model, source, mode,
size, &lazyBitmap, entry);
}
}
}
model->SetIconFrom(source);
} else {
switch (source) {
case kNode:
resultingOpenCache = nodeCacheLocker;
resultingOpenCache->Lock();
entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
&resultingOpenCache, model, source, mode,
size, &lazyBitmap, entry, permanent);
if (entry != NULL) {
entry = IconCacheEntry::ResolveIfAlias(&fSharedCache,
entry);
if (!entry->HaveIconBitmap(mode, size)
&& entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
entry->ConstructBitmap(mode, size, &lazyBitmap);
entry->SetIcon(lazyBitmap.Adopt(), mode, size);
}
ASSERT(entry->HaveIconBitmap(mode, size));
}
break;
case kTrackerSupplied:
if (model->IsRoot()) {
entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker,
&resultingOpenCache, model, source, mode, size,
&lazyBitmap);
break;
} else if (model->IsPrintersDir()) {
entry = GetPrinterIcon(nodeCacheLocker, sharedCacheLocker,
&resultingOpenCache, model, source, mode, size,
&lazyBitmap);
break;
} else {
entry = GetWellKnownIcon(nodeCacheLocker,
sharedCacheLocker, &resultingOpenCache, model,
source, mode, size, &lazyBitmap);
if (entry != NULL)
break;
}
case kTrackerDefault:
case kVolume:
if (model->IsVolume()) {
entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
&resultingOpenCache, model, source,
mode, size, &lazyBitmap, entry, permanent);
if (entry == NULL
|| !entry->HaveIconBitmap(mode, size)) {
entry = GetVolumeIcon(nodeCacheLocker,
sharedCacheLocker, &resultingOpenCache, model,
source, mode, size, &lazyBitmap);
}
break;
}
case kMetaMime:
case kPreferredAppForType:
case kPreferredAppForNode:
resultingOpenCache = sharedCacheLocker;
resultingOpenCache->Lock();
entry = GetIconFromFileTypes(&modelOpener, source, mode,
size, &lazyBitmap, 0);
if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
entry = GetGenericIcon(sharedCacheLocker,
&resultingOpenCache, model, source, mode, size,
&lazyBitmap, entry);
}
model->SetIconFrom(source);
ASSERT(entry != NULL);
ASSERT(entry->HaveIconBitmap(mode, size));
break;
default:
TRESPASS();
break;
}
}
if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
PRINT(
("icon cache complete miss, falling back on generic icon "
"for %s\n", model->Name()));
entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
model, source, mode, size, &lazyBitmap, entry);
if (entry == NULL || !entry->HaveIconBitmap(mode, size)) {
PRINT(
("icon cache complete miss, falling back on generic "
"icon for %s\n", model->Name()));
entry = GetFallbackIcon(sharedCacheLocker,
&resultingOpenCache, model, mode, size, &lazyBitmap,
entry);
}
model->SetIconFrom(kUnknownSource);
}
}
ASSERT(entry != NULL && entry->HaveIconBitmap(mode, size));
if (resultingCache != NULL)
*resultingCache = resultingOpenCache;
return entry;
}
void
IconCache::Draw(Model* model, BView* view, BPoint where, IconDrawMode mode,
BSize size, bool async)
{
AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
AutoLock<SimpleIconCache>* resultingCacheLocker;
IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
&resultingCacheLocker, model, mode, size, false);
if (entry == NULL)
return;
ASSERT(entry != NULL);
ASSERT(entry->HaveIconBitmap(mode, size));
resultingCacheLocker->LockedItem()->Draw(entry, view, where, mode,
size, async);
}
void
IconCache::SyncDraw(Model* model, BView* view, BPoint where,
IconDrawMode mode, BSize size,
void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
void* passThruState)
{
AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
AutoLock<SimpleIconCache>* resultingCacheLocker;
IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
&resultingCacheLocker, model, mode, size, false);
if (entry == NULL)
return;
ASSERT(entry != NULL);
ASSERT(entry->HaveIconBitmap(mode, size));
resultingCacheLocker->LockedItem()->Draw(entry, view, where,
mode, size, blitFunc, passThruState);
}
void
IconCache::Preload(Model* model, IconDrawMode mode, BSize size,
bool permanent)
{
AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
Preload(&nodeCacheLocker, &sharedCacheLocker, 0, model, mode, size,
permanent);
}
status_t
IconCache::Preload(const char* fileType, IconDrawMode mode, BSize size)
{
AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache);
LazyBitmapAllocator lazyBitmap(size);
BMimeType mime(fileType);
char preferredAppSig[B_MIME_TYPE_LENGTH];
status_t result = mime.GetPreferredApp(preferredAppSig);
if (result != B_OK)
return result;
IconCacheEntry* entry = GetIconForPreferredApp(fileType, preferredAppSig,
mode, size, &lazyBitmap, 0);
if (entry != NULL)
return B_OK;
result = mime.GetIcon(lazyBitmap.Get(), icon_size_for(size));
if (result != B_OK)
return result;
entry = fSharedCache.AddItem(fileType);
BBitmap* bitmap = lazyBitmap.Adopt();
entry->SetIcon(bitmap, kNormalIcon, size);
if (mode != kNormalIcon) {
entry->ConstructBitmap(mode, size, &lazyBitmap);
entry->SetIcon(lazyBitmap.Adopt(), mode, size);
}
return B_OK;
}
void
IconCache::Deleting(const Model* model)
{
AutoLock<SimpleIconCache> lock(&fNodeCache);
if (model->IconFrom() == kNode)
fNodeCache.Deleting(model->NodeRef());
}
void
IconCache::Removing(const Model* model)
{
AutoLock<SimpleIconCache> lock(&fNodeCache);
if (model->IconFrom() == kNode)
fNodeCache.Removing(model->NodeRef());
}
void
IconCache::Deleting(const BView* view)
{
AutoLock<SimpleIconCache> lock(&fNodeCache);
fNodeCache.Deleting(view);
}
void
IconCache::IconChanged(Model* model)
{
AutoLock<SimpleIconCache> lock(&fNodeCache);
if (model->IconFrom() == kNode || model->IconFrom() == kVolume)
fNodeCache.Deleting(model->NodeRef());
model->ResetIconFrom();
}
void
IconCache::IconChanged(const char* mimeType, const char* appSignature)
{
AutoLock<SimpleIconCache> sharedLock(&fSharedCache);
SharedCacheEntry* entry = fSharedCache.FindItem(mimeType, appSignature);
if (entry == NULL)
return;
AutoLock<SimpleIconCache> nodeLock(&fNodeCache);
entry = (SharedCacheEntry*)fSharedCache.ResolveIfAlias(entry);
ASSERT(entry != NULL);
fNodeCache.RemoveAliasesTo(entry);
fSharedCache.RemoveAliasesTo(entry);
fSharedCache.IconChanged(entry);
}
BBitmap*
IconCache::MakeSelectedIcon(const BBitmap* normal, BSize size,
LazyBitmapAllocator* lazyBitmap)
{
return MakeTransformedIcon(normal, size, fHighlightTable, lazyBitmap);
}
#if xDEBUG
static void
DumpBitmap(const BBitmap* bitmap)
{
if (bitmap == NULL) {
printf("NULL bitmap passed to DumpBitmap\n");
return;
}
int32 length = bitmap->BitsLength();
printf("data length %ld \n", length);
int32 columns = (int32)bitmap->Bounds().Width() + 1;
const unsigned char* bitPtr = (const unsigned char*)bitmap->Bits();
for (; length >= 0; length--) {
for (int32 columnIndex = 0; columnIndex < columns;
columnIndex++, length--)
printf("%c%c", "0123456789ABCDEF"[(*bitPtr)/0x10],
"0123456789ABCDEF"[(*bitPtr++)%0x10]);
printf("\n");
}
printf("\n");
}
#endif
void
IconCache::InitHighlightTable()
{
BScreen screen(B_MAIN_SCREEN_ID);
rgb_color color;
for (int32 index = 0; index < kColorTransformTableSize; index++) {
color = screen.ColorForIndex((uchar)index);
fHighlightTable[index] = screen.IndexForColor(tint_color(color, 1.3f));
}
fHighlightTable[B_TRANSPARENT_8_BIT] = B_TRANSPARENT_8_BIT;
fInitHighlightTable = false;
}
BBitmap*
IconCache::MakeTransformedIcon(const BBitmap* source, BSize ,
int32 colorTransformTable[], LazyBitmapAllocator* lazyBitmap)
{
if (fInitHighlightTable)
InitHighlightTable();
BBitmap* result = lazyBitmap->Get();
uint8* src = (uint8*)source->Bits();
uint8* dst = (uint8*)result->Bits();
if (result->ColorSpace() != source->ColorSpace()
|| result->Bounds() != source->Bounds()) {
printf("IconCache::MakeTransformedIcon() - bitmap format mismatch!\n");
return NULL;
}
switch (result->ColorSpace()) {
case B_RGB32:
case B_RGBA32: {
uint32 width = source->Bounds().IntegerWidth() + 1;
uint32 height = source->Bounds().IntegerHeight() + 1;
uint32 srcBPR = source->BytesPerRow();
uint32 dstBPR = result->BytesPerRow();
for (uint32 y = 0; y < height; y++) {
uint8* d = dst;
uint8* s = src;
for (uint32 x = 0; x < width; x++) {
d[0] = (int)s[0] * 168 >> 8;
d[1] = (int)s[1] * 168 >> 8;
d[2] = (int)s[2] * 168 >> 8;
d[3] = s[3];
d += 4;
s += 4;
}
dst += dstBPR;
src += srcBPR;
}
break;
}
case B_CMAP8: {
int32 bitsLength = result->BitsLength();
for (int32 i = 0; i < bitsLength; i++)
*dst++ = (uint8)colorTransformTable[*src++];
break;
}
default:
memset(dst, 0, result->BitsLength());
break;
}
return result;
}
bool
IconCache::IconHitTest(BPoint where, const Model* model, IconDrawMode mode,
BSize size)
{
AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
AutoLock<SimpleIconCache>* resultingCacheLocker;
IconCacheEntry* entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
&resultingCacheLocker, const_cast<Model*>(model), mode, size, false);
if (entry != NULL)
return entry->IconHitTest(where, mode, size);
return false;
}
void
IconCacheEntry::RetireIcons(BObjectList<BBitmap, true>* retiredBitmapList)
{
if (fLargeIcon != NULL) {
retiredBitmapList->AddItem(fLargeIcon);
fLargeIcon = NULL;
}
if (fHighlightedLargeIcon != NULL) {
retiredBitmapList->AddItem(fHighlightedLargeIcon);
fHighlightedLargeIcon = NULL;
}
if (fMiniIcon != NULL) {
retiredBitmapList->AddItem(fMiniIcon);
fMiniIcon = NULL;
}
if (fHighlightedMiniIcon != NULL) {
retiredBitmapList->AddItem(fHighlightedMiniIcon);
fHighlightedMiniIcon = NULL;
}
int32 count = retiredBitmapList->CountItems();
if (count > 10 * 1024) {
PRINT(("nuking old icons from the retired bitmap list\n"));
for (count = 512; count > 0; count--)
delete retiredBitmapList->RemoveItemAt(0);
}
}
SharedIconCache::SharedIconCache()
:
SimpleIconCache("Tracker shared icon cache"),
fHashTable(),
fRetiredBitmaps(256)
{
fHashTable.Init(256);
}
void
SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
IconDrawMode mode, BSize size, bool async)
{
((SharedCacheEntry*)entry)->Draw(view, where, mode, size, async);
}
void
SharedIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
IconDrawMode mode, BSize size, void (*blitFunc)(BView*, BPoint,
BBitmap*, void*), void* passThruState)
{
((SharedCacheEntry*)entry)->Draw(view, where, mode, size,
blitFunc, passThruState);
}
SharedCacheEntry*
SharedIconCache::FindItem(const char* fileType,
const char* appSignature) const
{
ASSERT(fileType);
if (!fileType)
fileType = B_FILE_MIMETYPE;
return fHashTable.Lookup(SharedCacheEntry::TypeAndSignature(fileType,
appSignature));
}
SharedCacheEntry*
SharedIconCache::AddItem(const char* fileType, const char* appSignature)
{
ASSERT(fileType != NULL);
if (fileType == NULL)
fileType = B_FILE_MIMETYPE;
SharedCacheEntry* entry = new SharedCacheEntry(fileType, appSignature);
if (fHashTable.Insert(entry) == B_OK)
return entry;
delete entry;
return NULL;
}
void
SharedIconCache::IconChanged(SharedCacheEntry* entry)
{
ASSERT(entry->fAliasTo == NULL);
entry->RetireIcons(&fRetiredBitmaps);
fHashTable.Remove(entry);
}
void
SharedIconCache::RemoveAliasesTo(SharedCacheEntry* alias)
{
EntryHashTable::Iterator it = fHashTable.GetIterator();
while (it.HasNext()) {
SharedCacheEntry* entry = it.Next();
if (entry->fAliasTo == alias)
fHashTable.RemoveUnchecked(entry);
}
}
void
SharedIconCache::SetAliasFor(IconCacheEntry* entry,
const SharedCacheEntry* original) const
{
entry->fAliasTo = original;
}
SharedCacheEntry::SharedCacheEntry()
:
fNext(NULL)
{
}
SharedCacheEntry::SharedCacheEntry(const char* fileType,
const char* appSignature)
:
fNext(NULL),
fFileType(fileType),
fAppSignature(appSignature)
{
}
void
SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
BSize size, bool async)
{
BBitmap* bitmap = IconForMode(mode, size);
ASSERT(bitmap != NULL);
drawing_mode oldMode = view->DrawingMode();
if (bitmap->ColorSpace() == B_RGBA32) {
if (oldMode != B_OP_ALPHA) {
view->SetDrawingMode(B_OP_ALPHA);
view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
}
} else
view->SetDrawingMode(B_OP_OVER);
if (async)
view->DrawBitmapAsync(bitmap, where);
else
view->DrawBitmap(bitmap, where);
view->SetDrawingMode(oldMode);
}
void
SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
BSize size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
void* passThruState)
{
BBitmap* bitmap = IconForMode(mode, size);
if (bitmap == NULL)
return;
(blitFunc)(view, where, bitmap, passThruState);
}
size_t
SharedCacheEntry::Hash(const TypeAndSignature& typeAndSignature)
{
size_t hash = SeededHashString(typeAndSignature.type, 0);
if (typeAndSignature.signature != NULL
&& *typeAndSignature.signature != '\0')
hash = SeededHashString(typeAndSignature.signature, hash);
return hash;
}
size_t
SharedCacheEntry::Hash() const
{
return Hash(TypeAndSignature(fFileType.String(), fAppSignature.String()));
}
bool
SharedCacheEntry::operator==(const TypeAndSignature& typeAndSignature) const
{
return fFileType == typeAndSignature.type
&& fAppSignature == typeAndSignature.signature;
}
NodeCacheEntry::NodeCacheEntry(bool permanent)
:
fNext(NULL),
fPermanent(permanent)
{
}
NodeCacheEntry::NodeCacheEntry(const node_ref* node, bool permanent)
:
fNext(NULL),
fRef(*node),
fPermanent(permanent)
{
}
void
NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
BSize size, bool async)
{
BBitmap* bitmap = IconForMode(mode, size);
if (bitmap == NULL)
return;
drawing_mode oldMode = view->DrawingMode();
if (bitmap->ColorSpace() == B_RGBA32) {
if (oldMode != B_OP_ALPHA) {
view->SetDrawingMode(B_OP_ALPHA);
view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
}
} else
view->SetDrawingMode(B_OP_OVER);
if (false && async) {
TRESPASS();
view->DrawBitmapAsync(bitmap, where);
} else
view->DrawBitmap(bitmap, where);
view->SetDrawingMode(oldMode);
}
void
NodeCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode,
BSize size, void (*blitFunc)(BView*, BPoint, BBitmap*, void*),
void* passThruState)
{
BBitmap* bitmap = IconForMode(mode, size);
if (bitmap == NULL)
return;
(blitFunc)(view, where, bitmap, passThruState);
}
const node_ref*
NodeCacheEntry::Node() const
{
return &fRef;
}
size_t
NodeCacheEntry::Hash() const
{
return Hash(&fRef);
}
size_t
NodeCacheEntry::Hash(const node_ref* node)
{
return node->device ^ ((uint32*)&node->node)[0]
^ ((uint32*)&node->node)[1];
}
bool
NodeCacheEntry::operator==(const node_ref* node) const
{
return fRef == *node;
}
bool
NodeCacheEntry::Permanent() const
{
return fPermanent;
}
NodeIconCache::NodeIconCache()
:
SimpleIconCache("Tracker node icon cache")
{
fHashTable.Init(100);
}
void
NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
IconDrawMode mode, BSize size, bool async)
{
((NodeCacheEntry*)entry)->Draw(view, where, mode, size, async);
}
void
NodeIconCache::Draw(IconCacheEntry* entry, BView* view, BPoint where,
IconDrawMode mode, BSize size,
void (*blitFunc)(BView*, BPoint, BBitmap*, void*), void* passThruState)
{
((NodeCacheEntry*)entry)->Draw(view, where, mode, size,
blitFunc, passThruState);
}
NodeCacheEntry*
NodeIconCache::FindItem(const node_ref* node) const
{
return fHashTable.Lookup(node);
}
NodeCacheEntry*
NodeIconCache::AddItem(const node_ref* node, bool permanent)
{
NodeCacheEntry* entry = new NodeCacheEntry(node, permanent);
if (fHashTable.Insert(entry) == B_OK)
return entry;
delete entry;
return NULL;
}
void
NodeIconCache::Deleting(const node_ref* node)
{
NodeCacheEntry* entry = FindItem(node);
if (entry == NULL || entry->Permanent())
return;
fHashTable.Remove(entry);
}
void
NodeIconCache::Removing(const node_ref* node)
{
NodeCacheEntry* entry = FindItem(node);
ASSERT(entry != NULL);
if (entry == NULL)
return;
fHashTable.Remove(entry);
}
void
NodeIconCache::Deleting(const BView*)
{
#ifdef NODE_CACHE_ASYNC_DRAWS
TRESPASS();
#endif
}
void
NodeIconCache::IconChanged(const Model* model)
{
Deleting(model->NodeRef());
}
void
NodeIconCache::RemoveAliasesTo(SharedCacheEntry* alias)
{
EntryHashTable::Iterator it = fHashTable.GetIterator();
while (it.HasNext()) {
NodeCacheEntry* entry = it.Next();
if (entry->fAliasTo == alias)
fHashTable.RemoveUnchecked(entry);
}
}
SimpleIconCache::SimpleIconCache(const char* name)
:
fLock(name)
{
}
void
SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
BSize, bool)
{
TRESPASS();
}
void
SimpleIconCache::Draw(IconCacheEntry*, BView*, BPoint, IconDrawMode,
BSize, void(*)(BView*, BPoint, BBitmap*, void*), void*)
{
TRESPASS();
}
bool
SimpleIconCache::Lock()
{
return fLock.Lock();
}
void
SimpleIconCache::Unlock()
{
fLock.Unlock();
}
bool
SimpleIconCache::IsLocked() const
{
return fLock.IsLocked();
}
LazyBitmapAllocator::LazyBitmapAllocator(BSize size,
color_space colorSpace, bool preallocate)
:
fBitmap(NULL),
fSize(size),
fColorSpace(colorSpace)
{
if (preallocate)
Get();
}
LazyBitmapAllocator::~LazyBitmapAllocator()
{
delete fBitmap;
}
BBitmap*
LazyBitmapAllocator::Get()
{
if (fBitmap == NULL)
fBitmap = new BBitmap(BRect(BPoint(0, 0), fSize), fColorSpace);
return fBitmap;
}
BBitmap*
LazyBitmapAllocator::Adopt()
{
if (fBitmap == NULL)
Get();
BBitmap* bitmap = fBitmap;
fBitmap = NULL;
return bitmap;
}
IconCache* IconCache::sIconCache;