From 0b0ff90fa0742fb59c66fc597f34ea16222733a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 14 Nov 2006 16:11:56 +0000 Subject: [PATCH] * Extended the IconView quite a bit to support what we need: - supports drag&drop from icons (only tested for non-vector icons yet) - allows to remove/edit/add icons (there is still need for support in Icon-O-Matic for this to work completely) - has a mode where it stores the changed icon data in an object rather than directly to disk; this object can maintain all BeOS icon types - reacts to live updates from file icons - supports different view sizes (for later or uses in other apps) * works again on BeOS (it crashed before because of a BeOS "feature"). * in the Application window, the "supported types" listview will now be enlarged with the window instead of the long version description. * To compensate for that, the latter now shows a scroll bar. * Application delivered supported type icons are now shown as well, and can be edited - they're not yet saved, though. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19279 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../filetypes/ApplicationTypeWindow.cpp | 90 +- .../filetypes/ApplicationTypeWindow.h | 4 +- src/preferences/filetypes/FileTypeWindow.cpp | 2 +- src/preferences/filetypes/FileTypesWindow.cpp | 5 + src/preferences/filetypes/IconView.cpp | 816 +++++++++++++++++- src/preferences/filetypes/IconView.h | 85 +- src/preferences/filetypes/Jamfile | 1 + .../filetypes/MimeTypeListView.cpp | 4 + 8 files changed, 949 insertions(+), 58 deletions(-) diff --git a/src/preferences/filetypes/ApplicationTypeWindow.cpp b/src/preferences/filetypes/ApplicationTypeWindow.cpp index 8b562e4ece..6b6423f08a 100644 --- a/src/preferences/filetypes/ApplicationTypeWindow.cpp +++ b/src/preferences/filetypes/ApplicationTypeWindow.cpp @@ -51,17 +51,22 @@ class SupportedTypeItem : public BStringItem { ~SupportedTypeItem(); const char* Type() const { return fType.String(); } + ::Icon* Icon() { return fIcon; } + void SetIcon(::Icon* icon); + void SetIcon(entry_ref& ref, const char* type); static int Compare(const void* _a, const void* _b); private: BString fType; + ::Icon* fIcon; }; SupportedTypeItem::SupportedTypeItem(const char* type) : BStringItem(type), - fType(type) + fType(type), + fIcon(NULL) { BMimeType mimeType(type); @@ -76,6 +81,27 @@ SupportedTypeItem::~SupportedTypeItem() } +void +SupportedTypeItem::SetIcon(::Icon* icon) +{ + delete fIcon; + if (icon != NULL) + fIcon = new ::Icon(*icon); + else + fIcon = NULL; +} + + +void +SupportedTypeItem::SetIcon(entry_ref& ref, const char* type) +{ + if (fIcon == NULL) + fIcon = new ::Icon; + + fIcon->SetTo(ref, type); +} + + /*static*/ int SupportedTypeItem::Compare(const void* _a, const void* _b) @@ -223,24 +249,24 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr rect.bottom = rect.top + box->Bounds().Height(); rect.left = 8.0f; rect.right = Bounds().Width() - 8.0f; - box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); - box->SetLabel("Supported Types"); - topView->AddChild(box); + BBox* typeBox = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); + typeBox->SetLabel("Supported Types"); + topView->AddChild(typeBox); - rect = box->Bounds().InsetByCopy(8.0f, 6.0f); + rect = typeBox->Bounds().InsetByCopy(8.0f, 6.0f); rect.top += ceilf(fontHeight.ascent); fAddTypeButton = new BButton(rect, "add type", "Add" B_UTF8_ELLIPSIS, new BMessage(kMsgAddType), B_FOLLOW_RIGHT); fAddTypeButton->ResizeToPreferred(); fAddTypeButton->MoveBy(rect.right - fAddTypeButton->Bounds().Width() - B_LARGE_ICON - 16.0f, 0.0f); - box->AddChild(fAddTypeButton); + typeBox->AddChild(fAddTypeButton); rect = fAddTypeButton->Frame(); rect.OffsetBy(0, rect.Height() + 4.0f); fRemoveTypeButton = new BButton(rect, "remove type", "Remove", new BMessage(kMsgRemoveType), B_FOLLOW_RIGHT); - box->AddChild(fRemoveTypeButton); + typeBox->AddChild(fRemoveTypeButton); rect.right = rect.left - 10.0f - B_V_SCROLL_BAR_WIDTH; rect.left = 10.0f; @@ -248,14 +274,14 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr rect.bottom -= 2.0f; // take scrollview border into account fTypeListView = new BListView(rect, "type listview", - B_SINGLE_SELECTION_LIST, B_FOLLOW_LEFT_RIGHT); + B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL); fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView, - B_FOLLOW_LEFT_RIGHT, B_FRAME_EVENTS | B_WILL_DRAW, false, true); - box->AddChild(scrollView); + B_FOLLOW_ALL, B_FRAME_EVENTS | B_WILL_DRAW, false, true); - box->ResizeTo(box->Bounds().Width(), fRemoveTypeButton->Frame().bottom + 8.0f); + typeBox->ResizeTo(typeBox->Bounds().Width(), fRemoveTypeButton->Frame().bottom + 8.0f); + typeBox->AddChild(scrollView); rect.left = fRemoveTypeButton->Frame().right + 8.0f; #ifdef __HAIKU__ @@ -266,16 +292,16 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr rect.right = rect.left + B_LARGE_ICON - 1.0f; rect.bottom = rect.top + B_LARGE_ICON - 1.0f; fTypeIconView = new IconView(rect, "type icon", B_FOLLOW_RIGHT | B_FOLLOW_TOP); - box->AddChild(fTypeIconView); + typeBox->AddChild(fTypeIconView); // "Version Info" group - rect.top = box->Frame().bottom + 8.0f; - rect.bottom = rect.top + box->Bounds().Height(); + rect.top = typeBox->Frame().bottom + 8.0f; + rect.bottom = rect.top + typeBox->Bounds().Height(); rect.left = 8.0f; rect.right = Bounds().Width() - 8.0f; box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); - // the resizing mode will later be set to B_FOLLOW_ALL + // the resizing mode will later also be set to B_FOLLOW_BOTTOM box->SetLabel("Version Info"); topView->AddChild(box); @@ -353,7 +379,7 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr rect = box->Bounds().InsetByCopy(8.0f, 0.0f); rect.top = fInternalVersionControl->Frame().bottom + 8.0f; fShortDescriptionControl = new BTextControl(rect, "short desc", "Short Description:", - NULL, NULL, B_FOLLOW_LEFT_RIGHT); + NULL, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); float labelWidth = fShortDescriptionControl->StringWidth( fShortDescriptionControl->Label()) + 4.0f; fShortDescriptionControl->SetDivider(labelWidth); @@ -373,15 +399,14 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr rect.left = rect.right + 3.0f; rect.top += 1.0f; - rect.right = box->Bounds().Width() - 8.0f; + rect.right = box->Bounds().Width() - 10.0f - B_V_SCROLL_BAR_WIDTH; rect.bottom = rect.top + fShortDescriptionControl->Bounds().Height() * 3.0f - 1.0f; fLongDescriptionView = new BTextView(rect, "long desc", rect.OffsetToCopy(B_ORIGIN), B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS); fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info)); -// box->AddChild(fLongDescriptionView); scrollView = new BScrollView("desc scrollview", fLongDescriptionView, - B_FOLLOW_ALL, B_FRAME_EVENTS | B_WILL_DRAW, false, false); + B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_FRAME_EVENTS | B_WILL_DRAW, false, true); box->ResizeTo(box->Bounds().Width(), scrollView->Frame().bottom + 8.0f); box->AddChild(scrollView); @@ -395,7 +420,8 @@ ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entr ResizeTo(Bounds().Width() > minWidth ? Bounds().Width() : minWidth, box->Frame().bottom + topView->Frame().top + 8.0f); SetSizeLimits(minWidth, 32767.0f, Bounds().Height(), 32767.0f); - box->SetResizingMode(B_FOLLOW_ALL); + typeBox->SetResizingMode(B_FOLLOW_ALL); + box->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM); fSignatureControl->MakeFocus(true); @@ -488,7 +514,11 @@ ApplicationTypeWindow::_SetTo(const BEntry& entry) entry_ref ref; if (entry.GetRef(&ref) == B_OK) - fIconView->SetTo(&ref); + fIcon.SetTo(ref); + else + fIcon.Unset(); + + fIconView->SetTo(&fIcon); // supported types @@ -499,9 +529,17 @@ ApplicationTypeWindow::_SetTo(const BEntry& entry) const char* type; for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) { - fTypeListView->AddItem(new SupportedTypeItem(type)); + SupportedTypeItem* item = new SupportedTypeItem(type); + + entry_ref ref; + if (fEntry.GetRef(&ref) == B_OK) + item->SetIcon(ref, type); + + fTypeListView->AddItem(item); } fTypeListView->SortItems(&SupportedTypeItem::Compare); + fTypeIconView->SetTo(NULL); + fTypeIconView->SetEnabled(false); fRemoveTypeButton->SetEnabled(false); // version info @@ -647,7 +685,10 @@ ApplicationTypeWindow::MessageReceived(BMessage* message) { int32 index; if (message->FindInt32("index", &index) == B_OK) { - BStringItem* item = (BStringItem*)fTypeListView->ItemAt(index); + SupportedTypeItem* item = (SupportedTypeItem*)fTypeListView->ItemAt(index); + + fTypeIconView->SetTo(item != NULL ? item->Icon() : NULL); + fTypeIconView->SetEnabled(item != NULL); fRemoveTypeButton->SetEnabled(item != NULL); } break; @@ -705,6 +746,9 @@ ApplicationTypeWindow::MessageReceived(BMessage* message) break; delete fTypeListView->RemoveItem(index); + fTypeIconView->SetTo(NULL); + fTypeIconView->SetEnabled(false); + fRemoveTypeButton->SetEnabled(false); break; } diff --git a/src/preferences/filetypes/ApplicationTypeWindow.h b/src/preferences/filetypes/ApplicationTypeWindow.h index 7c56157ea3..169b6e6a61 100644 --- a/src/preferences/filetypes/ApplicationTypeWindow.h +++ b/src/preferences/filetypes/ApplicationTypeWindow.h @@ -6,6 +6,8 @@ #define APPLICATION_TYPE_WINDOW_H +#include "IconView.h" + #include #include #include @@ -18,7 +20,6 @@ class BRadioButton; class BTextControl; class BTextView; -class IconView; class MimeTypeListView; @@ -43,6 +44,7 @@ class ApplicationTypeWindow : public BWindow { BTextControl* fSignatureControl; IconView* fIconView; + Icon fIcon; BCheckBox* fFlagsCheckBox; BRadioButton* fSingleLaunchButton; diff --git a/src/preferences/filetypes/FileTypeWindow.cpp b/src/preferences/filetypes/FileTypeWindow.cpp index 74627480cd..c77c21d497 100644 --- a/src/preferences/filetypes/FileTypeWindow.cpp +++ b/src/preferences/filetypes/FileTypeWindow.cpp @@ -261,7 +261,7 @@ FileTypeWindow::_SetTo(const BMessage& refs) fCommonPreferredApp = preferredApp; if (i == 0) - fIconView->SetTo(&ref); + fIconView->SetTo(ref); } fTypeControl->SetText(fCommonType.String()); diff --git a/src/preferences/filetypes/FileTypesWindow.cpp b/src/preferences/filetypes/FileTypesWindow.cpp index 602408a948..7cb0727ffc 100644 --- a/src/preferences/filetypes/FileTypesWindow.cpp +++ b/src/preferences/filetypes/FileTypesWindow.cpp @@ -114,8 +114,13 @@ TypeIconView::SetTo(BMimeType* type) if (type != NULL) { if (fIcon == NULL) { +#ifdef HAIKU_TARGET_PLATFORM_HAIKU fIcon = new BBitmap(BRect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1), B_RGB32); +#else + fIcon = new BBitmap(BRect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1), + B_CMAP8); +#endif } icon_for_type(*type, *fIcon, B_LARGE_ICON, &fIconSource); diff --git a/src/preferences/filetypes/IconView.cpp b/src/preferences/filetypes/IconView.cpp index d0bba10251..7c14927b70 100644 --- a/src/preferences/filetypes/IconView.cpp +++ b/src/preferences/filetypes/IconView.cpp @@ -5,28 +5,346 @@ #include "IconView.h" +#include "MimeTypeListView.h" + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU +# include +#endif #include +#include #include #include #include -#include +#include #include #include +#include +#include +#include #include +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(BAppFileInfo& info, const char* type) +{ + Unset(); + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + 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; + } +#endif + + 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(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::CopyTo(BAppFileInfo& info, const char* type, bool force) +{ + if (fLarge != NULL || force) + info.SetIconForType(type, fLarge, B_LARGE_ICON); + if (fMini != NULL || force) + info.SetIconForType(type, fMini, B_MINI_ICON); +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + if (fData != NULL || force) + info.SetIconForType(type, fData, fSize); +#endif +} + + +void +Icon::CopyTo(entry_ref& ref, const char* type, bool force) +{ + BFile file(&ref, B_READ_ONLY); + BAppFileInfo info(&file); + if (file.InitCheck() == B_OK + && info.InitCheck() == B_OK) + CopyTo(info, type, force); +} + + +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) +{ +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + 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); + } + } +#endif +} + + +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; + + *_data = data; + *_size = fSize; + return B_OK; +} + + +status_t +Icon::GetIcon(BBitmap* bitmap) const +{ + if (bitmap == NULL) + return B_BAD_VALUE; + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + if (fData != NULL && BIconUtils::GetVectorIcon(fData, fSize, bitmap) == B_OK) + return B_OK; +#endif + + 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; + } + + return B_ENTRY_NOT_FOUND; +} + + +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(int32 size, int32 space) +{ +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + int32 kSpace = B_RGB32; +#else + int32 kSpace = B_CMAP8; +#endif + if (space == -1) + space = kSpace; + + BBitmap* bitmap = new (nothrow) BBitmap(BRect(0, 0, size - 1, size - 1), + (color_space)space); + if (bitmap == NULL || bitmap->InitCheck() != B_OK) { + delete bitmap; + return NULL; + } + + return bitmap; +} + + +// #pragma mark - + + IconView::IconView(BRect rect, const char* name, uint32 resizeMode) : BView(rect, name, resizeMode, B_WILL_DRAW), + fIconSize(B_LARGE_ICON), fIcon(NULL), - fHeapIcon(NULL) + fHeapIcon(NULL), + fIconData(NULL), + fHasRef(false), + fIsLive(false), + fTracking(false), + fDragging(false), + fDropTarget(false), + fEnabled(true) { } IconView::~IconView() { + delete fIcon; } @@ -37,6 +355,107 @@ IconView::AttachedToWindow() SetViewColor(Parent()->ViewColor()); else SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + fTarget = this; + + // SetTo() was already called before we were a valid messenger + if (fIsLive) + _StartWatching(); +} + + +void +IconView::DetachedFromWindow() +{ + _StopWatching(); +} + + +void +IconView::MessageReceived(BMessage* message) +{ + if (message->WasDropped() && _AcceptsDrag(message)) { + // set icon from message + BBitmap* mini = NULL; + BBitmap* large = NULL; + const uint8* data = NULL; + ssize_t size = 0; + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + message->FindData("icon", B_VECTOR_ICON_TYPE, (const void**)&data, &size); +#endif + + 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); + + entry_ref ref; + if (message->FindRef("refs", &ref) == B_OK) + _SetIcon(&ref); + + return; + } + + switch (message->what) { + case kMsgIconInvoked: + _AddOrEditIcon(); + break; + case kMsgRemoveIcon: + _RemoveIcon(); + break; + + case B_NODE_MONITOR: + { + 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, "BEOS:L:STD_ICON")) + Update(); + break; + } + + default: + BView::MessageReceived(message); + break; + } +} + + +bool +IconView::_AcceptsDrag(const BMessage* message) +{ + if (!fEnabled) + return false; + + type_code type; + int32 count; + if (message->GetInfo("refs", &type, &count) == B_OK && count == 1 && type == B_REF_TYPE +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + || message->GetInfo("icon", &type) == B_OK && type == B_VECTOR_ICON_TYPE +#endif + || message->GetInfo("icon/large", &type) == B_OK && type == B_MESSAGE_TYPE + || message->GetInfo("icon/mini", &type) == B_OK && type == B_MESSAGE_TYPE) + return true; + + return false; +} + + +BRect +IconView::_BitmapRect() const +{ + return BRect(0, 0, 31, 31); } @@ -46,24 +465,60 @@ IconView::Draw(BRect updateRect) SetDrawingMode(B_OP_ALPHA); if (fHeapIcon != NULL) - DrawBitmap(fHeapIcon, BPoint(0.0f, 0.0f)); + DrawBitmap(fHeapIcon, _BitmapRect().LeftTop()); else if (fIcon != NULL) - DrawBitmap(fIcon, BPoint(0.0f, 0.0f)); - else { + DrawBitmap(fIcon, _BitmapRect().LeftTop()); + else if (!fDropTarget) { // 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(Bounds()); } + + if (fDropTarget) { + // mark this view as a drop target + SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); + StrokeRect(Bounds()); + } +} + + +void +IconView::GetPreferredSize(float* _width, float* _height) +{ + if (_width) + *_width = ceilf(fIconSize); + + if (_height) + *_height = ceilf(fIconSize); } void IconView::MouseDown(BPoint where) { + if (!fEnabled) + return; + int32 buttons = B_PRIMARY_MOUSE_BUTTON; - if (Looper() != NULL && Looper()->CurrentMessage() != NULL) - Looper()->CurrentMessage()->FindInt32("buttons", &buttons); + 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 + fTarget.SendMessage(kMsgIconInvoked); + } else if (fIcon != NULL) { + // start tracking - this icon might be dragged around + fDragPoint = where; + fTracking = true; + } + } if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) { // show context menu @@ -72,41 +527,110 @@ IconView::MouseDown(BPoint where) BPopUpMenu* menu = new BPopUpMenu("context"); menu->SetFont(be_plain_font); - BMenuItem* item; - menu->AddItem(item = new BMenuItem(fIcon != NULL - ? "Edit Icon" B_UTF8_ELLIPSIS : "Add Icon" B_UTF8_ELLIPSIS, NULL)); - item->SetEnabled(false); - menu->AddItem(item = new BMenuItem("Remove Icon", NULL)); - item->SetEnabled(false); - menu->Go(where); + if (fIcon != NULL) + menu->AddItem(new BMenuItem("Edit Icon" B_UTF8_ELLIPSIS, new BMessage(kMsgEditIcon))); + else + menu->AddItem(new BMenuItem("Add Icon" B_UTF8_ELLIPSIS, new BMessage(kMsgAddIcon))); + + menu->AddItem(new BMenuItem("Remove Icon", new BMessage(kMsgRemoveIcon))); + menu->SetTargetForItems(fTarget); + + menu->Go(where, true, false, true); } } void -IconView::SetTo(entry_ref* ref) +IconView::MouseUp(BPoint where) { - delete fIcon; - fIcon = NULL; + fTracking = false; + fDragging = false; - if (ref != NULL) { - BNode node(ref); - BNodeInfo info(&node); - if (node.InitCheck() != B_OK - || info.InitCheck() != B_OK) - return; + if (fDropTarget) { + fDropTarget = false; + Invalidate(); + } +} - BBitmap* icon = new BBitmap(BRect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1), B_RGB32); - if (info.GetIcon(icon, B_LARGE_ICON) != B_OK) { - delete icon; - return; - } - fIcon = icon; +void +IconView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage) +{ + if (fTracking && !fDragging && fIcon != NULL + && (abs((int32)(where.x - fDragPoint.x)) > 3 + || abs((int32)(where.y - fDragPoint.y)) > 3)) { + // Start drag + BMessage message(B_SIMPLE_DATA); + + // TODO: for now... + BMessage archive; + fIcon->Archive(&archive); + message.AddMessage("icon/large", &archive); + + BBitmap *dragBitmap = new BBitmap(fIcon); + DragMessage(&message, dragBitmap, B_OP_ALPHA, fDragPoint, this); + fDragging = true; + SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY); } - Invalidate(); + 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::SetTo(entry_ref& ref, const char* fileType) +{ + if (fHasRef && fIsLive) + _StopWatching(); + + fHasRef = true; + fRef = ref; + + if (fileType != NULL) + fFileType = fileType; + else + fFileType = ""; + + fIsLive = true; + _StartWatching(); + Update(); +} + + +void +IconView::SetTo(::Icon* icon) +{ + if (fIconData == icon) + return; + + fIconData = icon; + fHasRef = false; + Update(); +} + + +void +IconView::SetIconSize(int32 size) +{ + if (size < B_MINI_ICON) + size = B_MINI_ICON; + if (size > 256) + size = 256; + if (size == fIconSize) + return; + + fIconSize = size; + Update(); } @@ -119,11 +643,11 @@ IconView::ShowIconHeap(bool show) if (show) { BResources* resources = be_app->AppResources(); const void* data = NULL; + // TODO: get vector heap icon! if (resources != NULL) data = resources->LoadResource('ICON', "icon heap", NULL); if (data != NULL) { - fHeapIcon = new BBitmap(BRect(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1), - B_CMAP8); + fHeapIcon = Icon::AllocateBitmap(B_LARGE_ICON, B_CMAP8); memcpy(fHeapIcon->Bits(), data, fHeapIcon->BitsLength()); } } else { @@ -132,3 +656,231 @@ IconView::ShowIconHeap(bool show) } } + +void +IconView::SetTarget(const BMessenger& target) +{ + fTarget = target; +} + + +void +IconView::SetEnabled(bool enabled) +{ + if (fEnabled == enabled) + return; + + fEnabled = enabled; + Invalidate(); +} + + +void +IconView::Update() +{ + delete fIcon; + fIcon = NULL; + + Invalidate(); + // this will actually trigger a redraw *after* we updated the icon below + + BBitmap* icon = NULL; + + if (fHasRef) { + if (fIconData != NULL) + fIconData->Unset(); + + BFile file(&fRef, B_READ_ONLY); + if (file.InitCheck() != B_OK) + return; + + const char* fileType = fFileType.Length() > 0 ? fFileType.String() : NULL; + + BAppFileInfo info; + if (info.SetTo(&file) != B_OK) + return; + + icon = Icon::AllocateBitmap(fIconSize); + if (icon != NULL && info.GetIconForType(fileType, icon, (icon_size)fIconSize) != B_OK) { + delete icon; + return; + } + + if (fIconData != NULL) + fIconData->SetTo(info, fileType); + } else if (fIconData) { + icon = Icon::AllocateBitmap(fIconSize); + if (fIconData->GetIcon(icon) != B_OK) { + delete icon; + icon = NULL; + } + } + + fIcon = icon; +} + + +Icon* +IconView::Icon() +{ + return fIconData; +} + + +void +IconView::_AddOrEditIcon() +{ + BMessage message; + if (fIsLive) { + // in live 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); + if (fFileType.Length() > 0) + message.AddString("file type", fFileType.String()); + } else { + // in static mode, Icon-O-Matic need to return the buffer it + // changed once its done + message.what = 0; + // TODO: this interface is yet to be determined + message.AddMessenger("reply to", BMessenger(this)); + } + + 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 (fIsLive) { + BFile file(&fRef, B_READ_WRITE); + + BAppFileInfo info(&file); + if (info.InitCheck() == B_OK) { + const char* type = fFileType.Length() > 0 ? fFileType.String() : NULL; + + if (large != NULL || force) + info.SetIconForType(type, large, B_LARGE_ICON); + if (mini != NULL || force) + info.SetIconForType(type, mini, B_MINI_ICON); +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + if (data != NULL || force) + info.SetIconForType(type, data, size); +#endif + } + // the icon shown will be updated using node monitoring + } else if (fIconData != NULL) { + if (large != NULL || force) + fIconData->SetLarge(large); + if (mini != NULL || force) + fIconData->SetMini(mini); + if (data != NULL || force) + fIconData->SetData(data, size); + + // replace visible icon + if (fIcon == NULL && fIconData->HasData()) + fIcon = Icon::AllocateBitmap(fIconSize); + + if (fIconData->GetIcon(fIcon) != B_OK) { + delete fIcon; + fIcon = NULL; + } + Invalidate(); + } +} + + +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; + +#ifdef HAIKU_TARGET_PLATFORM_HAIKU + // 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; + } +#endif + + // 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; + + // TODO: vector icon support! + BMimeType mimeType(type); + 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 (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; + } + + BNode node(&fRef); + node_ref nodeRef; + if (node.InitCheck() == B_OK + && node.GetNodeRef(&nodeRef) == B_OK) + watch_node(&nodeRef, B_WATCH_ATTR, this); +} + + +void +IconView::_StopWatching() +{ + stop_watching(this); +} + diff --git a/src/preferences/filetypes/IconView.h b/src/preferences/filetypes/IconView.h index 6bf774669b..8213a905a4 100644 --- a/src/preferences/filetypes/IconView.h +++ b/src/preferences/filetypes/IconView.h @@ -6,9 +6,50 @@ #define ICON_VIEW_H +#include +#include +#include +#include #include +class Icon { + public: + Icon(); + Icon(const Icon& source); + ~Icon(); + + void SetTo(BAppFileInfo& info, const char* type = NULL); + void SetTo(entry_ref& ref, const char* type = NULL); + void CopyTo(BAppFileInfo& info, const char* type = NULL, bool force = false); + void CopyTo(entry_ref& ref, const char* type = NULL, bool force = false); + + void SetData(const uint8* data, size_t size); + void SetLarge(const BBitmap* large); + void SetMini(const BBitmap* large); + void Unset(); + + bool HasData() const; + status_t GetData(icon_size which, BBitmap** _bitmap) const; + status_t GetData(uint8** _data, size_t* _size) const; + + status_t GetIcon(BBitmap* bitmap) const; + + Icon& operator=(const Icon& source); + + void AdoptLarge(BBitmap *large); + void AdoptMini(BBitmap *mini); + void AdoptData(uint8* data, size_t size); + + static BBitmap* AllocateBitmap(int32 size, int32 space = -1); + + private: + BBitmap* fLarge; + BBitmap* fMini; + uint8* fData; + size_t fSize; +}; + class IconView : public BView { public: IconView(BRect rect, const char* name, @@ -16,16 +57,58 @@ class IconView : public BView { virtual ~IconView(); virtual void AttachedToWindow(); + virtual void DetachedFromWindow(); + virtual void MessageReceived(BMessage* message); virtual void Draw(BRect updateRect); + virtual void GetPreferredSize(float* _width, float* _height); virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage); - void SetTo(entry_ref* file); + void SetTo(entry_ref& file, const char* fileType = NULL); + void SetTo(::Icon* icon); + void SetIconSize(int32 size); void ShowIconHeap(bool show); + void SetEnabled(bool enabled); + void SetTarget(const BMessenger& target); + void Update(); + + ::Icon* Icon(); private: + bool _AcceptsDrag(const BMessage* message); + BRect _BitmapRect() const; + void _AddOrEditIcon(); + void _SetIcon(BBitmap* large, BBitmap* mini, const uint8* data, size_t size, + bool force = false); + void _SetIcon(entry_ref* ref); + void _RemoveIcon(); + void _DeleteIcons(); + void _StartWatching(); + void _StopWatching(); + + BMessenger fTarget; + int32 fIconSize; BBitmap* fIcon; BBitmap* fHeapIcon; + + ::Icon* fIconData; + + bool fHasRef; + bool fIsLive; + entry_ref fRef; + BString fFileType; + BPoint fDragPoint; + bool fTracking; + bool fDragging; + bool fDropTarget; + bool fEnabled; }; +static const uint32 kMsgIconInvoked = 'iciv'; +static const uint32 kMsgRemoveIcon = 'icrm'; +static const uint32 kMsgAddIcon = 'icad'; +static const uint32 kMsgEditIcon = 'iced'; + #endif // ICON_VIEW_H diff --git a/src/preferences/filetypes/Jamfile b/src/preferences/filetypes/Jamfile index 921680f0f6..6412ba9789 100644 --- a/src/preferences/filetypes/Jamfile +++ b/src/preferences/filetypes/Jamfile @@ -3,6 +3,7 @@ SubDir HAIKU_TOP src preferences filetypes ; SetSubDirSupportedPlatformsBeOSCompatible ; AddSubDirSupportedPlatforms libbe_test ; +UseLibraryHeaders icon ; UsePrivateHeaders shared ; SubDirSysHdrs $(HAIKU_TOP) src kits tracker ; diff --git a/src/preferences/filetypes/MimeTypeListView.cpp b/src/preferences/filetypes/MimeTypeListView.cpp index 88f1441698..b3e58be0ba 100644 --- a/src/preferences/filetypes/MimeTypeListView.cpp +++ b/src/preferences/filetypes/MimeTypeListView.cpp @@ -137,7 +137,11 @@ MimeTypeItem::DrawItem(BView* owner, BRect frame, bool complete) owner->FillRect(rect, B_SOLID_LOW); } +#ifdef HAIKU_TARGET_PLATFORM_HAIKU BBitmap bitmap(BRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1), B_RGB32); +#else + BBitmap bitmap(BRect(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1), B_CMAP8); +#endif BMimeType mimeType(fType.String()); status_t status = icon_for_type(mimeType, bitmap, B_MINI_ICON); if (status < B_OK) {