From bb0416a246118b535e761fa6a3462ee0a31a38c6 Mon Sep 17 00:00:00 2001 From: Philippe Houdoin Date: Thu, 24 Feb 2011 17:48:21 +0000 Subject: [PATCH] * PictureView is now keyboard navigable * Alpha transparency of image is now respected * Add a popup menu to load an image or remove the current one. * The current picture can also be removed by pressing Delete key * ... or by dragging the image to desktop's Trash git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40668 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/people/PictureView.cpp | 310 +++++++++++++++++++++++++++++--- src/apps/people/PictureView.h | 16 ++ 2 files changed, 298 insertions(+), 28 deletions(-) diff --git a/src/apps/people/PictureView.cpp b/src/apps/people/PictureView.cpp index dcd265e1ed..6269182891 100644 --- a/src/apps/people/PictureView.cpp +++ b/src/apps/people/PictureView.cpp @@ -10,24 +10,68 @@ #include "PictureView.h" #include +#include +#include #include +#include +#include #include #include +#include +#include #include +#include #include +#include #include "PeopleApp.h" // for B_PERSON_MIMETYPE +#undef B_TRANSLATE_CONTEXT +#define B_TRANSLATE_CONTEXT "People" + + +const uint32 kMsgPopUpMenuClosed = 'pmcl'; + +class PopUpMenu : public BPopUpMenu { +public: + PopUpMenu(const char* name, BMessenger target); + virtual ~PopUpMenu(); + +private: + BMessenger fTarget; +}; + + +PopUpMenu::PopUpMenu(const char* name, BMessenger target) + : + BPopUpMenu(name, false, false), fTarget(target) +{ + SetAsyncAutoDestruct(true); +} + + +PopUpMenu::~PopUpMenu() +{ + fTarget.SendMessage(kMsgPopUpMenuClosed); +} + + +// #pragma mark - + +using std::nothrow; + + const float kPictureMargin = 6.0; PictureView::PictureView(float width, float height, const entry_ref* ref) : - BView("pictureview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE), + BView("pictureview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_NAVIGABLE), fPicture(NULL), fOriginalPicture(NULL), - fDefaultPicture(NULL) + fDefaultPicture(NULL), + fShowingPopUpMenu(false) { SetViewColor(255, 255, 255); @@ -57,8 +101,7 @@ PictureView::PictureView(float width, float height, const entry_ref* ref) PictureView::~PictureView() { delete fDefaultPicture; - if (fPicture != fDefaultPicture) - delete fPicture; + delete fPicture; if (fOriginalPicture != fPicture) delete fOriginalPicture; } @@ -67,7 +110,7 @@ PictureView::~PictureView() bool PictureView::HasChanged() { - return fOriginalPicture != fPicture; + return fPicture != fOriginalPicture; } @@ -77,12 +120,8 @@ PictureView::Revert() if (!HasChanged()) return; - if (fPicture != fDefaultPicture) - delete fPicture; - - fPicture = fOriginalPicture != NULL ? - fOriginalPicture : fDefaultPicture; - + _DeletePicture(); + fPicture = fOriginalPicture; Invalidate(); } @@ -101,7 +140,7 @@ PictureView::Update(const entry_ref* ref) BBitmap* PictureView::Bitmap() { - return fPicture != fDefaultPicture ? fPicture : NULL; + return fPicture; } @@ -119,15 +158,26 @@ PictureView::MessageReceived(BMessage* message) if (picture == NULL) break; - if (fPicture != fDefaultPicture - && fPicture != fOriginalPicture) - delete fPicture; + _DeletePicture(); fPicture = picture; - Invalidate(); + MakeFocus(true); break; } + case B_COPY_TARGET: + _HandleDrop(message); + break; + + case B_DELETE: + case B_TRASH_TARGET: + _DeletePicture(); + break; + + case kMsgPopUpMenuClosed: + fShowingPopUpMenu = false; + break; + default: BView::MessageReceived(message); break; @@ -142,12 +192,6 @@ PictureView::Draw(BRect updateRect) BBitmap* picture = fPicture ? fPicture : fDefaultPicture; if (picture != NULL) { - if (picture == fDefaultPicture) { - SetDrawingMode(B_OP_ALPHA); - SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); - SetHighColor(0, 0, 0, 24); - } - // scale to fit and center picture in frame BRect frame = rect.InsetByCopy(kPictureMargin, kPictureMargin); BRect srcRect = picture->Bounds(); @@ -156,17 +200,227 @@ PictureView::Draw(BRect updateRect) size.height = srcRect.Height() * size.width / srcRect.Width(); else size.width = srcRect.Width() * size.height / srcRect.Height(); - BRect dstRect = BLayoutUtils::AlignInFrame(frame, size, + + fPictureRect = BLayoutUtils::AlignInFrame(frame, size, BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); - DrawBitmapAsync(picture, srcRect, dstRect, B_FILTER_BITMAP_BILINEAR); + SetDrawingMode(B_OP_ALPHA); + if (picture == fDefaultPicture) { + SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY); + SetHighColor(0, 0, 0, 24); + } - if (picture == fDefaultPicture) - SetDrawingMode(B_OP_OVER); + DrawBitmapAsync(picture, srcRect, fPictureRect, B_FILTER_BITMAP_BILINEAR); + + SetDrawingMode(B_OP_OVER); } // Draw the outer frame - rgb_color black = {0, 0, 0, 255}; - SetHighColor(tint_color(black, B_LIGHTEN_2_TINT)); + rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); + if (IsFocus() && Window() && Window()->IsActive()) + SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR)); + else + SetHighColor(tint_color(base, B_DARKEN_3_TINT)); StrokeRect(rect); } + + +void +PictureView::WindowActivated(bool active) +{ + BView::WindowActivated(active); + + if (IsFocus()) + Invalidate(); +} + + +void +PictureView::MakeFocus(bool focused) +{ + BView::MakeFocus(focused); + + Invalidate(); +} + + +void +PictureView::MouseDown(BPoint position) +{ + MakeFocus(true); + + uint32 buttons = 0; + if (Window() != NULL && Window()->CurrentMessage() != NULL) + buttons = Window()->CurrentMessage()->FindInt32("buttons"); + + if (fPicture != NULL && fPictureRect.Contains(position) + && (buttons + & (B_PRIMARY_MOUSE_BUTTON | B_SECONDARY_MOUSE_BUTTON)) != 0) { + + _BeginDrag(position); + + } else if (buttons == B_SECONDARY_MOUSE_BUTTON) + _ShowPopUpMenu(ConvertToScreen(position)); +} + + +void +PictureView::KeyDown(const char* bytes, int32 numBytes) +{ + if (numBytes != 1) { + BView::KeyDown(bytes, numBytes); + return; + } + + switch (*bytes) { + case B_DELETE: + _DeletePicture(); + break; + + default: + BView::KeyDown(bytes, numBytes); + break; + } +} + + +// #pragma mark - + + +void +PictureView::_ShowPopUpMenu(BPoint screen) +{ + if (fShowingPopUpMenu) + return; + + PopUpMenu* menu = new PopUpMenu("PopUpMenu", this); + + BMenuItem* item = new BMenuItem(B_TRANSLATE("Load image" B_UTF8_ELLIPSIS), + new BMessage(kMsgLoadImage)); + menu->AddItem(item); + + item = new BMenuItem(B_TRANSLATE("Remove image"), new BMessage(B_DELETE)); + item->SetEnabled(fPicture != NULL); + menu->AddItem(item); + + menu->SetTargetForItems(this); + menu->Go(screen, true, true, true); + fShowingPopUpMenu = true; +} + + +BBitmap* +PictureView::_CopyPicture(uint8 alpha) +{ + bool hasAlpha = alpha != 255; + + if (!fPicture) + return NULL; + + BRect rect = fPictureRect.OffsetToCopy(B_ORIGIN); + BView view(rect, NULL, B_FOLLOW_NONE, B_WILL_DRAW); + BBitmap* bitmap = new(nothrow) BBitmap(rect, hasAlpha ? B_RGBA32 + : fPicture->ColorSpace(), true); + if (bitmap == NULL || !bitmap->IsValid()) { + delete bitmap; + return NULL; + } + + if (bitmap->Lock()) { + bitmap->AddChild(&view); + if (hasAlpha) { + view.SetHighColor(0, 0, 0, 0); + view.FillRect(rect); + view.SetDrawingMode(B_OP_ALPHA); + view.SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); + view.SetHighColor(0, 0, 0, alpha); + } + view.DrawBitmap(fPicture, fPicture->Bounds().OffsetToCopy(B_ORIGIN), + rect, B_FILTER_BITMAP_BILINEAR); + view.Sync(); + bitmap->RemoveChild(&view); + bitmap->Unlock(); + } + + return bitmap; +} + + +void +PictureView::_BeginDrag(BPoint sourcePoint) +{ + BBitmap* bitmap = _CopyPicture(128); + if (bitmap == NULL) + return; + + // fill the drag message + BMessage drag(B_SIMPLE_DATA); + drag.AddInt32("be:actions", B_COPY_TARGET); + drag.AddInt32("be:actions", B_TRASH_TARGET); + + // name the clip after person name, if any + BString name = B_TRANSLATE("%name% picture"); + name.ReplaceFirst("%name%", Window() ? Window()->Title() : + B_TRANSLATE("Unnamed person")); + drag.AddString("be:clip_name", name.String()); + + BTranslatorRoster* roster = BTranslatorRoster::Default(); + if (roster == NULL) + return; + + int32 infoCount; + translator_info* info; + BBitmapStream stream(bitmap); + if (roster->GetTranslators(&stream, NULL, &info, &infoCount) == B_OK) { + for (int32 i = 0; i < infoCount; i++) { + const translation_format* formats; + int32 count; + roster->GetOutputFormats(info[i].translator, &formats, &count); + for (int32 j = 0; j < count; j++) { + if (strcmp(formats[j].MIME, "image/x-be-bitmap") != 0) { + // needed to send data in message + drag.AddString("be:types", formats[j].MIME); + // needed to pass data via file + drag.AddString("be:filetypes", formats[j].MIME); + drag.AddString("be:type_descriptions", formats[j].name); + } + } + } + } + stream.DetachBitmap(&bitmap); + + // we also support "Passing Data via File" protocol + drag.AddString("be:types", B_FILE_MIME_TYPE); + + sourcePoint -= fPictureRect.LeftTop(); + + SetMouseEventMask(B_POINTER_EVENTS); + + DragMessage(&drag, bitmap, B_OP_ALPHA, sourcePoint); + bitmap = NULL; +} + + +void +PictureView::_HandleDrop(BMessage* message) +{ + // TODO + printf("PictureView::_HandleDrop(): \n"); + message->PrintToStream(); +} + + +void +PictureView::_DeletePicture() +{ + if (fPicture == NULL) + return; + + if (fPicture != fOriginalPicture) + delete fPicture; + + fPicture = NULL; + Invalidate(); +} + + diff --git a/src/apps/people/PictureView.h b/src/apps/people/PictureView.h index 73c7f51c23..63c6d86d0a 100644 --- a/src/apps/people/PictureView.h +++ b/src/apps/people/PictureView.h @@ -16,6 +16,9 @@ class BBitmap; +const uint32 kMsgLoadImage = 'mLIM'; + + class PictureView : public BView { public: PictureView(float width, float height, @@ -30,11 +33,24 @@ public: virtual void MessageReceived(BMessage* message); virtual void Draw(BRect updateRect); + virtual void MouseDown(BPoint point); + virtual void KeyDown(const char* bytes, int32 numBytes); + virtual void WindowActivated(bool active); + virtual void MakeFocus(bool focused); private: + void _BeginDrag(BPoint sourcePoint); + void _HandleDrop(BMessage* message); + void _ShowPopUpMenu(BPoint screen); + BBitmap* _CopyPicture(uint8 alpha); + + void _DeletePicture(); + BBitmap* fPicture; BBitmap* fOriginalPicture; BBitmap* fDefaultPicture; + bool fShowingPopUpMenu; + BRect fPictureRect; }; #endif