* 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
This commit is contained in:
Philippe Houdoin 2011-02-24 17:48:21 +00:00
parent b2435cc021
commit bb0416a246
2 changed files with 298 additions and 28 deletions

View File

@ -10,24 +10,68 @@
#include "PictureView.h"
#include <math.h>
#include <new>
#include <stdio.h>
#include <Bitmap.h>
#include <BitmapStream.h>
#include <Catalog.h>
#include <IconUtils.h>
#include <LayoutUtils.h>
#include <PopUpMenu.h>
#include <MenuItem.h>
#include <MimeType.h>
#include <TranslatorRoster.h>
#include <TranslationUtils.h>
#include <Window.h>
#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();
}

View File

@ -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