/* * Copyright 2006-2024, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ #include "IconView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FileTypes.h" #include "MimeTypeListView.h" #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "Icon View" using namespace std; status_t icon_for_type(const BMimeType& type, uint8** _data, size_t* _size, icon_source* _source) { if (_data == NULL || _size == NULL) return B_BAD_VALUE; icon_source source = kNoIcon; uint8* data; size_t size; if (type.GetIcon(&data, &size) == B_OK) source = kOwnIcon; if (source == kNoIcon) { // check for icon from preferred app char preferred[B_MIME_TYPE_LENGTH]; if (type.GetPreferredApp(preferred) == B_OK) { BMimeType preferredApp(preferred); if (preferredApp.GetIconForType(type.Type(), &data, &size) == B_OK) source = kApplicationIcon; } } if (source == kNoIcon) { // check super type for an icon BMimeType superType; if (type.GetSupertype(&superType) == B_OK) { if (superType.GetIcon(&data, &size) == B_OK) source = kSupertypeIcon; else { // check the super type's preferred app char preferred[B_MIME_TYPE_LENGTH]; if (superType.GetPreferredApp(preferred) == B_OK) { BMimeType preferredApp(preferred); if (preferredApp.GetIconForType(superType.Type(), &data, &size) == B_OK) source = kSupertypeIcon; } } } } if (source != kNoIcon) { *_data = data; *_size = size; } // NOTE: else there is no data, so nothing is leaked. if (_source) *_source = source; return source != kNoIcon ? B_OK : B_ERROR; } status_t icon_for_type(const BMimeType& type, BBitmap& bitmap, icon_size size, icon_source* _source) { icon_source source = kNoIcon; if (type.GetIcon(&bitmap, size) == B_OK) source = kOwnIcon; if (source == kNoIcon) { // check for icon from preferred app char preferred[B_MIME_TYPE_LENGTH]; if (type.GetPreferredApp(preferred) == B_OK) { BMimeType preferredApp(preferred); if (preferredApp.GetIconForType(type.Type(), &bitmap, size) == B_OK) source = kApplicationIcon; } } if (source == kNoIcon) { // check super type for an icon BMimeType superType; if (type.GetSupertype(&superType) == B_OK) { if (superType.GetIcon(&bitmap, size) == B_OK) source = kSupertypeIcon; else { // check the super type's preferred app char preferred[B_MIME_TYPE_LENGTH]; if (superType.GetPreferredApp(preferred) == B_OK) { BMimeType preferredApp(preferred); if (preferredApp.GetIconForType(superType.Type(), &bitmap, size) == B_OK) source = kSupertypeIcon; } } } } if (_source) *_source = source; return source != kNoIcon ? B_OK : B_ERROR; } // #pragma mark - Icon::Icon() : fLarge(NULL), fMini(NULL), fData(NULL), fSize(0) { } Icon::Icon(const Icon& source) : fLarge(NULL), fMini(NULL), fData(NULL), fSize(0) { *this = source; } Icon::~Icon() { delete fLarge; delete fMini; free(fData); } void Icon::SetTo(const BAppFileInfo& info, const char* type) { Unset(); uint8* data; size_t size; if (info.GetIconForType(type, &data, &size) == B_OK) { // we have the vector icon, no need to get the rest AdoptData(data, size); return; } BBitmap* icon = AllocateBitmap(B_LARGE_ICON, B_CMAP8); if (icon && info.GetIconForType(type, icon, B_LARGE_ICON) == B_OK) AdoptLarge(icon); else delete icon; icon = AllocateBitmap(B_MINI_ICON, B_CMAP8); if (icon && info.GetIconForType(type, icon, B_MINI_ICON) == B_OK) AdoptMini(icon); else delete icon; } void Icon::SetTo(const entry_ref& ref, const char* type) { Unset(); BFile file(&ref, B_READ_ONLY); BAppFileInfo info(&file); if (file.InitCheck() == B_OK && info.InitCheck() == B_OK) SetTo(info, type); } void Icon::SetTo(const BMimeType& type, icon_source* _source) { Unset(); uint8* data; size_t size; if (icon_for_type(type, &data, &size, _source) == B_OK) { // we have the vector icon, no need to get the rest AdoptData(data, size); return; } BBitmap* icon = AllocateBitmap(B_LARGE_ICON, B_CMAP8); if (icon && icon_for_type(type, *icon, B_LARGE_ICON, _source) == B_OK) AdoptLarge(icon); else delete icon; icon = AllocateBitmap(B_MINI_ICON, B_CMAP8); if (icon && icon_for_type(type, *icon, B_MINI_ICON) == B_OK) AdoptMini(icon); else delete icon; } status_t Icon::CopyTo(BAppFileInfo& info, const char* type, bool force) const { status_t status = B_OK; if (fLarge != NULL || force) status = info.SetIconForType(type, fLarge, B_LARGE_ICON); if (fMini != NULL || force) status = info.SetIconForType(type, fMini, B_MINI_ICON); if (fData != NULL || force) status = info.SetIconForType(type, fData, fSize); return status; } status_t Icon::CopyTo(const entry_ref& ref, const char* type, bool force) const { BFile file; status_t status = file.SetTo(&ref, B_READ_ONLY); if (status < B_OK) return status; BAppFileInfo info(&file); status = info.InitCheck(); if (status < B_OK) return status; return CopyTo(info, type, force); } status_t Icon::CopyTo(BMimeType& type, bool force) const { status_t status = B_OK; if (fLarge != NULL || force) status = type.SetIcon(fLarge, B_LARGE_ICON); if (fMini != NULL || force) status = type.SetIcon(fMini, B_MINI_ICON); if (fData != NULL || force) status = type.SetIcon(fData, fSize); return status; } status_t Icon::CopyTo(BMessage& message) const { status_t status = B_OK; if (status == B_OK && fLarge != NULL) { BMessage archive; status = fLarge->Archive(&archive); if (status == B_OK) status = message.AddMessage("icon/large", &archive); } if (status == B_OK && fMini != NULL) { BMessage archive; status = fMini->Archive(&archive); if (status == B_OK) status = message.AddMessage("icon/mini", &archive); } if (status == B_OK && fData != NULL) status = message.AddData("icon", B_VECTOR_ICON_TYPE, fData, fSize); return B_OK; } void Icon::SetLarge(const BBitmap* large) { if (large != NULL) { if (fLarge == NULL) fLarge = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); memcpy(fLarge->Bits(), large->Bits(), min_c(large->BitsLength(), fLarge->BitsLength())); } else { delete fLarge; fLarge = NULL; } } void Icon::SetMini(const BBitmap* mini) { if (mini != NULL) { if (fMini == NULL) fMini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); memcpy(fMini->Bits(), mini->Bits(), min_c(mini->BitsLength(), fMini->BitsLength())); } else { delete fMini; fMini = NULL; } } void Icon::SetData(const uint8* data, size_t size) { free(fData); fData = NULL; if (data != NULL) { fData = (uint8*)malloc(size); if (fData != NULL) { fSize = size; //fType = B_VECTOR_ICON_TYPE; memcpy(fData, data, size); } } } void Icon::Unset() { delete fLarge; delete fMini; free(fData); fLarge = fMini = NULL; fData = NULL; } bool Icon::HasData() const { return fData != NULL || fLarge != NULL || fMini != NULL; } status_t Icon::GetData(icon_size which, BBitmap** _bitmap) const { BBitmap* source; switch (which) { case B_LARGE_ICON: source = fLarge; break; case B_MINI_ICON: source = fMini; break; default: return B_BAD_VALUE; } if (source == NULL) return B_ENTRY_NOT_FOUND; BBitmap* bitmap = new (nothrow) BBitmap(source); if (bitmap == NULL || bitmap->InitCheck() != B_OK) { delete bitmap; return B_NO_MEMORY; } *_bitmap = bitmap; return B_OK; } status_t Icon::GetData(uint8** _data, size_t* _size) const { if (fData == NULL) return B_ENTRY_NOT_FOUND; uint8* data = (uint8*)malloc(fSize); if (data == NULL) return B_NO_MEMORY; memcpy(data, fData, fSize); *_data = data; *_size = fSize; return B_OK; } status_t Icon::GetIcon(BBitmap* bitmap) const { if (bitmap == NULL) return B_BAD_VALUE; if (fData != NULL && BIconUtils::GetVectorIcon(fData, fSize, bitmap) == B_OK) return B_OK; int32 width = bitmap->Bounds().IntegerWidth() + 1; if (width == B_LARGE_ICON && fLarge != NULL) { bitmap->SetBits(fLarge->Bits(), fLarge->BitsLength(), 0, fLarge->ColorSpace()); return B_OK; } if (width == B_MINI_ICON && fMini != NULL) { bitmap->SetBits(fMini->Bits(), fMini->BitsLength(), 0, fMini->ColorSpace()); return B_OK; } BBitmap* source = (width > B_LARGE_ICON && fLarge != NULL) || fMini == NULL ? fLarge : fMini; if (source == NULL) return B_ENTRY_NOT_FOUND; // Resize bitmap to fit the target BBitmap* target = new (nothrow) BBitmap(bitmap->Bounds(), B_BITMAP_ACCEPTS_VIEWS, bitmap->ColorSpace()); if (target != NULL && target->InitCheck() == B_OK && target->Lock()) { BView* view = new BView(bitmap->Bounds(), NULL, B_FOLLOW_NONE, B_WILL_DRAW); target->AddChild(view); view->DrawBitmap(source, bitmap->Bounds()); view->Flush(); target->RemoveChild(view); target->Unlock(); // Copy target to original bitmap bitmap->SetBits(target->Bits(), target->BitsLength(), 0, target->ColorSpace()); delete view; } delete target; return B_OK; } Icon& Icon::operator=(const Icon& source) { Unset(); SetData(source.fData, source.fSize); SetLarge(source.fLarge); SetMini(source.fMini); return *this; } void Icon::AdoptLarge(BBitmap *large) { delete fLarge; fLarge = large; } void Icon::AdoptMini(BBitmap *mini) { delete fMini; fMini = mini; } void Icon::AdoptData(uint8* data, size_t size) { free(fData); fData = data; fSize = size; } /*static*/ BBitmap* Icon::AllocateBitmap(icon_size size, int32 space) { int32 kSpace = B_RGBA32; if (space == -1) space = kSpace; BBitmap* bitmap; if (space == B_CMAP8) { // Legacy mode; no scaling bitmap = new (nothrow) BBitmap(BRect(0, 0, (int32)size - 1, (int32)size - 1), B_CMAP8); } else { bitmap = new (nothrow) BBitmap(BRect(BPoint(0, 0), be_control_look->ComposeIconSize(size)), (color_space)space); } if (bitmap == NULL || bitmap->InitCheck() != B_OK) { delete bitmap; return NULL; } return bitmap; } // #pragma mark - IconView::IconView(const char* name, uint32 flags) : BControl(name, NULL, NULL, B_WILL_DRAW | flags), fModificationMessage(NULL), fIconSize((icon_size)0), fIconBitmap(NULL), fHeapIconBitmap(NULL), fHasRef(false), fHasType(false), fIcon(NULL), fTracking(false), fDragging(false), fDropTarget(false), fShowEmptyFrame(true) { SetIconSize(B_LARGE_ICON); } IconView::~IconView() { delete fIconBitmap; delete fModificationMessage; } void IconView::AttachedToWindow() { AdoptParentColors(); fTarget = this; // SetTo() was already called before we were a valid messenger if (fHasRef || fHasType) _StartWatching(); } void IconView::DetachedFromWindow() { _StopWatching(); } void IconView::MessageReceived(BMessage* message) { if (message->WasDropped() && message->ReturnAddress() != BMessenger(this) && AcceptsDrag(message)) { // set icon from message BBitmap* mini = NULL; BBitmap* large = NULL; const uint8* data = NULL; ssize_t size = 0; message->FindData("icon", B_VECTOR_ICON_TYPE, (const void**)&data, &size); BMessage archive; if (message->FindMessage("icon/large", &archive) == B_OK) large = (BBitmap*)BBitmap::Instantiate(&archive); if (message->FindMessage("icon/mini", &archive) == B_OK) mini = (BBitmap*)BBitmap::Instantiate(&archive); if (large != NULL || mini != NULL || (data != NULL && size > 0)) _SetIcon(large, mini, data, size); else { entry_ref ref; if (message->FindRef("refs", &ref) == B_OK) _SetIcon(&ref); } delete large; delete mini; return; } switch (message->what) { case kMsgIconInvoked: case kMsgEditIcon: case kMsgAddIcon: _AddOrEditIcon(); break; case kMsgRemoveIcon: _RemoveIcon(); break; case B_NODE_MONITOR: { if (!fHasRef) break; int32 opcode; if (message->FindInt32("opcode", &opcode) != B_OK || opcode != B_ATTR_CHANGED) break; const char* name; if (message->FindString("attr", &name) != B_OK) break; if (!strcmp(name, kAttrMiniIcon) || !strcmp(name, kAttrLargeIcon) || !strcmp(name, kAttrIcon)) Update(); break; } case B_META_MIME_CHANGED: { if (!fHasType) break; const char* type; int32 which; if (message->FindString("be:type", &type) != B_OK || message->FindInt32("be:which", &which) != B_OK) break; if (!strcasecmp(type, fType.Type())) { switch (which) { case B_MIME_TYPE_DELETED: Unset(); break; case B_ICON_CHANGED: Update(); break; default: break; } } else if (fSource != kOwnIcon && message->FindString("be:extra_type", &type) == B_OK && !strcasecmp(type, fType.Type())) { // this change could still affect our current icon if (which == B_MIME_TYPE_DELETED || which == B_PREFERRED_APP_CHANGED || which == B_SUPPORTED_TYPES_CHANGED || which == B_ICON_FOR_TYPE_CHANGED) Update(); } break; } case B_ICON_DATA_EDITED: { const uint8* data; ssize_t size; if (message->FindData("icon data", B_VECTOR_ICON_TYPE, (const void**)&data, &size) < B_OK) break; _SetIcon(NULL, NULL, data, size); break; } default: BControl::MessageReceived(message); break; } } bool IconView::AcceptsDrag(const BMessage* message) { if (!IsEnabled()) return false; type_code type; int32 count; if (message->GetInfo("refs", &type, &count) == B_OK && count == 1 && type == B_REF_TYPE) { // if we're bound to an entry, check that no one drops this to us entry_ref ref; if (fHasRef && message->FindRef("refs", &ref) == B_OK && fRef == ref) return false; return true; } if ((message->GetInfo("icon/large", &type) == B_OK && type == B_MESSAGE_TYPE) || (message->GetInfo("icon", &type) == B_OK && type == B_VECTOR_ICON_TYPE) || (message->GetInfo("icon/mini", &type) == B_OK && type == B_MESSAGE_TYPE)) return true; return false; } BRect IconView::BitmapRect() const { return fIconRect; } void IconView::Draw(BRect updateRect) { SetDrawingMode(B_OP_ALPHA); if (fHeapIconBitmap != NULL) DrawBitmap(fHeapIconBitmap, BitmapRect()); else if (fIconBitmap != NULL) DrawBitmap(fIconBitmap, BitmapRect()); else if (!fDropTarget && fShowEmptyFrame) { // draw frame so that the user knows here is something he // might be able to click on SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); StrokeRect(BitmapRect()); } if (IsFocus()) { // mark this view as a having focus SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); StrokeRect(BitmapRect()); } if (fDropTarget) { // mark this view as a drop target SetHighColor(0, 0, 0); SetPenSize(2); BRect rect = BitmapRect(); // TODO: this is an incompatibility between R5 and Haiku and should be fixed! // (Necessary adjustment differs.) rect.left++; rect.top++; StrokeRect(rect); SetPenSize(1); } } void IconView::GetPreferredSize(float* _width, float* _height) { if (_width) *_width = fIconRect.Width(); if (_height) *_height = fIconRect.Height(); } BSize IconView::MinSize() { float width, height; GetPreferredSize(&width, &height); return BSize(width, height); } BSize IconView::PreferredSize() { return MinSize(); } BSize IconView::MaxSize() { return MinSize(); } void IconView::MouseDown(BPoint where) { if (!IsEnabled()) return; int32 buttons = B_PRIMARY_MOUSE_BUTTON; int32 clicks = 1; if (Looper() != NULL && Looper()->CurrentMessage() != NULL) { if (Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) buttons = B_PRIMARY_MOUSE_BUTTON; if (Looper()->CurrentMessage()->FindInt32("clicks", &clicks) != B_OK) clicks = 1; } if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0 && BitmapRect().Contains(where)) { if (clicks == 2) { // double click - open Icon-O-Matic Invoke(); } else if (fIconBitmap != NULL) { // start tracking - this icon might be dragged around fDragPoint = where; fTracking = true; SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); } } if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { // show context menu ConvertToScreen(&where); BPopUpMenu* menu = new BPopUpMenu("context"); menu->SetFont(be_plain_font); bool hasIcon = fHasType ? fSource == kOwnIcon : fIconBitmap != NULL; if (hasIcon) { menu->AddItem(new BMenuItem( B_TRANSLATE("Edit icon" B_UTF8_ELLIPSIS), new BMessage(kMsgEditIcon))); } else { menu->AddItem(new BMenuItem( B_TRANSLATE("Add icon" B_UTF8_ELLIPSIS), new BMessage(kMsgAddIcon))); } BMenuItem* item = new BMenuItem( B_TRANSLATE("Remove icon"), new BMessage(kMsgRemoveIcon)); if (!hasIcon) item->SetEnabled(false); menu->AddItem(item); menu->SetTargetForItems(fTarget); menu->Go(where, true, false, true); } } void IconView::MouseUp(BPoint where) { fTracking = false; fDragging = false; if (fDropTarget) { fDropTarget = false; Invalidate(); } } void IconView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) { if (fTracking && !fDragging && fIconBitmap != NULL && (abs((int32)(where.x - fDragPoint.x)) > 3 || abs((int32)(where.y - fDragPoint.y)) > 3)) { // Start drag BMessage message(B_SIMPLE_DATA); ::Icon* icon = fIcon; if (fHasRef || fHasType) { icon = new ::Icon; if (fHasRef) icon->SetTo(fRef, fType.Type()); else if (fHasType) icon->SetTo(fType); } icon->CopyTo(message); if (icon != fIcon) delete icon; BBitmap *dragBitmap = new BBitmap(fIconBitmap->Bounds(), B_RGBA32, true); dragBitmap->Lock(); BView *view = new BView(dragBitmap->Bounds(), B_EMPTY_STRING, B_FOLLOW_NONE, 0); dragBitmap->AddChild(view); view->SetHighColor(B_TRANSPARENT_COLOR); view->FillRect(dragBitmap->Bounds()); view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); view->SetDrawingMode(B_OP_ALPHA); view->SetHighColor(0, 0, 0, 160); view->DrawBitmap(fIconBitmap); view->Sync(); dragBitmap->Unlock(); DragMessage(&message, dragBitmap, B_OP_ALPHA, fDragPoint - BitmapRect().LeftTop(), this); fDragging = true; } if (dragMessage != NULL && !fDragging && AcceptsDrag(dragMessage)) { bool dropTarget = transit == B_ENTERED_VIEW || transit == B_INSIDE_VIEW; if (dropTarget != fDropTarget) { fDropTarget = dropTarget; Invalidate(); } } else if (fDropTarget) { fDropTarget = false; Invalidate(); } } void IconView::KeyDown(const char* bytes, int32 numBytes) { if (numBytes == 1) { switch (bytes[0]) { case B_DELETE: case B_BACKSPACE: _RemoveIcon(); return; case B_ENTER: case B_SPACE: Invoke(); return; } } BControl::KeyDown(bytes, numBytes); } void IconView::MakeFocus(bool focus) { if (focus != IsFocus()) Invalidate(); BControl::MakeFocus(focus); } void IconView::SetTo(const entry_ref& ref, const char* fileType) { Unset(); fHasRef = true; fRef = ref; if (fileType != NULL) fType.SetTo(fileType); else fType.Unset(); _StartWatching(); Update(); } void IconView::SetTo(const BMimeType& type) { Unset(); if (type.Type() == NULL) return; fHasType = true; fType.SetTo(type.Type()); _StartWatching(); Update(); } void IconView::SetTo(::Icon* icon) { if (fIcon == icon) return; Unset(); fIcon = icon; Update(); } void IconView::Unset() { if (fHasRef || fHasType) _StopWatching(); fHasRef = false; fHasType = false; fType.Unset(); fIcon = NULL; } void IconView::Update() { delete fIconBitmap; fIconBitmap = NULL; Invalidate(); // this will actually trigger a redraw *after* we updated the icon below BBitmap* bitmap = NULL; if (fHasRef) { BFile file(&fRef, B_READ_ONLY); if (file.InitCheck() != B_OK) return; BNodeInfo info; if (info.SetTo(&file) != B_OK) return; bitmap = Icon::AllocateBitmap(fIconSize); if (bitmap != NULL && info.GetTrackerIcon(bitmap, (icon_size)(bitmap->Bounds().IntegerWidth() + 1)) != B_OK) { delete bitmap; return; } } else if (fHasType) { bitmap = Icon::AllocateBitmap(fIconSize); if (bitmap != NULL && icon_for_type(fType, *bitmap, (icon_size)fIconSize, &fSource) != B_OK) { delete bitmap; return; } } else if (fIcon != NULL) { bitmap = Icon::AllocateBitmap(fIconSize); if (fIcon->GetIcon(bitmap) != B_OK) { delete bitmap; bitmap = NULL; } } fIconBitmap = bitmap; } void IconView::SetIconSize(icon_size size) { if (size < B_MINI_ICON) size = B_MINI_ICON; if (size > 256) size = (icon_size)256; if (size == fIconSize) return; fIconSize = size; fIconRect = BRect(BPoint(0, 0), be_control_look->ComposeIconSize(fIconSize)); Update(); } void IconView::ShowIconHeap(bool show) { if (show == (fHeapIconBitmap != NULL)) return; if (show) { BResources* resources = be_app->AppResources(); if (resources != NULL) { const void* data = NULL; size_t size; data = resources->LoadResource('VICN', "icon heap", &size); if (data != NULL) { // got vector icon data fHeapIconBitmap = Icon::AllocateBitmap(B_LARGE_ICON, B_RGBA32); if (BIconUtils::GetVectorIcon((const uint8*)data, size, fHeapIconBitmap) != B_OK) { // bad data delete fHeapIconBitmap; fHeapIconBitmap = NULL; data = NULL; } } if (data == NULL) { // no vector icon or failed to get bitmap // try bitmap icon data = resources->LoadResource(B_LARGE_ICON_TYPE, "icon heap", NULL); if (data != NULL) { fHeapIconBitmap = Icon::AllocateBitmap(B_LARGE_ICON, B_CMAP8); if (fHeapIconBitmap != NULL) { memcpy(fHeapIconBitmap->Bits(), data, fHeapIconBitmap->BitsLength()); } } } } } else { delete fHeapIconBitmap; fHeapIconBitmap = NULL; } } void IconView::ShowEmptyFrame(bool show) { if (show == fShowEmptyFrame) return; fShowEmptyFrame = show; if (fIconBitmap == NULL) Invalidate(); } status_t IconView::SetTarget(const BMessenger& target) { fTarget = target; return B_OK; } void IconView::SetModificationMessage(BMessage* message) { delete fModificationMessage; fModificationMessage = message; } status_t IconView::Invoke(BMessage* message) { if (message == NULL) fTarget.SendMessage(kMsgIconInvoked); else fTarget.SendMessage(message); return B_OK; } Icon* IconView::Icon() { return fIcon; } status_t IconView::GetRef(entry_ref& ref) const { if (!fHasRef) return B_BAD_TYPE; ref = fRef; return B_OK; } status_t IconView::GetMimeType(BMimeType& type) const { if (!fHasType) return B_BAD_TYPE; type.SetTo(fType.Type()); return B_OK; } void IconView::_AddOrEditIcon() { BMessage message; if (fHasRef && fType.Type() == NULL) { // in ref mode, Icon-O-Matic can change the icon directly, and // we'll pick it up via node monitoring message.what = B_REFS_RECEIVED; message.AddRef("refs", &fRef); } else { // in static or MIME type mode, Icon-O-Matic needs to return the // buffer it changed once its done message.what = B_EDIT_ICON_DATA; message.AddMessenger("reply to", BMessenger(this)); ::Icon* icon = fIcon; if (icon == NULL) { icon = new ::Icon(); if (fHasRef) icon->SetTo(fRef, fType.Type()); else icon->SetTo(fType); } if (icon->HasData()) { uint8* data; size_t size; if (icon->GetData(&data, &size) == B_OK) { message.AddData("icon data", B_VECTOR_ICON_TYPE, data, size); free(data); } // TODO: somehow figure out how names of objects in the icon // can be preserved. Maybe in a second (optional) attribute // where ever a vector icon attribute is present? } if (icon != fIcon) delete icon; } be_roster->Launch("application/x-vnd.haiku-icon_o_matic", &message); } void IconView::_SetIcon(BBitmap* large, BBitmap* mini, const uint8* data, size_t size, bool force) { if (fHasRef) { BNodeInfo node; BDirectory refdir; BFile file; BEntry entry(&fRef, true); if (entry.IsFile()) { file.SetTo(&fRef, B_READ_WRITE); if (is_application(file)) { BAppFileInfo info(&file); if (info.InitCheck() == B_OK) { if (large != NULL || force) info.SetIconForType(fType.Type(), large, B_LARGE_ICON); if (mini != NULL || force) info.SetIconForType(fType.Type(), mini, B_MINI_ICON); if (data != NULL || force) info.SetIconForType(fType.Type(), data, size); } } else node.SetTo(&file); } if (entry.IsDirectory()) { refdir.SetTo(&fRef); node.SetTo(&refdir); } if (node.InitCheck() == B_OK) { if (large != NULL || force) node.SetIcon(large, B_LARGE_ICON); if (mini != NULL || force) node.SetIcon(mini, B_MINI_ICON); if (data != NULL || force) node.SetIcon(data, size); } // the icon shown will be updated using node monitoring } else if (fHasType) { if (large != NULL || force) fType.SetIcon(large, B_LARGE_ICON); if (mini != NULL || force) fType.SetIcon(mini, B_MINI_ICON); if (data != NULL || force) fType.SetIcon(data, size); // the icon shown will be updated automatically - we're watching // any changes to the MIME database } else if (fIcon != NULL) { if (large != NULL || force) fIcon->SetLarge(large); if (mini != NULL || force) fIcon->SetMini(mini); if (data != NULL || force) fIcon->SetData(data, size); // replace visible icon if (fIconBitmap == NULL && fIcon->HasData()) fIconBitmap = Icon::AllocateBitmap(fIconSize); if (fIcon->GetIcon(fIconBitmap) != B_OK) { delete fIconBitmap; fIconBitmap = NULL; } Invalidate(); } if (fModificationMessage) Invoke(fModificationMessage); } void IconView::_SetIcon(entry_ref* ref) { // retrieve icons from file BFile file(ref, B_READ_ONLY); BAppFileInfo info(&file); if (file.InitCheck() != B_OK || info.InitCheck() != B_OK) return; // try vector/PNG icon first uint8* data = NULL; size_t size = 0; if (info.GetIcon(&data, &size) == B_OK) { _SetIcon(NULL, NULL, data, size); free(data); return; } // try large/mini icons bool hasMini = false; bool hasLarge = false; BBitmap* large = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8); if (large->InitCheck() != B_OK) { delete large; large = NULL; } BBitmap* mini = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8); if (mini->InitCheck() != B_OK) { delete mini; mini = NULL; } if (large != NULL && info.GetIcon(large, B_LARGE_ICON) == B_OK) hasLarge = true; if (mini != NULL && info.GetIcon(mini, B_MINI_ICON) == B_OK) hasMini = true; if (!hasMini && !hasLarge) { // TODO: don't forget device icons! // try MIME type icon char type[B_MIME_TYPE_LENGTH]; if (info.GetType(type) != B_OK) return; BMimeType mimeType(type); if (icon_for_type(mimeType, &data, &size) != B_OK) { // only try large/mini icons when there is no vector icon if (large != NULL && icon_for_type(mimeType, *large, B_LARGE_ICON) == B_OK) hasLarge = true; if (mini != NULL && icon_for_type(mimeType, *mini, B_MINI_ICON) == B_OK) hasMini = true; } } if (data != NULL) { _SetIcon(NULL, NULL, data, size); free(data); } else if (hasLarge || hasMini) _SetIcon(large, mini, NULL, 0); delete large; delete mini; } void IconView::_RemoveIcon() { _SetIcon(NULL, NULL, NULL, 0, true); } void IconView::_StartWatching() { if (Looper() == NULL) { // we are not a valid messenger yet return; } if (fHasRef) { BNode node(&fRef); node_ref nodeRef; if (node.InitCheck() == B_OK && node.GetNodeRef(&nodeRef) == B_OK) watch_node(&nodeRef, B_WATCH_ATTR, this); } else if (fHasType) BMimeType::StartWatching(this); } void IconView::_StopWatching() { if (fHasRef) stop_watching(this); else if (fHasType) BMimeType::StopWatching(this); } #if __GNUC__ == 2 status_t IconView::SetTarget(BMessenger target) { return BControl::SetTarget(target); } status_t IconView::SetTarget(const BHandler* handler, const BLooper* looper = NULL) { return BControl::SetTarget(handler, looper); } #endif