2 * Copyright 2011, Haiku.
3 * Distributed under the terms of the MIT License.
10 #include "PictureView.h"
18 #include <BitmapStream.h>
20 #include <Clipboard.h>
21 #include <Directory.h>
23 #include <FilePanel.h>
24 #include <IconUtils.h>
25 #include <LayoutUtils.h>
26 #include <PopUpMenu.h>
29 #include <Messenger.h>
33 #include <TranslatorRoster.h>
34 #include <TranslationUtils.h>
37 #include "PeopleApp.h" // for B_PERSON_MIMETYPE
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "People"
44 const uint32 kMsgPopUpMenuClosed
= 'pmcl';
46 class PopUpMenu
: public BPopUpMenu
{
48 PopUpMenu(const char* name
, BMessenger target
);
56 PopUpMenu::PopUpMenu(const char* name
, BMessenger target
)
58 BPopUpMenu(name
, false, false), fTarget(target
)
60 SetAsyncAutoDestruct(true);
64 PopUpMenu::~PopUpMenu()
66 fTarget
.SendMessage(kMsgPopUpMenuClosed
);
75 const float kPictureMargin
= 6.0;
77 PictureView::PictureView(float width
, float height
, const entry_ref
* ref
)
79 BView("pictureview", B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
| B_NAVIGABLE
),
81 fOriginalPicture(NULL
),
82 fDefaultPicture(NULL
),
83 fShowingPopUpMenu(false),
85 fFocusChanging(false),
86 fOpenPanel(new BFilePanel(B_OPEN_PANEL
))
88 SetViewColor(255, 255, 255);
90 SetToolTip(B_TRANSLATE(
91 "Drop an image here,\n"
92 "or use the contextual menu."));
94 BSize
size(width
+ 2 * kPictureMargin
, height
+ 2 * kPictureMargin
);
95 SetExplicitMinSize(size
);
96 SetExplicitMaxSize(size
);
98 BMimeType
mime(B_PERSON_MIMETYPE
);
101 if (mime
.GetIcon(&iconData
, &iconDataSize
) == B_OK
) {
102 float size
= width
< height
? width
: height
;
103 fDefaultPicture
= new BBitmap(BRect(0, 0, size
, size
),
105 if (fDefaultPicture
->InitCheck() != B_OK
106 || BIconUtils::GetVectorIcon(iconData
, iconDataSize
,
107 fDefaultPicture
) != B_OK
) {
108 delete fDefaultPicture
;
109 fDefaultPicture
= NULL
;
117 PictureView::~PictureView()
119 delete fDefaultPicture
;
121 if (fOriginalPicture
!= fPicture
)
122 delete fOriginalPicture
;
129 PictureView::HasChanged()
131 return fPicture
!= fOriginalPicture
;
136 PictureView::Revert()
141 _SetPicture(fOriginalPicture
);
146 PictureView::Update()
148 if (fOriginalPicture
!= fPicture
) {
149 delete fOriginalPicture
;
150 fOriginalPicture
= fPicture
;
156 PictureView::Update(const entry_ref
* ref
)
158 // Don't update when user has modified the picture
162 if (_LoadPicture(ref
) == B_OK
) {
163 delete fOriginalPicture
;
164 fOriginalPicture
= fPicture
;
170 PictureView::Bitmap()
177 PictureView::SuggestedType()
184 PictureView::SuggestedMIMEType()
186 if (fPictureMIMEType
== "")
189 return fPictureMIMEType
.String();
194 PictureView::MessageReceived(BMessage
* message
)
196 switch (message
->what
) {
197 case B_REFS_RECEIVED
:
201 if (message
->FindRef("refs", &ref
) == B_OK
202 && _LoadPicture(&ref
) == B_OK
)
205 _HandleDrop(message
);
214 _HandleDrop(message
);
219 if (be_clipboard
->Lock() != B_OK
)
222 BMessage
* data
= be_clipboard
->Data();
223 BMessage archivedBitmap
;
224 if (data
->FindMessage("image/bitmap", &archivedBitmap
) == B_OK
) {
225 BBitmap
* picture
= new(std::nothrow
) BBitmap(&archivedBitmap
);
226 _SetPicture(picture
);
229 be_clipboard
->Unlock();
239 fOpenPanel
->SetTarget(BMessenger(this));
243 case kMsgPopUpMenuClosed
:
244 fShowingPopUpMenu
= false;
248 BView::MessageReceived(message
);
255 PictureView::Draw(BRect updateRect
)
257 BRect rect
= Bounds();
259 // Draw the outer frame
260 rgb_color base
= ui_color(B_PANEL_BACKGROUND_COLOR
);
261 if (IsFocus() && Window() && Window()->IsActive())
262 SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR
));
264 SetHighColor(tint_color(base
, B_DARKEN_3_TINT
));
267 if (fFocusChanging
) {
268 // focus frame is already redraw, stop here
272 BBitmap
* picture
= fPicture
? fPicture
: fDefaultPicture
;
273 if (picture
!= NULL
) {
274 // scale to fit and center picture in frame
275 BRect frame
= rect
.InsetByCopy(kPictureMargin
, kPictureMargin
);
276 BRect srcRect
= picture
->Bounds();
277 BSize size
= frame
.Size();
278 if (srcRect
.Width() > srcRect
.Height())
279 size
.height
= srcRect
.Height() * size
.width
/ srcRect
.Width();
281 size
.width
= srcRect
.Width() * size
.height
/ srcRect
.Height();
283 fPictureRect
= BLayoutUtils::AlignInFrame(frame
, size
,
284 BAlignment(B_ALIGN_HORIZONTAL_CENTER
, B_ALIGN_VERTICAL_CENTER
));
286 SetDrawingMode(B_OP_ALPHA
);
287 if (picture
== fDefaultPicture
) {
288 SetBlendingMode(B_CONSTANT_ALPHA
, B_ALPHA_OVERLAY
);
289 SetHighColor(0, 0, 0, 24);
292 DrawBitmapAsync(picture
, srcRect
, fPictureRect
,
293 B_FILTER_BITMAP_BILINEAR
);
295 SetDrawingMode(B_OP_OVER
);
301 PictureView::WindowActivated(bool active
)
303 BView::WindowActivated(active
);
311 PictureView::MakeFocus(bool focused
)
313 if (focused
== IsFocus())
316 BView::MakeFocus(focused
);
319 fFocusChanging
= true;
322 fFocusChanging
= false;
328 PictureView::MouseDown(BPoint position
)
333 if (Window() != NULL
&& Window()->CurrentMessage() != NULL
)
334 buttons
= Window()->CurrentMessage()->FindInt32("buttons");
336 if (fPicture
!= NULL
&& fPictureRect
.Contains(position
)
338 & (B_PRIMARY_MOUSE_BUTTON
| B_SECONDARY_MOUSE_BUTTON
)) != 0) {
340 _BeginDrag(position
);
342 } else if (buttons
== B_SECONDARY_MOUSE_BUTTON
)
343 _ShowPopUpMenu(ConvertToScreen(position
));
348 PictureView::KeyDown(const char* bytes
, int32 numBytes
)
351 BView::KeyDown(bytes
, numBytes
);
361 BView::KeyDown(bytes
, numBytes
);
371 PictureView::_ShowPopUpMenu(BPoint screen
)
373 if (fShowingPopUpMenu
)
376 PopUpMenu
* menu
= new PopUpMenu("PopUpMenu", this);
378 BMenuItem
* item
= new BMenuItem(B_TRANSLATE("Load image" B_UTF8_ELLIPSIS
),
379 new BMessage(kMsgLoadImage
));
382 item
= new BMenuItem(B_TRANSLATE("Remove image"), new BMessage(B_DELETE
));
383 item
->SetEnabled(fPicture
!= NULL
);
386 menu
->SetTargetForItems(this);
387 menu
->Go(screen
, true, true, true);
388 fShowingPopUpMenu
= true;
393 PictureView::_CopyPicture(uint8 alpha
)
395 bool hasAlpha
= alpha
!= 255;
400 BRect rect
= fPictureRect
.OffsetToCopy(B_ORIGIN
);
401 BView
view(rect
, NULL
, B_FOLLOW_NONE
, B_WILL_DRAW
);
402 BBitmap
* bitmap
= new(nothrow
) BBitmap(rect
, hasAlpha
? B_RGBA32
403 : fPicture
->ColorSpace(), true);
404 if (bitmap
== NULL
|| !bitmap
->IsValid()) {
409 if (bitmap
->Lock()) {
410 bitmap
->AddChild(&view
);
412 view
.SetHighColor(0, 0, 0, 0);
414 view
.SetDrawingMode(B_OP_ALPHA
);
415 view
.SetBlendingMode(B_CONSTANT_ALPHA
, B_ALPHA_COMPOSITE
);
416 view
.SetHighColor(0, 0, 0, alpha
);
418 view
.DrawBitmap(fPicture
, fPicture
->Bounds().OffsetToCopy(B_ORIGIN
),
419 rect
, B_FILTER_BITMAP_BILINEAR
);
421 bitmap
->RemoveChild(&view
);
430 PictureView::_BeginDrag(BPoint sourcePoint
)
432 BBitmap
* bitmap
= _CopyPicture(128);
436 // fill the drag message
437 BMessage
drag(B_SIMPLE_DATA
);
438 drag
.AddInt32("be:actions", B_COPY_TARGET
);
439 drag
.AddInt32("be:actions", B_TRASH_TARGET
);
441 // name the clip after person name, if any
442 BString name
= B_TRANSLATE("%name% picture");
443 name
.ReplaceFirst("%name%", Window() ? Window()->Title() :
444 B_TRANSLATE("Unnamed person"));
445 drag
.AddString("be:clip_name", name
.String());
447 BTranslatorRoster
* roster
= BTranslatorRoster::Default();
448 if (roster
== NULL
) {
454 translator_info
* info
;
455 BBitmapStream
stream(bitmap
);
456 if (roster
->GetTranslators(&stream
, NULL
, &info
, &infoCount
) == B_OK
) {
457 for (int32 i
= 0; i
< infoCount
; i
++) {
458 const translation_format
* formats
;
460 roster
->GetOutputFormats(info
[i
].translator
, &formats
, &count
);
461 for (int32 j
= 0; j
< count
; j
++) {
462 if (strcmp(formats
[j
].MIME
, "image/x-be-bitmap") != 0) {
463 // needed to send data in message
464 drag
.AddString("be:types", formats
[j
].MIME
);
465 // needed to pass data via file
466 drag
.AddString("be:filetypes", formats
[j
].MIME
);
467 drag
.AddString("be:type_descriptions", formats
[j
].name
);
472 stream
.DetachBitmap(&bitmap
);
474 // we also support "Passing Data via File" protocol
475 drag
.AddString("be:types", B_FILE_MIME_TYPE
);
477 sourcePoint
-= fPictureRect
.LeftTop();
479 SetMouseEventMask(B_POINTER_EVENTS
);
481 DragMessage(&drag
, bitmap
, B_OP_ALPHA
, sourcePoint
);
487 PictureView::_HandleDrop(BMessage
* msg
)
491 bool saveToFile
= msg
->FindString("be:filetypes", &type
) == B_OK
492 && msg
->FindRef("directory", &dirRef
) == B_OK
493 && msg
->FindString("name", &name
) == B_OK
;
495 bool sendInMessage
= !saveToFile
496 && msg
->FindString("be:types", &type
) == B_OK
;
498 if (!sendInMessage
&& !saveToFile
)
501 BBitmap
* bitmap
= fPicture
;
505 BTranslatorRoster
* roster
= BTranslatorRoster::Default();
509 BBitmapStream
stream(bitmap
);
511 // find translation format we're asked for
512 translator_info
* outInfo
;
515 translation_format format
;
517 if (roster
->GetTranslators(&stream
, NULL
, &outInfo
, &outNumInfo
) == B_OK
) {
518 for (int32 i
= 0; i
< outNumInfo
; i
++) {
519 const translation_format
* formats
;
521 roster
->GetOutputFormats(outInfo
[i
].translator
, &formats
,
523 for (int32 j
= 0; j
< formatCount
; j
++) {
524 if (strcmp(formats
[j
].MIME
, type
.String()) == 0) {
534 stream
.DetachBitmap(&bitmap
);
540 BMessage
reply(B_MIME_DATA
);
542 if (roster
->Translate(&stream
, NULL
, NULL
, &memStream
,
543 format
.type
) == B_OK
) {
544 reply
.AddData(format
.MIME
, B_MIME_TYPE
, memStream
.Buffer(),
545 memStream
.BufferLength());
546 msg
->SendReply(&reply
);
551 BDirectory
dir(&dirRef
);
552 BFile
file(&dir
, name
.String(), B_WRITE_ONLY
| B_CREATE_FILE
555 if (file
.InitCheck() == B_OK
556 && roster
->Translate(&stream
, NULL
, NULL
, &file
,
557 format
.type
) == B_OK
) {
558 BNodeInfo
nodeInfo(&file
);
559 if (nodeInfo
.InitCheck() == B_OK
)
560 nodeInfo
.SetType(type
.String());
562 BString text
= B_TRANSLATE("The file '%name%' could not "
564 text
.ReplaceFirst("%name%", name
);
565 BAlert
* alert
= new BAlert(B_TRANSLATE("Error"), text
.String(),
566 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
567 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
572 // Detach, as we don't want our fPicture to be deleted
573 stream
.DetachBitmap(&bitmap
);
578 PictureView::_LoadPicture(const entry_ref
* ref
)
581 status_t status
= file
.SetTo(ref
, B_READ_ONLY
);
586 status
= file
.GetSize(&fileSize
);
590 // Check that we've at least some data to translate...
594 translator_info info
;
595 memset(&info
, 0, sizeof(translator_info
));
596 BMessage ioExtension
;
598 BTranslatorRoster
* roster
= BTranslatorRoster::Default();
602 status
= roster
->Identify(&file
, &ioExtension
, &info
, 0, NULL
,
603 B_TRANSLATOR_BITMAP
);
605 BBitmapStream stream
;
607 if (status
== B_OK
) {
608 status
= roster
->Translate(&file
, &info
, &ioExtension
, &stream
,
609 B_TRANSLATOR_BITMAP
);
614 BBitmap
* picture
= NULL
;
615 if (stream
.DetachBitmap(&picture
) != B_OK
619 // Remember image format so we could store using the same
620 fPictureMIMEType
= info
.MIME
;
621 fPictureType
= info
.type
;
623 _SetPicture(picture
);
629 PictureView::_SetPicture(BBitmap
* picture
)
631 if (fPicture
!= fOriginalPicture
)
636 if (picture
== NULL
) {
638 fPictureMIMEType
= "";