mirror of
https://review.haiku-os.org/haiku
synced 2024-11-23 07:18:40 +01:00
Tracker: Add thumbnail support
Define thumbnail attributes in Attributes.h: Media:Thumbnail to store the thumbnail, Media:Thumbnail:CreationTime to see if thumbs need to be regenerated. Store 128x128 thumbnail in attribute, for icon sizes smaller than 128x128 down-scale the 128x128 thumbnail. Use B_FILTER_BITMAP_BILINEAR to down-scale the image using the bilinear scaling algorithm which creates nicer looking thumbnails than the default scaling algorithm. Store thumbnails as WebP images which compress smaller than PNGs and fit in the inode better at 128x128. Check the file's modification time in GetFileIconFromAttr() and compare it to the thumbnail creation time. If the file has not been modified since the last time we generated thumbnails return the thumbnail from the attribute, otherwise fetch a new thumbnail with GetThumbnailIcon(). Add "Generate image thumbnails" Tracker setting. Default is turned off for now. To generate image thumbnails you must first turn this setting on in Tracker Windows preferences. Spawn a get_thumbnail() thread to generate thumbnails and retrieve them later on from the window thread to fill out into the icon. This should improve responsiveness of generating thumbnails from a folder with a lot of images. The generator thread will write the thumbnail data to an attribute if on writable BFS volume. If not on writable BFS volume, the generator thread will send the data back to the original thread through a port by calling write_port(). When the thread is finished creating the thumbnail it sends a message back to the Tracker application thread to update the pose which instructs the window thread to look for an thumbnail. It either finds a thumbnail in an attribute, or picks up the thumbnail data that has been sent through write_port() using read_port(). This works on both read-write and read-only BFS volumes but it still depends on the presence of a BEOS:TYPE parameter to have been written to the volume before it became read-only. Thumbnail generation does not work on other read-only volumes for example an ISO-9660 CD, but it does work on read-only BFS volumes for example the BeOS R5 CD. Move BPrivate::CheckNodeIconHintPrivate() from BNodeInfo to Tracker Model CheckNodeIconHint(). Create Model::CheckAppIconHint() and look for a vector icon or mini and large icon in that method. Check that the base type is directory, volume, trash, desktop, or if executable call CheckAppIconHint(). Add 1 to temp_name to fix the following warning: src/kits/tracker/FSUtils.cpp:2437:12: note: 'snprintf' output 3 or more bytes (assuming 267) into a destination of size 266 Rename temp_name to tempName following our style guidelines. Use strlcpy() and strlcat() instead of strcpy() to safely copy the string. This fixes thumbnail generation on 64-bit Haiku. Change-Id: I7f927a5a1f8cf65e4b1aa1e0eb55bbfae87fd969 Reviewed-on: https://review.haiku-os.org/c/haiku/+/3163 Reviewed-by: John Scipione <jscipione@gmail.com> Reviewed-by: Adrien Destugues <pulkomandy@gmail.com> Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
parent
57a743987c
commit
7f8195344a
@ -48,6 +48,7 @@ enum {
|
||||
B_TGA_FORMAT = 'TGA ',
|
||||
B_BMP_FORMAT = 'BMP ',
|
||||
B_TIFF_FORMAT = 'TIFF',
|
||||
B_WEBP_FORMAT = 'WebP',
|
||||
|
||||
// Picture formats
|
||||
B_DXF_FORMAT = 'DXF ',
|
||||
|
@ -766,19 +766,3 @@ BNodeInfo::operator=(const BNodeInfo &nodeInfo)
|
||||
BNodeInfo::BNodeInfo(const BNodeInfo &)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
extern bool
|
||||
CheckNodeIconHintPrivate(const BNode *node, bool whatever)
|
||||
{
|
||||
// I've no idea what this is supposed to do exactly, but
|
||||
// it seems to tell Tracker if there is an icon for the
|
||||
// node. See kits/tracker/Model.cpp for details
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace BPrivate
|
||||
|
@ -67,6 +67,9 @@ namespace BPrivate {
|
||||
#define kAttrMiniIcon "BEOS:M:STD_ICON"
|
||||
#define kAttrIcon "BEOS:ICON"
|
||||
|
||||
#define kAttrThumbnail "Media:Thumbnail"
|
||||
#define kAttrThumbnailCreationTime "Media:Thumbnail:CreationTime"
|
||||
|
||||
#define kAttrDisksFrame "_trk/d_windframe"
|
||||
#define kAttrDisksWorkspace "_trk/d_windwkspc"
|
||||
|
||||
@ -114,6 +117,7 @@ namespace BPrivate {
|
||||
#define kAttrExtendedDisksPoseInfo_le "_trk/xt_d_pinfo_le"
|
||||
|
||||
#if B_HOST_IS_LENDIAN
|
||||
|
||||
#define kEndianSuffix "_le"
|
||||
#define kForeignEndianSuffix ""
|
||||
|
||||
@ -149,6 +153,7 @@ namespace BPrivate {
|
||||
#define kAttrExtendedDisksPoseInfoForegin kAttrExtendedDisksPoseInfo_be
|
||||
|
||||
#else
|
||||
|
||||
#define kEndianSuffix ""
|
||||
#define kForeignEndianSuffix "_le"
|
||||
|
||||
|
@ -114,6 +114,9 @@ const uint32 kSwitchToHome = 'Tswh';
|
||||
|
||||
const uint32 kTestIconCache = 'TicC';
|
||||
|
||||
// thumbnail generator thread sends this message to be_app when done
|
||||
const uint32 kUpdateThumbnail = 'TUTb';
|
||||
|
||||
// Observers and Notifiers:
|
||||
|
||||
// Settings-changed messages:
|
||||
@ -131,6 +134,7 @@ const uint32 kTransparentSelectionChanged = 'Trse';
|
||||
const uint32 kSortFolderNamesFirstChanged = 'Sfnf';
|
||||
const uint32 kHideDotFilesChanged = 'Hdfc';
|
||||
const uint32 kTypeAheadFilteringChanged = 'Tafc';
|
||||
const uint32 kGenerateImageThumbnailsChanged = 'GITc';
|
||||
|
||||
const uint32 kDesktopFilePanelRootChanged = 'Dfpr';
|
||||
const uint32 kFavoriteCountChanged = 'Fvct';
|
||||
|
@ -1043,6 +1043,7 @@ BContainerWindow::Init(const BMessage* message)
|
||||
SetFlags(Flags() & ~B_NO_WORKSPACE_ACTIVATION);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BContainerWindow::InitLayout()
|
||||
{
|
||||
@ -1081,6 +1082,7 @@ BContainerWindow::InitLayout()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BContainerWindow::RestoreState()
|
||||
{
|
||||
|
@ -2360,7 +2360,7 @@ FSMakeOriginalName(char* name, BDirectory* destDir, const char* suffix)
|
||||
{
|
||||
char root[B_FILE_NAME_LENGTH];
|
||||
char copybase[B_FILE_NAME_LENGTH];
|
||||
char temp_name[B_FILE_NAME_LENGTH + 10];
|
||||
char tempName[B_FILE_NAME_LENGTH + 11];
|
||||
int32 fnum;
|
||||
|
||||
// is this name already original?
|
||||
@ -2414,31 +2414,34 @@ FSMakeOriginalName(char* name, BDirectory* destDir, const char* suffix)
|
||||
name[B_FILE_NAME_LENGTH - 8] = '\0';
|
||||
}
|
||||
|
||||
strcpy(root, name); // save root name
|
||||
strcat(name, suffix);
|
||||
strlcpy(root, name, sizeof(root));
|
||||
// save root name
|
||||
strlcat(name, suffix, sizeof(name));
|
||||
}
|
||||
|
||||
strcpy(copybase, name);
|
||||
strlcpy(copybase, name, sizeof(copybase));
|
||||
|
||||
// if name already exists then add a number
|
||||
fnum = 1;
|
||||
strcpy(temp_name, name);
|
||||
while (destDir->Contains(temp_name)) {
|
||||
snprintf(temp_name, sizeof(temp_name), "%s %" B_PRId32, copybase, ++fnum);
|
||||
strlcpy(tempName, name, sizeof(tempName));
|
||||
while (destDir->Contains(tempName)) {
|
||||
snprintf(tempName, sizeof(tempName), "%s %" B_PRId32, copybase,
|
||||
++fnum);
|
||||
|
||||
if (strlen(temp_name) > (B_FILE_NAME_LENGTH - 1)) {
|
||||
if (strlen(tempName) > (B_FILE_NAME_LENGTH - 1)) {
|
||||
// The name has grown too long. Maybe we just went from
|
||||
// "<filename> copy 9" to "<filename> copy 10" and that extra
|
||||
// character was too much. The solution is to further
|
||||
// truncate the 'root' name and continue.
|
||||
// ??? should we reset fnum or not ???
|
||||
root[strlen(root) - 1] = '\0';
|
||||
snprintf(temp_name, sizeof(temp_name), "%s%s %" B_PRId32, root, suffix, fnum);
|
||||
snprintf(tempName, sizeof(tempName), "%s%s %" B_PRId32, root,
|
||||
suffix, fnum);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT((strlen(temp_name) <= (B_FILE_NAME_LENGTH - 1)));
|
||||
strcpy(name, temp_name);
|
||||
ASSERT((strlen(tempName) <= (B_FILE_NAME_LENGTH - 1)));
|
||||
strlcpy(name, tempName, sizeof(name));
|
||||
}
|
||||
|
||||
|
||||
@ -3132,8 +3135,7 @@ FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef,
|
||||
int32 fnum = 1;
|
||||
while (dir.Contains(name)) {
|
||||
// if base name already exists then add a number
|
||||
// ToDo:
|
||||
// move this logic ot FSMakeOriginalName
|
||||
// TODO: move this logic to FSMakeOriginalName
|
||||
if (++fnum > 9) {
|
||||
snprintf(name, sizeof(name), B_TRANSLATE("New folder%ld"),
|
||||
fnum);
|
||||
@ -3165,6 +3167,7 @@ FSCreateNewFolderIn(const node_ref* dirNode, entry_ref* newRef,
|
||||
B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
||||
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
|
||||
alert->Go();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -3279,6 +3282,7 @@ FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node)
|
||||
status_t error = FSGetParentVirtualDirectoryAware(entry, ref);
|
||||
if (error == B_OK)
|
||||
error = _node.SetTo(&ref);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -3410,6 +3414,7 @@ _TrackerLaunchAppWithDocuments(const entry_ref* appRef, const BMessage* refs,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" char** environ;
|
||||
|
||||
|
||||
|
@ -773,20 +773,20 @@ IconCache::GetNodeIcon(ModelNodeLazyOpener* modelOpener,
|
||||
if (entry == NULL || !entry->HaveIconBitmap(NORMAL_ICON_ONLY, size)) {
|
||||
modelOpener->OpenNode();
|
||||
|
||||
BFile* file = NULL;
|
||||
PRINT_DISK_HITS(("File %s; Line %d # hitting disk for node %s\n",
|
||||
__FILE__, __LINE__, model->Name()));
|
||||
|
||||
// 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 = file != NULL
|
||||
? GetAppIconFromAttr(file, lazyBitmap->Get(), size)
|
||||
: GetFileIconFromAttr(model->Node(), lazyBitmap->Get(), size);
|
||||
BFile* file = NULL;
|
||||
status_t result = B_ERROR;
|
||||
if (model->IsExecutable()
|
||||
&& (file = dynamic_cast<BFile*>(model->Node())) != NULL) {
|
||||
result = GetAppIconFromAttr(file, lazyBitmap->Get(), size);
|
||||
} else
|
||||
result = GetFileIconFromAttr(model, lazyBitmap->Get(), size);
|
||||
|
||||
if (result == B_OK) {
|
||||
// node has its own icon, use it
|
||||
@ -801,6 +801,10 @@ IconCache::GetNodeIcon(ModelNodeLazyOpener* modelOpener,
|
||||
entry->SetIcon(lazyBitmap->Adopt(), mode, size);
|
||||
}
|
||||
source = kNode;
|
||||
} else if (result == B_BUSY) {
|
||||
// still waiting for thumbnail icon to be generated,
|
||||
// provide a hint to come back here for it
|
||||
source = kNode;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,25 +82,6 @@ BObjectList<Model>* readOnlyOpenModelList = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
static bool
|
||||
CheckNodeIconHint(BNode* node)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
|
||||
attr_info info;
|
||||
if (node->GetAttrInfo(kAttrIcon, &info) == B_OK
|
||||
// has a vector icon, or
|
||||
|| (node->GetAttrInfo(kAttrMiniIcon, &info) == B_OK
|
||||
&& node->GetAttrInfo(kAttrLargeIcon, &info) == B_OK)) {
|
||||
// has a mini _and_ large icon
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Model()
|
||||
|
||||
|
||||
@ -611,14 +592,14 @@ Model::CacheLocalizedName()
|
||||
void
|
||||
Model::FinishSettingUpType()
|
||||
{
|
||||
char mimeString[B_MIME_TYPE_LENGTH];
|
||||
char type[B_MIME_TYPE_LENGTH];
|
||||
BEntry entry;
|
||||
|
||||
// While we are reading the node, do a little snooping to see if it even
|
||||
// makes sense to look for a node-based icon. This serves as a hint to the
|
||||
// icon cache, allowing it to not hit the disk again for models that do not
|
||||
// have an icon defined by the node.
|
||||
if (IsNodeOpen() && fBaseType != kLinkNode && !CheckNodeIconHint(fNode))
|
||||
if (CheckNodeIconHint())
|
||||
fIconFrom = kUnknownNotFromNode;
|
||||
|
||||
if (fBaseType != kDirectoryNode
|
||||
@ -628,22 +609,22 @@ Model::FinishSettingUpType()
|
||||
BNodeInfo info(fNode);
|
||||
|
||||
// check if a specific mime type is set
|
||||
if (info.GetType(mimeString) == B_OK) {
|
||||
if (info.GetType(type) == B_OK) {
|
||||
// node has a specific mime type
|
||||
fMimeType = mimeString;
|
||||
if (strcmp(mimeString, B_QUERY_MIMETYPE) == 0)
|
||||
fMimeType = type;
|
||||
if (strcmp(type, B_QUERY_MIMETYPE) == 0)
|
||||
fBaseType = kQueryNode;
|
||||
else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0)
|
||||
else if (strcmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0)
|
||||
fBaseType = kQueryTemplateNode;
|
||||
else if (strcmp(mimeString, kVirtualDirectoryMimeType) == 0)
|
||||
else if (strcmp(type, kVirtualDirectoryMimeType) == 0)
|
||||
fBaseType = kVirtualDirectoryNode;
|
||||
|
||||
if (info.GetPreferredApp(mimeString) == B_OK) {
|
||||
if (info.GetPreferredApp(type) == B_OK) {
|
||||
if (fPreferredAppName)
|
||||
DeletePreferredAppVolumeNameLinkTo();
|
||||
|
||||
if (mimeString[0])
|
||||
fPreferredAppName = strdup(mimeString);
|
||||
if (*type != '0')
|
||||
fPreferredAppName = strdup(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -662,8 +643,8 @@ Model::FinishSettingUpType()
|
||||
// should use a shared string here
|
||||
if (IsNodeOpen()) {
|
||||
BNodeInfo info(fNode);
|
||||
if (info.GetType(mimeString) == B_OK)
|
||||
fMimeType = mimeString;
|
||||
if (info.GetType(type) == B_OK)
|
||||
fMimeType = type;
|
||||
|
||||
if (fIconFrom == kUnknownNotFromNode
|
||||
&& WellKnowEntryList::Match(NodeRef())
|
||||
@ -738,6 +719,29 @@ Model::FinishSettingUpType()
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Model::CheckNodeIconHint() const
|
||||
{
|
||||
return (fBaseType == kDirectoryNode || fBaseType == kVolumeNode
|
||||
|| fBaseType == kTrashNode || fBaseType == kDesktopNode)
|
||||
|| (fBaseType == kExecutableNode && !CheckAppIconHint());
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Model::CheckAppIconHint() const
|
||||
{
|
||||
attr_info info;
|
||||
return fNode != NULL
|
||||
// node is open, and it
|
||||
&& (fNode->GetAttrInfo(kAttrIcon, &info) == B_OK
|
||||
// has a vector icon, or
|
||||
|| (fNode->GetAttrInfo(kAttrMiniIcon, &info) == B_OK
|
||||
&& fNode->GetAttrInfo(kAttrLargeIcon, &info) == B_OK));
|
||||
// has a mini _and_ large icon
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Model::ResetIconFrom()
|
||||
{
|
||||
@ -746,10 +750,7 @@ Model::ResetIconFrom()
|
||||
if (InitCheck() != B_OK)
|
||||
return;
|
||||
|
||||
// mirror the logic from FinishSettingUpType
|
||||
if ((fBaseType == kDirectoryNode || fBaseType == kVolumeNode
|
||||
|| fBaseType == kTrashNode || fBaseType == kDesktopNode)
|
||||
&& !CheckNodeIconHint(fNode)) {
|
||||
if (CheckNodeIconHint()) {
|
||||
BDirectory* directory = dynamic_cast<BDirectory*>(fNode);
|
||||
if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) {
|
||||
fIconFrom = kTrackerSupplied;
|
||||
@ -888,16 +889,16 @@ Model::AttrChanged(const char* attrName)
|
||||
if (attrName == NULL
|
||||
|| strcmp(attrName, kAttrMIMEType) == 0
|
||||
|| strcmp(attrName, kAttrPreferredApp) == 0) {
|
||||
char mimeString[B_MIME_TYPE_LENGTH];
|
||||
char type[B_MIME_TYPE_LENGTH];
|
||||
BNodeInfo info(fNode);
|
||||
if (info.GetType(mimeString) != B_OK)
|
||||
if (info.GetType(type) != B_OK)
|
||||
fMimeType = "";
|
||||
else {
|
||||
// node has a specific mime type
|
||||
fMimeType = mimeString;
|
||||
fMimeType = type;
|
||||
if (!IsVolume() && !IsSymLink()
|
||||
&& info.GetPreferredApp(mimeString) == B_OK) {
|
||||
SetPreferredAppSignature(mimeString);
|
||||
&& info.GetPreferredApp(type) == B_OK) {
|
||||
SetPreferredAppSignature(type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1258,6 +1259,7 @@ Model::GetLongVersionString(BString &result, version_kind kind)
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Model::GetVersionString(BString &result, version_kind kind)
|
||||
{
|
||||
|
@ -212,6 +212,8 @@ private:
|
||||
status_t OpenNodeCommon(bool writable);
|
||||
void SetupBaseType();
|
||||
void FinishSettingUpType();
|
||||
bool CheckNodeIconHint() const;
|
||||
bool CheckAppIconHint() const;
|
||||
void DeletePreferredAppVolumeNameLinkTo();
|
||||
void CacheLocalizedName();
|
||||
|
||||
|
@ -420,6 +420,7 @@ WindowsSettingsView::WindowsSettingsView()
|
||||
fSortFolderNamesFirstCheckBox(NULL),
|
||||
fHideDotFilesCheckBox(NULL),
|
||||
fTypeAheadFilteringCheckBox(NULL),
|
||||
fGenerateImageThumbnailsCheckBox(NULL),
|
||||
fShowFullPathInTitleBar(false),
|
||||
fSingleWindowBrowse(false),
|
||||
fShowNavigator(false),
|
||||
@ -456,6 +457,10 @@ WindowsSettingsView::WindowsSettingsView()
|
||||
B_TRANSLATE("Enable type-ahead filtering"),
|
||||
new BMessage(kTypeAheadFilteringChanged));
|
||||
|
||||
fGenerateImageThumbnailsCheckBox = new BCheckBox("",
|
||||
B_TRANSLATE("Generate image thumbnails"),
|
||||
new BMessage(kGenerateImageThumbnailsChanged));
|
||||
|
||||
const float spacing = be_control_look->DefaultItemSpacing();
|
||||
|
||||
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
|
||||
@ -472,6 +477,7 @@ WindowsSettingsView::WindowsSettingsView()
|
||||
.Add(fSortFolderNamesFirstCheckBox)
|
||||
.Add(fHideDotFilesCheckBox)
|
||||
.Add(fTypeAheadFilteringCheckBox)
|
||||
.Add(fGenerateImageThumbnailsCheckBox)
|
||||
.End()
|
||||
.AddGlue()
|
||||
.SetInsets(spacing);
|
||||
@ -488,6 +494,7 @@ WindowsSettingsView::AttachedToWindow()
|
||||
fSortFolderNamesFirstCheckBox->SetTarget(this);
|
||||
fHideDotFilesCheckBox->SetTarget(this);
|
||||
fTypeAheadFilteringCheckBox->SetTarget(this);
|
||||
fGenerateImageThumbnailsCheckBox->SetTarget(this);
|
||||
}
|
||||
|
||||
|
||||
@ -582,6 +589,17 @@ WindowsSettingsView::MessageReceived(BMessage* message)
|
||||
break;
|
||||
}
|
||||
|
||||
case kGenerateImageThumbnailsChanged:
|
||||
{
|
||||
settings.SetGenerateImageThumbnails(
|
||||
fGenerateImageThumbnailsCheckBox->Value() == 1);
|
||||
send_bool_notices(kGenerateImageThumbnailsChanged,
|
||||
"GenerateImageThumbnails",
|
||||
fGenerateImageThumbnailsCheckBox->Value() == 1);
|
||||
Window()->PostMessage(kSettingsContentsModified);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
_inherited::MessageReceived(message);
|
||||
break;
|
||||
@ -637,6 +655,12 @@ WindowsSettingsView::SetDefaults()
|
||||
"TypeAheadFiltering", true);
|
||||
}
|
||||
|
||||
if (settings.GenerateImageThumbnails()) {
|
||||
settings.SetGenerateImageThumbnails(false);
|
||||
send_bool_notices(kGenerateImageThumbnailsChanged,
|
||||
"GenerateImageThumbnails", true);
|
||||
}
|
||||
|
||||
ShowCurrentSettings();
|
||||
}
|
||||
|
||||
@ -651,7 +675,8 @@ WindowsSettingsView::IsDefaultable() const
|
||||
|| settings.ShowNavigator() != false
|
||||
|| settings.TransparentSelection() != true
|
||||
|| settings.SortFolderNamesFirst() != true
|
||||
|| settings.TypeAheadFiltering() != false;
|
||||
|| settings.TypeAheadFiltering() != false
|
||||
|| settings.GenerateImageThumbnails() != false;
|
||||
}
|
||||
|
||||
|
||||
@ -703,6 +728,12 @@ WindowsSettingsView::Revert()
|
||||
"TypeAheadFiltering", fTypeAheadFiltering);
|
||||
}
|
||||
|
||||
if (settings.GenerateImageThumbnails() != fGenerateImageThumbnails) {
|
||||
settings.SetGenerateImageThumbnails(fGenerateImageThumbnails);
|
||||
send_bool_notices(kGenerateImageThumbnailsChanged,
|
||||
"GenerateImageThumbnails", fGenerateImageThumbnails);
|
||||
}
|
||||
|
||||
ShowCurrentSettings();
|
||||
}
|
||||
|
||||
@ -722,6 +753,8 @@ WindowsSettingsView::ShowCurrentSettings()
|
||||
fSortFolderNamesFirstCheckBox->SetValue(settings.SortFolderNamesFirst());
|
||||
fHideDotFilesCheckBox->SetValue(settings.HideDotFiles());
|
||||
fTypeAheadFilteringCheckBox->SetValue(settings.TypeAheadFiltering());
|
||||
fGenerateImageThumbnailsCheckBox->SetValue(
|
||||
settings.GenerateImageThumbnails());
|
||||
}
|
||||
|
||||
|
||||
@ -737,6 +770,7 @@ WindowsSettingsView::RecordRevertSettings()
|
||||
fSortFolderNamesFirst = settings.SortFolderNamesFirst();
|
||||
fHideDotFiles = settings.HideDotFiles();
|
||||
fTypeAheadFiltering = settings.TypeAheadFiltering();
|
||||
fGenerateImageThumbnails = settings.GenerateImageThumbnails();
|
||||
}
|
||||
|
||||
|
||||
@ -751,7 +785,8 @@ WindowsSettingsView::IsRevertable() const
|
||||
|| fTransparentSelection != settings.TransparentSelection()
|
||||
|| fSortFolderNamesFirst != settings.SortFolderNamesFirst()
|
||||
|| fHideDotFiles != settings.HideDotFiles()
|
||||
|| fTypeAheadFiltering != settings.TypeAheadFiltering();
|
||||
|| fTypeAheadFiltering != settings.TypeAheadFiltering()
|
||||
|| fGenerateImageThumbnails != settings.GenerateImageThumbnails();
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,6 +123,7 @@ private:
|
||||
BCheckBox* fSortFolderNamesFirstCheckBox;
|
||||
BCheckBox* fHideDotFilesCheckBox;
|
||||
BCheckBox* fTypeAheadFilteringCheckBox;
|
||||
BCheckBox* fGenerateImageThumbnailsCheckBox;
|
||||
|
||||
bool fShowFullPathInTitleBar;
|
||||
bool fSingleWindowBrowse;
|
||||
@ -131,6 +132,7 @@ private:
|
||||
bool fSortFolderNamesFirst;
|
||||
bool fHideDotFiles;
|
||||
bool fTypeAheadFiltering;
|
||||
bool fGenerateImageThumbnails;
|
||||
|
||||
typedef SettingsView _inherited;
|
||||
};
|
||||
|
@ -617,6 +617,45 @@ TTracker::MessageReceived(BMessage* message)
|
||||
break;
|
||||
}
|
||||
|
||||
case kUpdateThumbnail:
|
||||
{
|
||||
// message passed from generator thread
|
||||
// update icon on passed-in node_ref
|
||||
dev_t device;
|
||||
ino_t inode;
|
||||
// call this inode so we can call the node_ref node
|
||||
if (message->FindInt32("device", (int32*)&device) == B_OK
|
||||
&& message->FindUInt64("node", (uint64*)&inode) == B_OK) {
|
||||
const node_ref node = node_ref(device, inode);
|
||||
|
||||
// cycle through open windows to find the node's pose
|
||||
// TODO find a faster way
|
||||
AutoLock<WindowList> lock(&fWindowList);
|
||||
int32 count = fWindowList.CountItems();
|
||||
for (int32 index = 0; index < count; index++) {
|
||||
BContainerWindow* window = dynamic_cast<BContainerWindow*>(
|
||||
fWindowList.ItemAt(index));
|
||||
if (window == NULL)
|
||||
continue;
|
||||
|
||||
AutoLock<BWindow> windowLock(window);
|
||||
if (!windowLock.IsLocked())
|
||||
continue;
|
||||
|
||||
BPoseView* poseView = window->PoseView();
|
||||
if (poseView == NULL)
|
||||
continue;
|
||||
|
||||
BPose* pose = poseView->FindPose(&node);
|
||||
if (pose != NULL) {
|
||||
poseView->UpdateIcon(pose);
|
||||
break; // updated pose icon, exit loop
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
_inherited::MessageReceived(message);
|
||||
break;
|
||||
|
@ -72,6 +72,7 @@ private:
|
||||
BooleanValueSetting* fSortFolderNamesFirst;
|
||||
BooleanValueSetting* fHideDotFiles;
|
||||
BooleanValueSetting* fTypeAheadFiltering;
|
||||
BooleanValueSetting* fGenerateImageThumbnails;
|
||||
|
||||
ScalarValueSetting* fRecentApplicationsCount;
|
||||
ScalarValueSetting* fRecentDocumentsCount;
|
||||
@ -136,6 +137,7 @@ TTrackerState::TTrackerState()
|
||||
fSortFolderNamesFirst(NULL),
|
||||
fHideDotFiles(NULL),
|
||||
fTypeAheadFiltering(NULL),
|
||||
fGenerateImageThumbnails(NULL),
|
||||
fRecentApplicationsCount(NULL),
|
||||
fRecentDocumentsCount(NULL),
|
||||
fRecentFoldersCount(NULL),
|
||||
@ -167,6 +169,7 @@ TTrackerState::TTrackerState(const TTrackerState&)
|
||||
fSortFolderNamesFirst(NULL),
|
||||
fHideDotFiles(NULL),
|
||||
fTypeAheadFiltering(NULL),
|
||||
fGenerateImageThumbnails(NULL),
|
||||
fRecentApplicationsCount(NULL),
|
||||
fRecentDocumentsCount(NULL),
|
||||
fRecentFoldersCount(NULL),
|
||||
@ -228,6 +231,8 @@ TTrackerState::LoadSettingsIfNeeded()
|
||||
Add(fHideDotFiles = new BooleanValueSetting("HideDotFiles", false));
|
||||
Add(fTypeAheadFiltering
|
||||
= new BooleanValueSetting("TypeAheadFiltering", false));
|
||||
Add(fGenerateImageThumbnails
|
||||
= new BooleanValueSetting("GenerateImageThumbnails", false));
|
||||
Add(fSingleWindowBrowse
|
||||
= new BooleanValueSetting("SingleWindowBrowse", false));
|
||||
Add(fShowNavigator = new BooleanValueSetting("ShowNavigator", false));
|
||||
@ -465,6 +470,20 @@ TrackerSettings::SetTypeAheadFiltering(bool enabled)
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TrackerSettings::GenerateImageThumbnails()
|
||||
{
|
||||
return gTrackerState.fGenerateImageThumbnails->Value();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TrackerSettings::SetGenerateImageThumbnails(bool enabled)
|
||||
{
|
||||
gTrackerState.fGenerateImageThumbnails->SetValue(enabled);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
TrackerSettings::ShowSelectionWhenInactive()
|
||||
{
|
||||
|
@ -94,6 +94,8 @@ public:
|
||||
void SetHideDotFiles(bool hide);
|
||||
bool TypeAheadFiltering();
|
||||
void SetTypeAheadFiltering(bool enabled);
|
||||
bool GenerateImageThumbnails();
|
||||
void SetGenerateImageThumbnails(bool enabled);
|
||||
|
||||
bool ShowSelectionWhenInactive();
|
||||
void SetShowSelectionWhenInactive(bool);
|
||||
|
@ -49,22 +49,37 @@ All rights reserved.
|
||||
#include <Font.h>
|
||||
#include <IconUtils.h>
|
||||
#include <MenuItem.h>
|
||||
#include <Mime.h>
|
||||
#include <Node.h>
|
||||
#include <NodeInfo.h>
|
||||
#include <OS.h>
|
||||
#include <PopUpMenu.h>
|
||||
#include <Region.h>
|
||||
#include <StorageDefs.h>
|
||||
#include <TextView.h>
|
||||
#include <TranslatorFormats.h>
|
||||
#include <TranslatorRoster.h>
|
||||
#include <TranslationUtils.h>
|
||||
#include <TypeConstants.h>
|
||||
#include <View.h>
|
||||
#include <Volume.h>
|
||||
#include <VolumeRoster.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "Attributes.h"
|
||||
#include "Commands.h"
|
||||
#include "ContainerWindow.h"
|
||||
#include "FSUtils.h"
|
||||
#include "MimeTypes.h"
|
||||
#include "Model.h"
|
||||
#include "PoseView.h"
|
||||
|
||||
|
||||
#ifdef B_XXL_ICON
|
||||
# undef B_XXL_ICON
|
||||
#endif
|
||||
#define B_XXL_ICON 128
|
||||
|
||||
#ifndef _IMPEXP_BE
|
||||
# define _IMPEXP_BE
|
||||
#endif
|
||||
@ -421,7 +436,7 @@ OffscreenBitmap::NewBitmap(BRect bounds)
|
||||
{
|
||||
delete fBitmap;
|
||||
fBitmap = new(std::nothrow) BBitmap(bounds, B_RGB32, true);
|
||||
if (fBitmap && fBitmap->Lock()) {
|
||||
if (fBitmap != NULL && fBitmap->Lock()) {
|
||||
BView* view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
|
||||
fBitmap->AddChild(view);
|
||||
|
||||
@ -555,7 +570,7 @@ FadeRGBA32Vertical(uint32* bits, int32 width, int32 height, int32 from,
|
||||
|
||||
|
||||
DraggableIcon::DraggableIcon(BRect rect, const char* name,
|
||||
const char* mimeType, icon_size which, const BMessage* message,
|
||||
const char* type, icon_size which, const BMessage* message,
|
||||
BMessenger target, uint32 resizingMode, uint32 flags)
|
||||
:
|
||||
BView(rect, name, resizingMode, flags),
|
||||
@ -563,11 +578,11 @@ DraggableIcon::DraggableIcon(BRect rect, const char* name,
|
||||
fTarget(target)
|
||||
{
|
||||
fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
|
||||
BMimeType mime(mimeType);
|
||||
BMimeType mime(type);
|
||||
status_t result = mime.GetIcon(fBitmap, which);
|
||||
ASSERT(mime.IsValid());
|
||||
if (result != B_OK) {
|
||||
PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(result)));
|
||||
PRINT(("failed to get icon for %s, %s\n", type, strerror(result)));
|
||||
BMimeType mime(B_FILE_MIMETYPE);
|
||||
ASSERT(mime.IsInstalled());
|
||||
mime.GetIcon(fBitmap, which);
|
||||
@ -1485,13 +1500,399 @@ GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which)
|
||||
|
||||
|
||||
status_t
|
||||
GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which)
|
||||
GetFileIconFromAttr(Model* model, BBitmap* icon, icon_size which)
|
||||
{
|
||||
BNodeInfo fileInfo(node);
|
||||
return fileInfo.GetIcon(icon, which);
|
||||
// bad input value
|
||||
if (model == NULL || icon == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// unitialized model
|
||||
status_t result = model->InitCheck();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
// unitialized icon
|
||||
result = icon->InitCheck();
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
// node not open
|
||||
BNode* node = model->Node();
|
||||
if (node == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// look for a thumbnail in an attribute
|
||||
time_t modtime;
|
||||
bigtime_t created;
|
||||
if (node->GetModificationTime(&modtime) == B_OK
|
||||
&& node->ReadAttr(kAttrThumbnailCreationTime, B_TIME_TYPE, 0,
|
||||
&created, sizeof(bigtime_t)) == sizeof(bigtime_t)) {
|
||||
if (created > (bigtime_t)modtime) {
|
||||
// file has not changed, try to return an existing thumbnail
|
||||
attr_info attrInfo;
|
||||
if (node->GetAttrInfo(kAttrThumbnail, &attrInfo) == B_OK) {
|
||||
uint8 webpData[attrInfo.size];
|
||||
if (node->ReadAttr(kAttrThumbnail, attrInfo.type, 0,
|
||||
webpData, attrInfo.size) == attrInfo.size) {
|
||||
BMemoryIO stream((const void*)webpData, attrInfo.size);
|
||||
BBitmap thumb(BTranslationUtils::GetBitmap(&stream));
|
||||
|
||||
// convert thumb to icon size
|
||||
if (which == B_XXL_ICON) {
|
||||
// import icon data from attribute without resizing
|
||||
result = icon->ImportBits(&thumb);
|
||||
} else {
|
||||
// down-scale thumb to icon size
|
||||
// TODO don't make a copy, allow icon to accept views
|
||||
BBitmap tmp = BBitmap(icon->Bounds(),
|
||||
icon->ColorSpace(), true);
|
||||
BView view(tmp.Bounds(), "", B_FOLLOW_NONE,
|
||||
B_WILL_DRAW);
|
||||
tmp.AddChild(&view);
|
||||
if (view.LockLooper()) {
|
||||
// fill with transparent
|
||||
view.SetLowColor(B_TRANSPARENT_COLOR);
|
||||
view.FillRect(view.Bounds(), B_SOLID_LOW);
|
||||
// draw bitmap
|
||||
view.SetDrawingMode(B_OP_ALPHA);
|
||||
view.SetBlendingMode(B_PIXEL_ALPHA,
|
||||
B_ALPHA_COMPOSITE);
|
||||
view.DrawBitmap(&thumb, thumb.Bounds(),
|
||||
tmp.Bounds(), B_FILTER_BITMAP_BILINEAR);
|
||||
view.Sync();
|
||||
view.UnlockLooper();
|
||||
}
|
||||
tmp.RemoveChild(&view);
|
||||
|
||||
// copy tmp bitmap into icon
|
||||
result = icon->ImportBits(&tmp);
|
||||
}
|
||||
// we found a thumbnail
|
||||
if (result == B_OK)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// else we did not find a thumbnail
|
||||
} else {
|
||||
// file changed, remove all thumb attrs
|
||||
char attrName[B_ATTR_NAME_LENGTH];
|
||||
while (node->GetNextAttrName(attrName) == B_OK) {
|
||||
if (BString(attrName).StartsWith(kAttrThumbnail))
|
||||
node->RemoveAttr(attrName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check generate thumbnail setting,
|
||||
// mime type must be an image type
|
||||
if (TrackerSettings().GenerateImageThumbnails()
|
||||
&& BString(model->MimeType()).IStartsWith("image")) {
|
||||
// try to fetch a new thumbnail icon
|
||||
result = GetThumbnailIcon(model, icon, which);
|
||||
if (result == B_OK) {
|
||||
// icon ready
|
||||
return B_OK;
|
||||
} else if (result == B_BUSY) {
|
||||
// working on icon, come back later
|
||||
return B_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
// get icon from the node info
|
||||
BNodeInfo nodeInfo(node);
|
||||
return nodeInfo.GetIcon(icon, which);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - image thumbnails
|
||||
|
||||
|
||||
struct ThumbGenParams {
|
||||
ThumbGenParams(Model* _model, BFile* _file, icon_size _which,
|
||||
color_space _colorSpace, port_id _port);
|
||||
virtual ~ThumbGenParams();
|
||||
|
||||
status_t InitCheck() { return fInitStatus; };
|
||||
|
||||
Model* model;
|
||||
BFile* file;
|
||||
icon_size which;
|
||||
color_space colorSpace;
|
||||
port_id port;
|
||||
|
||||
private:
|
||||
status_t fInitStatus;
|
||||
};
|
||||
|
||||
|
||||
ThumbGenParams::ThumbGenParams(Model* _model, BFile* _file, icon_size _which,
|
||||
color_space _colorSpace, port_id _port)
|
||||
{
|
||||
model = new(std::nothrow) Model(*_model);
|
||||
file = new(std::nothrow) BFile(*_file);
|
||||
which = _which;
|
||||
colorSpace = _colorSpace;
|
||||
port = _port;
|
||||
|
||||
fInitStatus = (model == NULL || file == NULL ? B_NO_MEMORY : B_OK);
|
||||
}
|
||||
|
||||
|
||||
ThumbGenParams::~ThumbGenParams()
|
||||
{
|
||||
delete file;
|
||||
delete model;
|
||||
}
|
||||
|
||||
|
||||
status_t get_thumbnail(void* castToParams);
|
||||
static const int32 kMsgIconData = 'ICON';
|
||||
|
||||
|
||||
BRect
|
||||
ThumbBounds(BBitmap* icon, float aspectRatio)
|
||||
{
|
||||
BRect thumbBounds;
|
||||
|
||||
if (aspectRatio > 1) {
|
||||
// wide
|
||||
thumbBounds = BRect(0, 0, icon->Bounds().IntegerWidth() - 1,
|
||||
floorf((icon->Bounds().IntegerHeight() - 1) / aspectRatio));
|
||||
thumbBounds.OffsetBySelf(0, floorf((icon->Bounds().IntegerHeight()
|
||||
- thumbBounds.IntegerHeight()) / 2.0f));
|
||||
} else if (aspectRatio < 1) {
|
||||
// tall
|
||||
thumbBounds = BRect(0, 0, floorf((icon->Bounds().IntegerWidth() - 1)
|
||||
* aspectRatio), icon->Bounds().IntegerHeight() - 1);
|
||||
thumbBounds.OffsetBySelf(floorf((icon->Bounds().IntegerWidth()
|
||||
- thumbBounds.IntegerWidth()) / 2.0f), 0);
|
||||
} else {
|
||||
// square
|
||||
thumbBounds = icon->Bounds();
|
||||
}
|
||||
|
||||
return thumbBounds;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
GetThumbnailIcon(Model* model, BBitmap* icon, icon_size which)
|
||||
{
|
||||
status_t result = B_ERROR;
|
||||
|
||||
// create a name for the node icon generator thread (32 chars max)
|
||||
icon_size w = (icon_size)B_XXL_ICON;
|
||||
dev_t d = model->NodeRef()->device;
|
||||
ino_t n = model->NodeRef()->node;
|
||||
BString genThreadName = BString("_thumbgen_w")
|
||||
<< w << "_d" << d << "_n" << n << "_";
|
||||
|
||||
bool volumeReadOnly = true;
|
||||
BVolume volume(model->NodeRef()->device);
|
||||
if (volume.InitCheck() == B_OK)
|
||||
volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr();
|
||||
|
||||
port_id port = B_NAME_NOT_FOUND;
|
||||
if (volumeReadOnly) {
|
||||
// look for a port with some icon data
|
||||
port = find_port(genThreadName.String());
|
||||
// give the port the same name as the generator thread
|
||||
if (port != B_NAME_NOT_FOUND && port_count(port) > 0) {
|
||||
// a generator thread has written some data to the port, fetch it
|
||||
uint8 iconData[icon->BitsLength()];
|
||||
int32 msgCode;
|
||||
int32 bytesRead = read_port(port, &msgCode, iconData,
|
||||
icon->BitsLength());
|
||||
if (bytesRead == icon->BitsLength() && msgCode == kMsgIconData
|
||||
&& iconData != NULL) {
|
||||
// fill icon data into the passed in icon
|
||||
result = icon->ImportBits(iconData, icon->BitsLength(),
|
||||
icon->BytesPerRow(), 0, icon->ColorSpace());
|
||||
}
|
||||
|
||||
if (result == B_OK) {
|
||||
// make a new port next time
|
||||
delete_port(port);
|
||||
port = B_NAME_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we found an icon from a generator thread
|
||||
if (result == B_OK)
|
||||
return B_OK;
|
||||
|
||||
// look for an existing generator thread before spawning a new one
|
||||
if (find_thread(genThreadName.String()) == B_NAME_NOT_FOUND) {
|
||||
// no generater thread found, spawn one
|
||||
BFile* file = dynamic_cast<BFile*>(model->Node());
|
||||
if (file == NULL)
|
||||
result = B_NOT_SUPPORTED; // node must be a file
|
||||
else {
|
||||
// create a new port if one doesn't already exist
|
||||
if (volumeReadOnly && port == B_NAME_NOT_FOUND)
|
||||
port = create_port(1, genThreadName.String());
|
||||
|
||||
ThumbGenParams* params = new ThumbGenParams(model, file, which,
|
||||
icon->ColorSpace(), port);
|
||||
if (params->InitCheck() == B_OK) {
|
||||
// generator thread will delete params, it makes copies
|
||||
resume_thread(spawn_thread(get_thumbnail,
|
||||
genThreadName.String(), B_LOW_PRIORITY, params));
|
||||
result = B_BUSY; // try again later
|
||||
} else
|
||||
delete params;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - thumbnail generator thread
|
||||
|
||||
|
||||
status_t
|
||||
get_thumbnail(void* castToParams)
|
||||
{
|
||||
ThumbGenParams* params = (ThumbGenParams*)castToParams;
|
||||
Model* model = params->model;
|
||||
BFile* file = params->file;
|
||||
icon_size which = params->which;
|
||||
color_space colorSpace = params->colorSpace;
|
||||
port_id port = params->port;
|
||||
|
||||
// get the mime type from the model
|
||||
const char* type = model->MimeType();
|
||||
|
||||
// check if attributes can be written to
|
||||
bool volumeReadOnly = true;
|
||||
BVolume volume(model->NodeRef()->device);
|
||||
if (volume.InitCheck() == B_OK)
|
||||
volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr();
|
||||
|
||||
// see if we have a thumbnail attribute
|
||||
attr_info attrInfo;
|
||||
status_t result = file->GetAttrInfo(kAttrThumbnail, &attrInfo);
|
||||
if (result != B_OK) {
|
||||
// create a new thumbnail
|
||||
|
||||
// check to see if we have a translator that works
|
||||
BBitmapStream imageStream;
|
||||
BBitmap* image;
|
||||
if (BTranslatorRoster::Default()->Translate(file, NULL, NULL,
|
||||
&imageStream, B_TRANSLATOR_BITMAP, 0, type) == B_OK
|
||||
&& imageStream.DetachBitmap(&image) == B_OK) {
|
||||
// we have translated the image file into a BBitmap
|
||||
|
||||
// check if we can write attrs
|
||||
if (!volumeReadOnly) {
|
||||
// write image width to an attribute
|
||||
int32 width = image->Bounds().IntegerWidth();
|
||||
file->WriteAttr("Media:Width", B_INT32_TYPE, 0, &width,
|
||||
sizeof(int32));
|
||||
|
||||
// write image height to an attribute
|
||||
int32 height = image->Bounds().IntegerHeight();
|
||||
file->WriteAttr("Media:Height", B_INT32_TYPE, 0, &height,
|
||||
sizeof(int32));
|
||||
|
||||
// convert image into a 128x128 WebP image and stash it
|
||||
BBitmap thumb = BBitmap(BRect(0, 0, B_XXL_ICON - 1,
|
||||
B_XXL_ICON - 1), colorSpace, true);
|
||||
BView view(thumb.Bounds(), "", B_FOLLOW_NONE,
|
||||
B_WILL_DRAW);
|
||||
thumb.AddChild(&view);
|
||||
if (view.LockLooper()) {
|
||||
// fill with transparent
|
||||
view.SetLowColor(B_TRANSPARENT_COLOR);
|
||||
view.FillRect(view.Bounds(), B_SOLID_LOW);
|
||||
// draw bitmap
|
||||
view.SetDrawingMode(B_OP_ALPHA);
|
||||
view.SetBlendingMode(B_PIXEL_ALPHA,
|
||||
B_ALPHA_COMPOSITE);
|
||||
view.DrawBitmap(image, image->Bounds(),
|
||||
ThumbBounds(&thumb, image->Bounds().Width()
|
||||
/ image->Bounds().Height()),
|
||||
B_FILTER_BITMAP_BILINEAR);
|
||||
view.Sync();
|
||||
view.UnlockLooper();
|
||||
}
|
||||
thumb.RemoveChild(&view);
|
||||
|
||||
BBitmap* thumbPointer = &thumb;
|
||||
BBitmapStream thumbStream(thumbPointer);
|
||||
BMallocIO stream;
|
||||
if (BTranslatorRoster::Default()->Translate(&thumbStream,
|
||||
NULL, NULL, &stream, B_WEBP_FORMAT) == B_OK
|
||||
&& thumbStream.DetachBitmap(&thumbPointer) == B_OK) {
|
||||
// write WebP image data into an attribute
|
||||
file->WriteAttr(kAttrThumbnail, B_RAW_TYPE, 0,
|
||||
stream.Buffer(), stream.BufferLength());
|
||||
|
||||
// write thumbnail creation time into an attribute
|
||||
bigtime_t created = system_time();
|
||||
file->WriteAttr(kAttrThumbnailCreationTime, B_TIME_TYPE,
|
||||
0, &created, sizeof(bigtime_t));
|
||||
|
||||
// we wrote thumbnail to an attribute
|
||||
result = B_OK;
|
||||
}
|
||||
} else if (port != B_NAME_NOT_FOUND) {
|
||||
// create a thumb at the requested icon size
|
||||
BBitmap thumb = BBitmap(BRect(0, 0, which - 1, which - 1),
|
||||
colorSpace, true);
|
||||
// copy image into a view bitmap, scaled and centered
|
||||
BView view(thumb.Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW);
|
||||
thumb.AddChild(&view);
|
||||
if (view.LockLooper()) {
|
||||
// fill with transparent
|
||||
view.SetLowColor(B_TRANSPARENT_COLOR);
|
||||
view.FillRect(view.Bounds(), B_SOLID_LOW);
|
||||
// draw bitmap
|
||||
view.SetDrawingMode(B_OP_ALPHA);
|
||||
view.SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
|
||||
view.DrawBitmap(image, image->Bounds(),
|
||||
ThumbBounds(&thumb, image->Bounds().Width()
|
||||
/ image->Bounds().Height()),
|
||||
B_FILTER_BITMAP_BILINEAR);
|
||||
view.Sync();
|
||||
view.UnlockLooper();
|
||||
}
|
||||
thumb.RemoveChild(&view);
|
||||
|
||||
// send icon back to the calling thread through the port
|
||||
result = write_port(port, kMsgIconData, (void*)thumb.Bits(),
|
||||
thumb.BitsLength());
|
||||
}
|
||||
}
|
||||
|
||||
delete image;
|
||||
}
|
||||
|
||||
if (result == B_OK) {
|
||||
// trigger an icon refresh
|
||||
if (!volumeReadOnly)
|
||||
model->Mimeset(true); // only works on read-write volumes
|
||||
else {
|
||||
// send Tracker a message to tell it to update the thumbnail
|
||||
BMessage message(kUpdateThumbnail);
|
||||
if (message.AddInt32("device", model->NodeRef()->device) == B_OK
|
||||
&& message.AddUInt64("node", model->NodeRef()->node) == B_OK) {
|
||||
be_app->PostMessage(&message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete params;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - PrintToStream
|
||||
|
||||
|
||||
void
|
||||
PrintToStream(rgb_color color)
|
||||
{
|
||||
@ -1500,6 +1901,9 @@ PrintToStream(rgb_color color)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - EachMenuItem
|
||||
|
||||
|
||||
extern BMenuItem*
|
||||
EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *))
|
||||
{
|
||||
|
@ -67,6 +67,7 @@ namespace BPrivate {
|
||||
class Benaphore;
|
||||
class BPose;
|
||||
class BPoseView;
|
||||
class Model;
|
||||
|
||||
// global variables
|
||||
static const rgb_color kBlack = {0, 0, 0, 255};
|
||||
@ -481,7 +482,8 @@ void _ThrowOnAssert(bool, const char*, int32);
|
||||
// stub calls that work around BAppFile info inefficiency
|
||||
status_t GetAppSignatureFromAttr(BFile*, char*);
|
||||
status_t GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which);
|
||||
status_t GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which);
|
||||
status_t GetFileIconFromAttr(Model* model, BBitmap* icon, icon_size which);
|
||||
status_t GetThumbnailIcon(Model* model, BBitmap* icon, icon_size which);
|
||||
|
||||
// debugging
|
||||
void HexDump(const void* buffer, int32 length);
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <Node.h>
|
||||
#include <NodeInfo.h>
|
||||
#include <String.h>
|
||||
#include <TypeConstants.h>
|
||||
|
||||
#include "AutoDeleter.h"
|
||||
@ -28,14 +30,11 @@
|
||||
#include "MessageImporter.h"
|
||||
|
||||
|
||||
#ifndef HAIKU_TARGET_PLATFORM_HAIKU
|
||||
# define B_MINI_ICON_TYPE 'MICN'
|
||||
# define B_LARGE_ICON_TYPE 'ICON'
|
||||
#endif
|
||||
#define B_MINI_ICON_TYPE 'MICN'
|
||||
#define B_LARGE_ICON_TYPE 'ICON'
|
||||
|
||||
|
||||
_USING_ICON_NAMESPACE;
|
||||
using std::nothrow;
|
||||
|
||||
|
||||
// #pragma mark - Scaling functions
|
||||
@ -150,15 +149,15 @@ scale_down(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight
|
||||
// nearby pixels
|
||||
p1 = *((rgb_color*)srcBits + (l * srcWidth) + c);
|
||||
p2 = *((rgb_color*)srcBits + (l * srcWidth) + c + 1);
|
||||
p3 = *((rgb_color*)srcBits + ((l + 1)* srcWidth) + c + 1);
|
||||
p4 = *((rgb_color*)srcBits + ((l + 1)* srcWidth) + c);
|
||||
p3 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c + 1);
|
||||
p4 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c);
|
||||
|
||||
// color components
|
||||
out.blue = (uint8)(p1.blue * d1 + p2.blue * d2 + p3.blue * d3
|
||||
out.blue = (uint8)(p1.blue * d1 + p2.blue * d2 + p3.blue * d3
|
||||
+ p4.blue * d4);
|
||||
out.green = (uint8)(p1.green * d1 + p2.green * d2 + p3.green * d3
|
||||
out.green = (uint8)(p1.green * d1 + p2.green * d2 + p3.green * d3
|
||||
+ p4.green * d4);
|
||||
out.red = (uint8)(p1.red * d1 + p2.red * d2 + p3.red * d3
|
||||
out.red = (uint8)(p1.red * d1 + p2.red * d2 + p3.red * d3
|
||||
+ p4.red * d4);
|
||||
out.alpha = (uint8)(p1.alpha * d1 + p2.alpha * d2 + p3.alpha * d3
|
||||
+ p4.alpha * d4);
|
||||
@ -285,8 +284,8 @@ scale4x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight,
|
||||
int32 srcBPR, int32 dstBPR)
|
||||
{
|
||||
// scale4x is just scale2x twice
|
||||
BBitmap* tmp = new BBitmap(BRect(0, 0, srcWidth * 2 - 1,
|
||||
srcHeight * 2 - 1), B_RGBA32);
|
||||
BRect rect = BRect(0, 0, srcWidth * 2 - 1, srcHeight * 2 - 1);
|
||||
BBitmap* tmp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, B_RGBA32);
|
||||
uint8* tmpBits = (uint8*)tmp->Bits();
|
||||
int32 tmpBPR = tmp->BytesPerRow();
|
||||
|
||||
@ -326,7 +325,7 @@ BIconUtils::GetIcon(BNode* node, const char* vectorIconAttrName,
|
||||
// (converting to B_RGBA32 is handled)
|
||||
|
||||
// override size
|
||||
if (icon->Bounds().IntegerWidth() + 1 >= 32)
|
||||
if (icon->Bounds().IntegerWidth() + 1 >= B_LARGE_ICON)
|
||||
which = B_LARGE_ICON;
|
||||
else
|
||||
which = B_MINI_ICON;
|
||||
@ -342,12 +341,8 @@ BIconUtils::GetIcon(BNode* node, const char* vectorIconAttrName,
|
||||
which, icon);
|
||||
if (result != B_OK) {
|
||||
// try to fallback to vector icon
|
||||
#ifdef HAIKU_TARGET_PLATFORM_HAIKU
|
||||
BBitmap temp(icon->Bounds(), B_BITMAP_NO_SERVER_LINK,
|
||||
B_RGBA32);
|
||||
#else
|
||||
BBitmap temp(icon->Bounds(), B_RGBA32);
|
||||
#endif
|
||||
result = temp.InitCheck();
|
||||
if (result != B_OK)
|
||||
break;
|
||||
@ -453,7 +448,7 @@ BIconUtils::GetVectorIcon(const uint8* buffer, size_t size, BBitmap* icon)
|
||||
ObjectDeleter<BBitmap> deleter;
|
||||
|
||||
if (icon->ColorSpace() != B_RGBA32 && icon->ColorSpace() != B_RGB32) {
|
||||
temp = new (nothrow) BBitmap(icon->Bounds(),
|
||||
temp = new(std::nothrow) BBitmap(icon->Bounds(),
|
||||
B_BITMAP_NO_SERVER_LINK, B_RGBA32);
|
||||
deleter.SetTo(temp);
|
||||
if (temp == NULL || temp->InitCheck() != B_OK)
|
||||
@ -544,16 +539,16 @@ BIconUtils::GetCMAP8Icon(BNode* node, const char* smallIconAttrName,
|
||||
switch (which) {
|
||||
case B_MINI_ICON:
|
||||
attribute = smallIconAttrName;
|
||||
bounds.Set(0, 0, 15, 15);
|
||||
bounds.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
|
||||
attrType = B_MINI_ICON_TYPE;
|
||||
attrSize = 16 * 16;
|
||||
attrSize = B_MINI_ICON * B_MINI_ICON;
|
||||
break;
|
||||
|
||||
case B_LARGE_ICON:
|
||||
attribute = largeIconAttrName;
|
||||
bounds.Set(0, 0, 31, 31);
|
||||
bounds.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1);
|
||||
attrType = B_LARGE_ICON_TYPE;
|
||||
attrSize = 32 * 32;
|
||||
attrSize = B_LARGE_ICON * B_LARGE_ICON;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -586,11 +581,13 @@ BIconUtils::GetCMAP8Icon(BNode* node, const char* smallIconAttrName,
|
||||
ssize_t bytesRead;
|
||||
if (useBuffer) {
|
||||
// other color space or bitmap size than stored in attribute
|
||||
buffer = new(nothrow) uint8[attrSize];
|
||||
buffer = new(std::nothrow) uint8[attrSize];
|
||||
if (buffer == NULL)
|
||||
result = B_NO_MEMORY;
|
||||
else
|
||||
bytesRead = node->ReadAttr(attribute, attrType, 0, buffer, attrSize);
|
||||
bytesRead = result = B_NO_MEMORY;
|
||||
else {
|
||||
bytesRead = node->ReadAttr(attribute, attrType, 0, buffer,
|
||||
attrSize);
|
||||
}
|
||||
} else {
|
||||
bytesRead = node->ReadAttr(attribute, attrType, 0, icon->Bits(),
|
||||
attrSize);
|
||||
@ -695,8 +692,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height,
|
||||
|| (dstWidth == 2 * width && dstHeight == 2 * height)
|
||||
|| (dstWidth == 3 * width && dstHeight == 3 * height)
|
||||
|| (dstWidth == 4 * width && dstHeight == 4 * height)) {
|
||||
BBitmap* converted = new BBitmap(BRect(0, 0, width - 1, height - 1),
|
||||
icon->ColorSpace());
|
||||
BRect rect = BRect(0, 0, width - 1, height - 1);
|
||||
BBitmap* converted = new(std::nothrow) BBitmap(rect,
|
||||
B_BITMAP_NO_SERVER_LINK, icon->ColorSpace());
|
||||
if (converted == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8);
|
||||
uint8* convertedBits = (uint8*)converted->Bits();
|
||||
int32 convertedBPR = converted->BytesPerRow();
|
||||
@ -746,8 +747,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height,
|
||||
if (dstWidth > width && dstHeight > height
|
||||
&& dstWidth < 2 * width && dstHeight < 2 * height) {
|
||||
// scale2x then downscale
|
||||
BBitmap* temp = new BBitmap(BRect(0, 0, width * 2 - 1, height * 2 - 1),
|
||||
icon->ColorSpace());
|
||||
BRect rect = BRect(0, 0, width * 2 - 1, height * 2 - 1);
|
||||
BBitmap* temp = new(std::nothrow) BBitmap(rect,
|
||||
B_BITMAP_NO_SERVER_LINK, icon->ColorSpace());
|
||||
if (temp == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint8* tempBits = (uint8*)temp->Bits();
|
||||
uint32 tempBPR = temp->BytesPerRow();
|
||||
scale2x(dst, tempBits, width, height, dstBPR, tempBPR);
|
||||
@ -756,8 +761,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height,
|
||||
} else if (dstWidth > 2 * width && dstHeight > 2 * height
|
||||
&& dstWidth < 3 * width && dstHeight < 3 * height) {
|
||||
// scale3x then downscale
|
||||
BBitmap* temp = new BBitmap(BRect(0, 0, width * 3 - 1, height * 3 - 1),
|
||||
BRect rect = BRect(0, 0, width * 3 - 1, height * 3 - 1);
|
||||
BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK,
|
||||
icon->ColorSpace());
|
||||
if (temp == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint8* tempBits = (uint8*)temp->Bits();
|
||||
uint32 tempBPR = temp->BytesPerRow();
|
||||
scale3x(dst, tempBits, width, height, dstBPR, tempBPR);
|
||||
@ -766,8 +775,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height,
|
||||
} else if (dstWidth > 3 * width && dstHeight > 3 * height
|
||||
&& dstWidth < 4 * width && dstHeight < 4 * height) {
|
||||
// scale4x then downscale
|
||||
BBitmap* temp = new BBitmap(BRect(0, 0, width * 4 - 1, height * 4 - 1),
|
||||
BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1);
|
||||
BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK,
|
||||
icon->ColorSpace());
|
||||
if (temp == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint8* tempBits = (uint8*)temp->Bits();
|
||||
uint32 tempBPR = temp->BytesPerRow();
|
||||
scale4x(dst, tempBits, width, height, dstBPR, tempBPR);
|
||||
@ -775,8 +788,12 @@ BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height,
|
||||
delete temp;
|
||||
} else if (dstWidth > 4 * width && dstHeight > 4 * height) {
|
||||
// scale4x then bilinear
|
||||
BBitmap* temp = new BBitmap(BRect(0, 0, width * 4 - 1, height * 4 - 1),
|
||||
BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1);
|
||||
BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK,
|
||||
icon->ColorSpace());
|
||||
if (temp == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
uint8* tempBits = (uint8*)temp->Bits();
|
||||
uint32 tempBPR = temp->BytesPerRow();
|
||||
scale4x(dst, tempBits, width, height, dstBPR, tempBPR);
|
||||
|
Loading…
Reference in New Issue
Block a user