haiku/src/kits/tracker/IconCache.cpp
Axel Dörfler 67fb7cd0ed Minor cleanup.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20443 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-03-28 08:47:28 +00:00

1959 lines
49 KiB
C++

/*
Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
// Icon cache is used for drawing node icons; it caches icons
// and reuses them for successive draws
//
// Possible performance improvements:
// - Mime API requires BBitmaps to retrieve bits and successive
// SetBits that cause app server contention
// Consider having special purpose "give me just the bits" calls
// to deal with that.
// - Related to this, node cache entries would only store the raw bits
// to cut down on number of BBitmaps and related overhead
// - Make the cache miss and fill case for the shared cache reuse the
// already calculated hash value
//
// Other ToDo items:
// Use lazily allocated bitmap arrays for every view for node icon cache
// drawing
// Have an overflow list for deleting shared icons, delete from the list
// every now and then
// Actual icon lookup sequence:
// icon from node
// preferred app for node -> icon for type
// preferred app for type -> icon for type
// metamime -> icon for type
// preferred app for supertype -> icon for type
// supertype metamime -> icon for type
// generic icon
#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"
#if DEBUG
// #define LOG_DISK_HITS
// the LOG_DISK_HITS define is used to check that the disk is not hit more
// than needed - enable it, open a window with a bunch of poses, force
// it to redraw, shouldn't recache
// #define LOG_ADD_ITEM
#endif
// set up a few printing macros to get rid of a ton of debugging ifdefs in the code
#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
IconCacheEntry::IconCacheEntry()
: fLargeIcon(NULL),
fMiniIcon(NULL),
fHilitedLargeIcon(NULL),
fHilitedMiniIcon(NULL),
fAliasForIndex(-1)
{
}
IconCacheEntry::~IconCacheEntry()
{
if (fAliasForIndex < 0) {
delete fLargeIcon;
delete fMiniIcon;
delete fHilitedLargeIcon;
delete fHilitedMiniIcon;
// clean up a bit to leave the hash table entry in an initialized state
fLargeIcon = NULL;
fMiniIcon = NULL;
fHilitedLargeIcon = NULL;
fHilitedMiniIcon = NULL;
}
fAliasForIndex = -1;
}
void
IconCacheEntry::SetAliasFor(const SharedIconCache *sharedCache,
const SharedCacheEntry *entry)
{
sharedCache->SetAliasFor(this, entry);
ASSERT(fAliasForIndex >= 0);
}
IconCacheEntry *
IconCacheEntry::ResolveIfAlias(const SharedIconCache *sharedCache)
{
return sharedCache->ResolveIfAlias(this);
}
IconCacheEntry *
IconCacheEntry::ResolveIfAlias(const SharedIconCache *sharedCache,
IconCacheEntry *entry)
{
if (!entry)
return NULL;
return sharedCache->ResolveIfAlias(entry);
}
bool
IconCacheEntry::CanConstructBitmap(IconDrawMode mode, icon_size) const
{
if (mode == kSelected)
// for now only
return true;
return false;
}
bool
IconCacheEntry::HaveIconBitmap(IconDrawMode mode, icon_size size) const
{
ASSERT(mode == kSelected || mode == kNormalIcon);
// for now only
if (mode == kNormalIcon) {
if (size == B_MINI_ICON)
return fMiniIcon != NULL;
else
return fLargeIcon != NULL
&& fLargeIcon->Bounds().IntegerWidth() + 1 == size;
} else if (mode == kSelected) {
if (size == B_MINI_ICON)
return fHilitedMiniIcon != NULL;
else
return fHilitedLargeIcon != NULL
&& fHilitedLargeIcon->Bounds().IntegerWidth() + 1 == size;
}
return false;
}
BBitmap *
IconCacheEntry::IconForMode(IconDrawMode mode, icon_size size) const
{
ASSERT(mode == kSelected || mode == kNormalIcon);
// for now only
if (mode == kNormalIcon) {
if (size == B_MINI_ICON)
return fMiniIcon;
else
return fLargeIcon;
} else if (mode == kSelected) {
if (size == B_MINI_ICON)
return fHilitedMiniIcon;
else
return fHilitedLargeIcon;
}
return NULL;
}
bool
IconCacheEntry::IconHitTest(BPoint where, IconDrawMode mode, icon_size size) const
{
ASSERT(where.x < size && where.y < size);
BBitmap *bitmap = IconForMode(mode, size);
if (!bitmap)
return false;
uchar *bits = (uchar *)bitmap->Bits();
ASSERT(bits);
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:
// test alpha channel
return *(bits + (int32)(floorf(where.y) * bitmap->BytesPerRow()
+ floorf(where.x) * 4 + 3)) > 20;
case B_CMAP8:
return *(bits + (int32)(floorf(where.y) * size + where.x))
!= B_TRANSPARENT_8_BIT;
default:
return true;
}
}
BBitmap *
IconCacheEntry::ConstructBitmap(BBitmap *constructFrom, IconDrawMode requestedMode,
IconDrawMode constructFromMode, icon_size size, LazyBitmapAllocator *lazyBitmap)
{
ASSERT(requestedMode == kSelected && constructFromMode == kNormalIcon);
// for now
if (requestedMode == kSelected && constructFromMode == kNormalIcon)
return IconCache::sIconCache->MakeSelectedIcon(constructFrom, size, lazyBitmap);
return NULL;
}
BBitmap *
IconCacheEntry::ConstructBitmap(IconDrawMode requestedMode, icon_size size,
LazyBitmapAllocator *lazyBitmap)
{
BBitmap *source = (size == B_MINI_ICON) ? fMiniIcon : fLargeIcon;
ASSERT(source);
return ConstructBitmap(source, requestedMode, kNormalIcon, size, lazyBitmap);
}
bool
IconCacheEntry::AlternateModeForIconConstructing(IconDrawMode requestedMode,
IconDrawMode &alternate, icon_size)
{
if (requestedMode & kSelected) {
// for now
alternate = kNormalIcon;
return true;
}
return false;
}
void
IconCacheEntry::SetIcon(BBitmap *bitmap, IconDrawMode mode, icon_size size,
bool /*create*/)
{
if (mode == kNormalIcon) {
if (size == B_MINI_ICON)
fMiniIcon = bitmap;
else
fLargeIcon = bitmap;
} else if (mode == kSelectedIcon) {
if (size == B_MINI_ICON)
fHilitedMiniIcon = bitmap;
else
fHilitedLargeIcon = bitmap;
} else
TRESPASS();
}
IconCache::IconCache()
: fInitHiliteTable(true)
{
InitHiliteTable();
}
// The following calls use the icon lookup sequence node-prefered app for node-
// metamime-preferred app for metamime to find an icon;
// if we are trying to get a specialized icon, we will first look for a normal
// icon in each of the locations, if we get a hit, we look for the specialized,
// if we don't find one, we try to auto-construct one, if we can't we assume the
// icon is not available
// for now the code only looks for normal icons, selected icons are auto-generated
IconCacheEntry *
IconCache::GetIconForPreferredApp(const char *fileTypeSignature,
const char *preferredApp, IconDrawMode mode, icon_size size,
LazyBitmapAllocator *lazyBitmap, IconCacheEntry *entry)
{
ASSERT(fSharedCache.IsLocked());
if (!preferredApp[0])
return NULL;
if (!entry) {
entry = fSharedCache.FindItem(fileTypeSignature, preferredApp);
if (entry) {
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 || !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(),
size) != B_OK)
return NULL;
BBitmap *bitmap = lazyBitmap->Adopt();
if (!entry) {
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,
icon_size size, LazyBitmapAllocator *lazyBitmap, IconCacheEntry *entry)
{
ASSERT(fSharedCache.IsLocked());
if (!entry)
entry = fSharedCache.FindItem(fileType);
if (entry) {
entry = entry->ResolveIfAlias(&fSharedCache, entry);
// metamime defines an icon and we have it cached
if (entry->HaveIconBitmap(mode, size))
return entry;
}
if (!entry || !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);
// try getting the icon directly from the metamime
if (mime.GetIcon(lazyBitmap->Get(), size) != B_OK) {
// try getting it from the preferred app of this type
char preferredAppSig[B_MIME_TYPE_LENGTH];
if (mime.GetPreferredApp(preferredAppSig) != B_OK)
return NULL;
SharedCacheEntry *aliasTo = NULL;
if (entry)
aliasTo = (SharedCacheEntry *)entry->ResolveIfAlias(&fSharedCache);
// look for icon defined by preferred app from metamime
aliasTo = (SharedCacheEntry *)GetIconForPreferredApp(fileType,
preferredAppSig, mode, size, lazyBitmap, aliasTo);
if (aliasTo == NULL)
return NULL;
// make an aliased entry so that the next time we get a
// hit on the first FindItem in here
if (!entry) {
PRINT_ADD_ITEM(("File %s; Line %d # adding entry as alias for type %s\n",
__FILE__, __LINE__, fileType));
entry = fSharedCache.AddItem(&aliasTo, fileType);
entry->SetAliasFor(&fSharedCache, aliasTo);
}
ASSERT(aliasTo->HaveIconBitmap(mode, size));
return aliasTo;
}
// at this point, we've found an icon for the MIME type
BBitmap *bitmap = lazyBitmap->Adopt();
if (!entry) {
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);
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, icon_size size,
LazyBitmapAllocator *lazyBitmap, IconCacheEntry *entry)
{
ASSERT(fSharedCache.IsLocked());
// use file types to get the icon
Model *model = modelOpener->TargetModel();
const char *fileType = model->MimeType();
const char *nodePreferredApp = model->PreferredAppSignature();
if (source == kUnknownSource || source == kUnknownNotFromNode
|| source == kPreferredAppForNode) {
if (nodePreferredApp[0]) {
// file has a locally set preferred app, try getting an icon from
// there
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) {
source = kPreferredAppForNode;
ASSERT(entry->HaveIconBitmap(mode, size));
return entry;
}
}
if (source == kPreferredAppForNode)
source = kUnknownSource;
}
entry = GetIconFromMetaMime(fileType, mode, size, lazyBitmap, entry);
if (!entry) {
// Try getting a supertype handler icon
BMimeType mime(fileType);
if (!mime.IsSupertypeOnly()) {
BMimeType superType;
mime.GetSupertype(&superType);
const char *superTypeFileType = superType.Type();
if (superTypeFileType)
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 || entry->HaveIconBitmap(mode, size));
if (entry) {
if (nodePreferredApp[0]) {
// we got a miss using GetIconForPreferredApp before, cache this
// fileType/preferredApp combo with an aliased entry
// make an aliased entry so that the next time we get a
// hit and substitute a generic icon right away
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((SharedCacheEntry **)&entry,
fileType, nodePreferredApp);
aliasedEntry->SetAliasFor(&fSharedCache, (SharedCacheEntry *)entry);
// OK to cast here, have a runtime check
source = kPreferredAppForNode;
// set source as preferred for node, so that next time we get a hit in
// the initial find that uses GetIconForPreferredApp
} 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, icon_size size, LazyBitmapAllocator *lazyBitmap)
{
*resultingOpenCache = nodeCacheLocker;
nodeCacheLocker->Lock();
IconCacheEntry *entry = 0;
if (source != kUnknownSource) {
// cached in the node cache
entry = fNodeCache.FindItem(model->NodeRef());
if (entry) {
entry = IconCacheEntry::ResolveIfAlias(&fSharedCache, entry);
if (source == kTrackerDefault) {
// if tracker default, resolved entry is from shared cache
// this could be done a little cleaner if entry had a way to reach
// the cache it is in
*resultingOpenCache = sharedCacheLocker;
sharedCacheLocker->Lock();
}
if (entry->HaveIconBitmap(mode, size))
return entry;
}
}
// try getting using the BVolume::GetIcon call; if miss,
// go for the default mime based icon
if (!entry || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
BVolume volume(model->NodeRef()->device);
if (volume.IsShared()) {
// Check if it's a network share and give it a special icon
BBitmap *bitmap = lazyBitmap->Get();
GetTrackerResources()->GetIconResource(kResShareIcon, size, bitmap);
if (!entry) {
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(), size) == B_OK) {
// Ask the device for an icon
BBitmap *bitmap = lazyBitmap->Adopt();
ASSERT(bitmap);
if (!entry) {
PRINT_ADD_ITEM(("File %s; Line %d # adding entry for model %s\n",
__FILE__, __LINE__, model->Name()));
entry = fNodeCache.AddItem(model->NodeRef());
}
ASSERT(entry);
entry->SetIcon(bitmap, kNormalIcon, size);
source = kVolume;
} else {
*resultingOpenCache = sharedCacheLocker;
sharedCacheLocker->Lock();
// If the volume doesnt have a device it should have the generic icon
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,
icon_size size, LazyBitmapAllocator *lazyBitmap)
{
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
source = kTrackerSupplied;
return GetIconFromMetaMime(B_ROOT_MIMETYPE, mode, size, lazyBitmap, 0);
}
IconCacheEntry *
IconCache::GetWellKnownIcon(AutoLock<SimpleIconCache> *,
AutoLock<SimpleIconCache> *sharedCacheLocker,
AutoLock<SimpleIconCache> **resultingOpenCache,
Model *model, IconSource &source, IconDrawMode mode, icon_size size,
LazyBitmapAllocator *lazyBitmap)
{
const WellKnowEntryList::WellKnownEntry *wellKnownEntry
= WellKnowEntryList::MatchEntry(model->NodeRef());
if (!wellKnownEntry)
return NULL;
IconCacheEntry *entry = NULL;
BString type("tracker/active_");
type += wellKnownEntry->name;
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
source = kTrackerSupplied;
entry = fSharedCache.FindItem(type.String());
if (entry) {
entry = entry->ResolveIfAlias(&fSharedCache, entry);
if (entry->HaveIconBitmap(mode, size))
return entry;
}
if (!entry || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
// match up well known entries in the file system with specialized
// icons stored in Tracker's resources
int32 resid = -1;
switch (wellKnownEntry->which) {
case B_BOOT_DISK:
resid = kResBootVolumeIcon;
break;
case B_BEOS_DIRECTORY:
resid = kResBeosFolderIcon;
break;
case B_USER_DIRECTORY:
resid = kResHomeDirIcon;
break;
case B_BEOS_FONTS_DIRECTORY:
case B_COMMON_FONTS_DIRECTORY:
case B_USER_FONTS_DIRECTORY:
resid = kResFontDirIcon;
break;
case B_BEOS_APPS_DIRECTORY:
case B_APPS_DIRECTORY:
case B_USER_DESKBAR_APPS_DIRECTORY:
resid = kResAppsDirIcon;
break;
case B_BEOS_PREFERENCES_DIRECTORY:
case B_PREFERENCES_DIRECTORY:
case B_USER_DESKBAR_PREFERENCES_DIRECTORY:
resid = kResPrefsDirIcon;
break;
case B_USER_MAIL_DIRECTORY:
resid = kResMailDirIcon;
break;
case B_USER_QUERIES_DIRECTORY:
resid = kResQueryDirIcon;
break;
case B_COMMON_DEVELOP_DIRECTORY:
case B_USER_DESKBAR_DEVELOP_DIRECTORY:
resid = kResDevelopDirIcon;
break;
case B_USER_CONFIG_DIRECTORY:
resid = kResConfigDirIcon;
break;
case B_USER_PEOPLE_DIRECTORY:
resid = kResPersonDirIcon;
break;
case B_USER_DOWNLOADS_DIRECTORY:
resid = kResDownloadDirIcon;
break;
default:
return NULL;
}
entry = fSharedCache.AddItem(type.String());
BBitmap *bitmap = lazyBitmap->Get();
GetTrackerResources()->GetIconResource(resid, 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, icon_size size,
LazyBitmapAllocator *lazyBitmap, IconCacheEntry *entry, bool permanent)
{
*resultingOpenCache = nodeCacheLocker;
(*resultingOpenCache)->Lock();
entry = fNodeCache.FindItem(model->NodeRef());
if (!entry || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
modelOpener->OpenNode();
BFile *file = NULL;
// if we are dealing with an application, use the BAppFileInfo
// superset of node; this makes GetIcon grab the proper icon for
// an app
if (model->IsExecutable())
file = dynamic_cast<BFile *>(model->Node());
PRINT_DISK_HITS(("File %s; Line %d # hitting disk for node %s\n",
__FILE__, __LINE__, model->Name()));
status_t result;
if (file)
result = GetAppIconFromAttr(file, lazyBitmap->Get(), size);
else
result = GetFileIconFromAttr(model->Node(), lazyBitmap->Get(), size);
if (result == B_OK) {
// node has it's own icon, use it
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);
entry->SetIcon(bitmap, kNormalIcon, size);
if (mode != kNormalIcon) {
entry->ConstructBitmap(mode, size, lazyBitmap);
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
}
source = kNode;
}
}
if (!entry) {
(*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, icon_size size,
LazyBitmapAllocator *lazyBitmap, IconCacheEntry *entry)
{
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
entry = GetIconFromMetaMime(B_FILE_MIMETYPE, mode,
size, lazyBitmap, 0);
if (!entry)
return NULL;
// make an aliased entry so that the next time we get a
// hit and substitute a generic icon right away
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(
(SharedCacheEntry **)&entry, 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, icon_size size,
LazyBitmapAllocator *lazyBitmap, IconCacheEntry *entry)
{
*resultingOpenCache = sharedCacheLocker;
(*resultingOpenCache)->Lock();
entry = fSharedCache.AddItem(model->MimeType(),
model->PreferredAppSignature());
BBitmap *bitmap = lazyBitmap->Get();
GetTrackerResources()->GetIconResource(kResFileIcon, 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, icon_size size,
bool permanent)
{
IconCacheEntry *entry = NULL;
AutoLock<SimpleIconCache> *resultingOpenCache = NULL;
// resultingOpenCache is the locker that points to the cache that
// ended with a hit and will be used for the drawing
{ // scope for modelOpener
ModelNodeLazyOpener modelOpener(model);
// this opener takes care of opening the model and possibly
// closing it when we are done
LazyBitmapAllocator lazyBitmap(size);
// lazyBitmap manages bitmap allocation and freeing if needed
IconSource source = model->IconFrom();
if (source == kUnknownSource || source == kUnknownNotFromNode) {
// fish for special first models and handle them appropriately
if (model->IsVolume()) {
// volume may use specialized icon in the volume node
entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
&resultingOpenCache, model, source, mode, size,
&lazyBitmap, entry, permanent);
if (!entry || !entry->HaveIconBitmap(mode, size))
// look for volume defined icon
entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker,
&resultingOpenCache, model, source, mode,
size, &lazyBitmap);
} else if (model->IsRoot()) {
entry = GetRootIcon(nodeCacheLocker, sharedCacheLocker,
&resultingOpenCache, model, source, mode, size, &lazyBitmap);
ASSERT(entry);
} else {
if (source == kUnknownSource)
// look for node icons first
entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
&resultingOpenCache, model, source,
mode, size, &lazyBitmap, entry, permanent);
if (!entry) {
// no node icon, look for file type based one
modelOpener.OpenNode();
// use file types to get the icon
resultingOpenCache = sharedCacheLocker;
resultingOpenCache->Lock();
entry = GetIconFromFileTypes(&modelOpener, source, mode, size,
&lazyBitmap, 0);
if (!entry) // we don't have an icon, go with the generic
entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
model, source, mode, size, &lazyBitmap, entry);
}
}
// update the icon source
model->SetIconFrom(source);
} else {
// we already know where the icon should come from,
// use shortcuts to get it
switch (source) {
case kNode:
resultingOpenCache = nodeCacheLocker;
resultingOpenCache->Lock();
entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
&resultingOpenCache, model, source, mode,
size, &lazyBitmap, entry, permanent);
if (entry) {
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 {
entry = GetWellKnownIcon(nodeCacheLocker, sharedCacheLocker,
&resultingOpenCache, model, source, mode, size,
&lazyBitmap);
if (entry)
break;
}
// fall through
case kTrackerDefault:
case kVolume:
if (model->IsVolume()) {
entry = GetNodeIcon(&modelOpener, nodeCacheLocker,
&resultingOpenCache, model, source,
mode, size, &lazyBitmap, entry, permanent);
if (!entry || !entry->HaveIconBitmap(mode, size))
entry = GetVolumeIcon(nodeCacheLocker, sharedCacheLocker,
&resultingOpenCache, model, source, mode, size,
&lazyBitmap);
break;
}
// fall through
case kMetaMime:
case kPreferredAppForType:
case kPreferredAppForNode:
resultingOpenCache = sharedCacheLocker;
resultingOpenCache->Lock();
entry = GetIconFromFileTypes(&modelOpener, source, mode, size,
&lazyBitmap, 0);
ASSERT(!entry || entry->HaveIconBitmap(mode, size));
if (!entry || !entry->HaveIconBitmap(mode, size))
// we don't have an icon, go with the generic
entry = GetGenericIcon(sharedCacheLocker, &resultingOpenCache,
model, source, mode, size, &lazyBitmap, entry);
model->SetIconFrom(source);
// the source shouldn't change in this case; if it does though we
// might never be hitting the correct icon and instead keep leaking
// entries after each miss
// this now happens if an app defines an icon but a GetIconForType
// fails and we fall back to generic icon
// ToDo:
// fix this and add an assert to the effect
ASSERT(entry);
ASSERT(entry->HaveIconBitmap(mode, size));
break;
default:
TRESPASS();
}
}
if (!entry || !entry->HaveIconBitmap(mode, size)) {
// we don't have an icon, go with the generic
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);
// we don't even have generic, something is really broken,
// go with hardcoded generic icon
if (!entry || !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);
}
// force icon pick up next time around because we probably just
// hit a node in transition
model->SetIconFrom(kUnknownSource);
}
}
ASSERT(entry && entry->HaveIconBitmap(mode, size));
if (resultingCache)
*resultingCache = resultingOpenCache;
return entry;
}
void
IconCache::Draw(Model *model, BView *view, BPoint where, IconDrawMode mode,
icon_size size, bool async)
{
// the following does not actually lock the caches, we are using the
// lockLater mode; we will decide which of the two to lock down depending
// on where we get the icon from
AutoLock<SimpleIconCache> nodeCacheLocker(&fNodeCache, false);
AutoLock<SimpleIconCache> sharedCacheLocker(&fSharedCache, false);
AutoLock<SimpleIconCache> *resultingCacheLocker;
IconCacheEntry *entry = Preload(&nodeCacheLocker, &sharedCacheLocker,
&resultingCacheLocker, model, mode, size, false);
// Preload finds/creates the appropriate entry, locking down the
// cache it is in and returns the whole state back to here
if (!entry)
return;
ASSERT(entry);
ASSERT(entry->HaveIconBitmap(mode, size));
// got the entry, now draw it
resultingCacheLocker->LockedItem()->Draw(entry, view, where, mode,
size, async);
// either of the two cache lockers that got locked down by this call get
// unlocked at this point
}
void
IconCache::SyncDraw(Model *model, BView *view, BPoint where, IconDrawMode mode,
icon_size 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)
return;
ASSERT(entry);
ASSERT(entry->HaveIconBitmap(mode, size));
resultingCacheLocker->LockedItem()->Draw(entry, view, where,
mode, size, blitFunc, passThruState);
}
void
IconCache::Preload(Model *model, IconDrawMode mode, icon_size 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, icon_size 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;
// try getting the icon from the preferred app for the signature
IconCacheEntry *entry = GetIconForPreferredApp(fileType, preferredAppSig,
mode, size, &lazyBitmap, 0);
if (entry)
return B_OK;
// try getting the icon directly from the metamime
result = mime.GetIcon(lazyBitmap.Get(), 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());
// don't care if the node uses the shared cache
}
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)
return;
AutoLock<SimpleIconCache> nodeLock(&fNodeCache);
entry = (SharedCacheEntry *)fSharedCache.ResolveIfAlias(entry);
ASSERT(entry);
int32 index = fSharedCache.EntryIndex(entry);
fNodeCache.RemoveAliasesTo(index);
fSharedCache.RemoveAliasesTo(index);
fSharedCache.IconChanged(entry);
}
BBitmap *
IconCache::MakeSelectedIcon(const BBitmap *normal, icon_size size,
LazyBitmapAllocator *lazyBitmap)
{
return MakeTransformedIcon(normal, size, fHiliteTable, lazyBitmap);
}
#if xDEBUG
static void
DumpBitmap(const BBitmap *bitmap)
{
if (!bitmap){
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::InitHiliteTable()
{
// build the color transform tables for different icon modes
BScreen screen(B_MAIN_SCREEN_ID);
rgb_color color;
for (int32 index = 0; index < kColorTransformTableSize; index++) {
color = screen.ColorForIndex((uchar)index);
fHiliteTable[index] = screen.IndexForColor(tint_color(color, 1.3f));
}
fHiliteTable[B_TRANSPARENT_8_BIT] = B_TRANSPARENT_8_BIT;
fInitHiliteTable = false;
}
BBitmap *
IconCache::MakeTransformedIcon(const BBitmap* source, icon_size /*size*/,
int32 colorTransformTable[], LazyBitmapAllocator* lazyBitmap)
{
if (fInitHiliteTable)
InitHiliteTable();
BBitmap* result = lazyBitmap->Get();
uint8* src = (uint8*)source->Bits();
uint8* dst = (uint8*)result->Bits();
// ASSERT(result->ColorSpace() == source->ColorSpace()
// && result->Bounds() == source->Bounds());
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++) {
// 66% brightness
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());
// unkown colorspace, no tinting for you
// "black" should make the problem stand out
break;
}
return result;
}
bool
IconCache::IconHitTest(BPoint where, const Model *model, IconDrawMode mode,
icon_size 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);
// Preload finds/creates the appropriate entry, locking down the
// cache it is in and returns the whole state back to here
if (entry)
return entry->IconHitTest(where, mode, size);
return false;
}
void
IconCacheEntry::RetireIcons(BObjectList<BBitmap> *retiredBitmapList)
{
if (fLargeIcon) {
retiredBitmapList->AddItem(fLargeIcon);
fLargeIcon = NULL;
}
if (fMiniIcon) {
retiredBitmapList->AddItem(fMiniIcon);
fMiniIcon = NULL;
}
if (fHilitedLargeIcon) {
retiredBitmapList->AddItem(fHilitedLargeIcon);
fHilitedLargeIcon = NULL;
}
if (fHilitedMiniIcon) {
retiredBitmapList->AddItem(fHilitedMiniIcon);
fHilitedMiniIcon = 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);
}
}
// #pragma mark -
// In debug mode keep the hash table sizes small so that they grow a lot and
// execercise the resizing code a lot. In release mode allocate them large up-front
// for better performance
SharedIconCache::SharedIconCache()
#if DEBUG
: SimpleIconCache("Shared Icon cache aka \"The Dead-Locker\""),
fHashTable(20),
fElementArray(20),
fRetiredBitmaps(20, true)
#else
: SimpleIconCache("Tracker shared icon cache"),
fHashTable(1000),
fElementArray(1024),
fRetiredBitmaps(256, true)
#endif
{
fHashTable.SetElementVector(&fElementArray);
}
void
SharedIconCache::Draw(IconCacheEntry *entry, BView *view, BPoint where,
IconDrawMode mode, icon_size size, bool async)
{
((SharedCacheEntry *)entry)->Draw(view, where, mode, size, async);
}
void
SharedIconCache::Draw(IconCacheEntry *entry, BView *view, BPoint where,
IconDrawMode mode, icon_size 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;
SharedCacheEntry *result = fHashTable.FindFirst(SharedCacheEntry::Hash(fileType,
appSignature));
if (!result)
return NULL;
for(;;) {
if (result->fFileType == fileType && result->fAppSignature == appSignature)
return result;
if (result->fNext < 0)
break;
result = const_cast<SharedCacheEntry *>(&fElementArray.At(result->fNext));
}
return NULL;
}
SharedCacheEntry *
SharedIconCache::AddItem(const char *fileType, const char *appSignature)
{
ASSERT(fileType);
if (!fileType)
fileType = B_FILE_MIMETYPE;
SharedCacheEntry *result = &fHashTable.Add(SharedCacheEntry::Hash(fileType,
appSignature));
result->SetTo(fileType, appSignature);
return result;
}
SharedCacheEntry *
SharedIconCache::AddItem(SharedCacheEntry **outstandingEntry, const char *fileType,
const char *appSignature)
{
int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
ASSERT(entryToken >= 0);
ASSERT(fileType);
if (!fileType)
fileType = B_FILE_MIMETYPE;
SharedCacheEntry *result = &fHashTable.Add(SharedCacheEntry::Hash(fileType,
appSignature));
result->SetTo(fileType, appSignature);
*outstandingEntry = fHashTable.ElementAt(entryToken);
return result;
}
void
SharedIconCache::IconChanged(SharedCacheEntry *entry)
{
// by now there should be no aliases to entry, just remove entry
// itself
ASSERT(entry->fAliasForIndex == -1);
entry->RetireIcons(&fRetiredBitmaps);
fHashTable.Remove(entry);
}
void
SharedIconCache::RemoveAliasesTo(int32 aliasIndex)
{
int32 count = fHashTable.VectorSize();
for (int32 index = 0; index < count; index++) {
SharedCacheEntry *entry = fHashTable.ElementAt(index);
if (entry->fAliasForIndex == aliasIndex)
fHashTable.Remove(entry);
}
}
void
SharedIconCache::SetAliasFor(IconCacheEntry *alias, const SharedCacheEntry *original) const
{
alias->fAliasForIndex = fHashTable.ElementIndex(original);
}
SharedCacheEntry::SharedCacheEntry()
: fNext(-1)
{
}
SharedCacheEntry::SharedCacheEntry(const char *fileType, const char *appSignature)
: fNext(-1),
fFileType(fileType),
fAppSignature(appSignature)
{
}
void
SharedCacheEntry::Draw(BView* view, BPoint where, IconDrawMode mode, icon_size size,
bool async)
{
BBitmap* bitmap = IconForMode(mode, size);
ASSERT(bitmap);
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, icon_size size,
void (*blitFunc)(BView *, BPoint ,BBitmap *, void *), void *passThruState)
{
BBitmap *bitmap = IconForMode(mode, size);
if (!bitmap)
return;
// if (bitmap->ColorSpace() == B_RGBA32) {
// view->SetDrawingMode(B_OP_ALPHA);
// view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
// } else {
// view->SetDrawingMode(B_OP_OVER);
// }
//
(blitFunc)(view, where, bitmap, passThruState);
}
uint32
SharedCacheEntry::Hash(const char *fileType, const char *appSignature)
{
uint32 hash = HashString(fileType, 0);
if (appSignature && appSignature[0])
hash = HashString(appSignature, hash);
return hash;
}
uint32
SharedCacheEntry::Hash() const
{
uint32 hash = HashString(fFileType.String(), 0);
if (fAppSignature.Length())
hash = HashString(fAppSignature.String(), hash);
return hash;
}
bool
SharedCacheEntry::operator==(const SharedCacheEntry &entry) const
{
return fFileType == entry.FileType() && fAppSignature == entry.AppSignature();
}
void
SharedCacheEntry::SetTo(const char *fileType, const char *appSignature)
{
fFileType = fileType;
fAppSignature = appSignature;
}
SharedCacheEntryArray::SharedCacheEntryArray(int32 initialSize)
: OpenHashElementArray<SharedCacheEntry>(initialSize)
{
}
SharedCacheEntry *
SharedCacheEntryArray::Add()
{
return &At(OpenHashElementArray<SharedCacheEntry>::Add());
}
// #pragma mark -
NodeCacheEntry::NodeCacheEntry(bool permanent)
: fNext(-1),
fPermanent(permanent)
{
}
NodeCacheEntry::NodeCacheEntry(const node_ref *node, bool permanent)
: fNext(-1),
fRef(*node),
fPermanent(permanent)
{
}
void
NodeCacheEntry::Draw(BView *view, BPoint where, IconDrawMode mode, icon_size size,
bool async)
{
BBitmap *bitmap = IconForMode(mode, size);
if (!bitmap)
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();
// need to copy the bits first in here
view->DrawBitmapAsync(bitmap, where);
} else
view->DrawBitmap(bitmap, where);
view->SetDrawingMode(oldMode);
}
void
NodeCacheEntry::Draw(BView *view, BPoint where, IconDrawMode mode, icon_size size,
void (*blitFunc)(BView *, BPoint ,BBitmap *, void *), void *passThruState)
{
BBitmap *bitmap = IconForMode(mode, size);
if (!bitmap)
return;
// if (bitmap->ColorSpace() == B_RGBA32) {
// view->SetDrawingMode(B_OP_ALPHA);
// view->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
// } else {
// view->SetDrawingMode(B_OP_OVER);
// }
(blitFunc)(view, where, bitmap, passThruState);
}
const node_ref *
NodeCacheEntry::Node() const
{
return &fRef;
}
uint32
NodeCacheEntry::Hash() const
{
return Hash(&fRef);
}
uint32
NodeCacheEntry::Hash(const node_ref *node)
{
return node->device ^ ((uint32 *)&node->node)[0] ^ ((uint32 *)&node->node)[1];
}
bool
NodeCacheEntry::operator==(const NodeCacheEntry &entry) const
{
return fRef == entry.fRef;
}
void
NodeCacheEntry::SetTo(const node_ref *node)
{
fRef = *node;
}
bool
NodeCacheEntry::Permanent() const
{
return fPermanent;
}
void
NodeCacheEntry::MakePermanent()
{
fPermanent = true;
}
// #pragma mark -
NodeIconCache::NodeIconCache()
#if DEBUG
: SimpleIconCache("Node Icon cache aka \"The Dead-Locker\""),
fHashTable(20),
fElementArray(20)
#else
: SimpleIconCache("Tracker node icon cache"),
fHashTable(100),
fElementArray(100)
#endif
{
fHashTable.SetElementVector(&fElementArray);
}
void
NodeIconCache::Draw(IconCacheEntry *entry, BView *view, BPoint where,
IconDrawMode mode, icon_size size, bool async)
{
((NodeCacheEntry *)entry)->Draw(view, where, mode, size, async);
}
void
NodeIconCache::Draw(IconCacheEntry *entry, BView *view, BPoint where,
IconDrawMode mode, icon_size 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
{
NodeCacheEntry *result = fHashTable.FindFirst(NodeCacheEntry::Hash(node));
if (!result)
return NULL;
for(;;) {
if (*result->Node() == *node)
return result;
if (result->fNext < 0)
break;
result = const_cast<NodeCacheEntry *>(&fElementArray.At(result->fNext));
}
return NULL;
}
NodeCacheEntry *
NodeIconCache::AddItem(const node_ref *node, bool permanent)
{
NodeCacheEntry *result = &fHashTable.Add(NodeCacheEntry::Hash(node));
result->SetTo(node);
if (permanent)
result->MakePermanent();
return result;
}
NodeCacheEntry *
NodeIconCache::AddItem(NodeCacheEntry **outstandingEntry, const node_ref *node)
{
int32 entryToken = fHashTable.ElementIndex(*outstandingEntry);
NodeCacheEntry *result = &fHashTable.Add(NodeCacheEntry::Hash(node));
result->SetTo(node);
*outstandingEntry = fHashTable.ElementAt(entryToken);
return result;
}
void
NodeIconCache::Deleting(const node_ref *node)
{
NodeCacheEntry *entry = FindItem(node);
ASSERT(entry);
if (!entry || entry->Permanent())
return;
fHashTable.Remove(entry);
}
void
NodeIconCache::Removing(const node_ref *node)
{
NodeCacheEntry *entry = FindItem(node);
ASSERT(entry);
if (!entry)
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(int32 aliasIndex)
{
int32 count = fHashTable.VectorSize();
for (int32 index = 0; index < count; index++) {
NodeCacheEntry *entry = fHashTable.ElementAt(index);
if (entry->fAliasForIndex == aliasIndex)
fHashTable.Remove(entry);
}
}
// #pragma mark -
NodeCacheEntryArray::NodeCacheEntryArray(int32 initialSize)
: OpenHashElementArray<NodeCacheEntry>(initialSize)
{
}
NodeCacheEntry *
NodeCacheEntryArray::Add()
{
return &At(OpenHashElementArray<NodeCacheEntry>::Add());
}
// #pragma mark -
SimpleIconCache::SimpleIconCache(const char *name)
: fLock(name)
{
}
void
SimpleIconCache::Draw(IconCacheEntry *, BView *, BPoint, IconDrawMode ,
icon_size , bool )
{
TRESPASS();
// pure virtual, do nothing
}
void
SimpleIconCache::Draw(IconCacheEntry *, BView *, BPoint, IconDrawMode, icon_size,
void(*)(BView *, BPoint, BBitmap *, void *), void *)
{
TRESPASS();
// pure virtual, do nothing
}
bool
SimpleIconCache::Lock()
{
return fLock.Lock();
}
void
SimpleIconCache::Unlock()
{
fLock.Unlock();
}
bool
SimpleIconCache::IsLocked() const
{
return fLock.IsLocked();
}
// #pragma mark -
LazyBitmapAllocator::LazyBitmapAllocator(icon_size size, color_space colorSpace,
bool preallocate)
: fBitmap(NULL),
fSize(size),
fColorSpace(colorSpace)
{
if (preallocate)
Get();
}
LazyBitmapAllocator::~LazyBitmapAllocator()
{
delete fBitmap;
}
BBitmap *
LazyBitmapAllocator::Get()
{
if (!fBitmap)
fBitmap = new BBitmap(BRect(0, 0, fSize - 1, fSize - 1), fColorSpace);
return fBitmap;
}
BBitmap *
LazyBitmapAllocator::Adopt()
{
if (!fBitmap)
Get();
BBitmap *result = fBitmap;
fBitmap = NULL;
return result;
}
IconCache *IconCache::sIconCache;