1 #include "BitmapView.h"
3 #include <BitmapStream.h>
8 #include <TranslationUtils.h>
9 #include <TranslatorRoster.h>
10 #include <TranslatorFormats.h>
12 // TODO: Add support for labels
14 #define M_REMOVE_IMAGE 'mrmi'
15 #define M_PASTE_IMAGE 'mpsi'
26 inline void SetRGBColor(rgb_color
*col
, uint8 r
, uint8 g
, uint8 b
, uint8 a
= 255);
30 SetRGBColor(rgb_color
*col
, uint8 r
, uint8 g
, uint8 b
, uint8 a
)
41 BitmapView::BitmapView(BRect frame
, const char *name
, BMessage
*mod
, BBitmap
*bitmap
,
42 const char *label
, border_style borderstyle
, int32 resize
, int32 flags
)
43 : BView(frame
, name
, resize
, flags
)
45 SetViewUIColor(B_PANEL_BACKGROUND_COLOR
);
47 if (bitmap
&& bitmap
->IsValid())
56 fBorderStyle
= borderstyle
;
59 fRemovableBitmap
= false;
62 fConstrainDrops
= true;
66 fPopUpMenu
= new BPopUpMenu("deletepopup", false, false);
67 fPopUpMenu
->AddItem(new BMenuItem("Close This Menu", new BMessage(B_CANCEL
)));
68 fPopUpMenu
->AddSeparatorItem();
70 fPasteItem
= new BMenuItem("Paste Photo from Clipboard", new BMessage(M_PASTE_IMAGE
));
71 fPopUpMenu
->AddItem(fPasteItem
);
73 fPopUpMenu
->AddSeparatorItem();
75 fRemoveItem
= new BMenuItem("Remove Photo", new BMessage(M_REMOVE_IMAGE
));
76 fPopUpMenu
->AddItem(fRemoveItem
);
78 CalculateBitmapRect();
80 // Calculate the offsets for each of the words -- the phrase will be center justified
81 fNoPhotoWidths
[0] = StringWidth("Drop");
82 fNoPhotoWidths
[1] = StringWidth("a");
83 fNoPhotoWidths
[2] = StringWidth("Photo");
84 fNoPhotoWidths
[3] = StringWidth("Here");
88 float totalheight
= fh
.ascent
+ fh
.descent
+ fh
.leading
;
89 float yoffset
= (Bounds().Height() - 10 - (totalheight
* 4)) / 2;
90 fNoPhotoOffsets
[0].Set((Bounds().Width() - fNoPhotoWidths
[0]) / 2, totalheight
+ yoffset
);
91 fNoPhotoOffsets
[1].Set((Bounds().Width() - fNoPhotoWidths
[1]) / 2,
92 fNoPhotoOffsets
[0].y
+ totalheight
);
93 fNoPhotoOffsets
[2].Set((Bounds().Width() - fNoPhotoWidths
[2]) / 2,
94 fNoPhotoOffsets
[1].y
+ totalheight
);
95 fNoPhotoOffsets
[3].Set((Bounds().Width() - fNoPhotoWidths
[3]) / 2,
96 fNoPhotoOffsets
[2].y
+ totalheight
);
100 BitmapView::~BitmapView(void)
107 BitmapView::AttachedToWindow(void)
109 SetTarget((BHandler
*)Window());
110 fPopUpMenu
->SetTargetForItems(this);
115 BitmapView::SetBitmap(BBitmap
*bitmap
)
117 if (bitmap
&& bitmap
->IsValid()) {
118 if (fBitmap
== bitmap
)
127 CalculateBitmapRect();
134 BitmapView::SetEnabled(bool value
)
136 if (fEnabled
!= value
) {
145 BitmapView::SetLabel(const char *label)
147 if (fLabel.Compare(label) != 0) {
150 CalculateBitmapRect();
159 BitmapView::SetStyle(border_style style
)
161 if (fBorderStyle
!= style
) {
162 fBorderStyle
= style
;
164 CalculateBitmapRect();
172 BitmapView::SetFixedSize(bool isfixed
)
174 if (fFixedSize
!= isfixed
) {
175 fFixedSize
= isfixed
;
177 CalculateBitmapRect();
185 BitmapView::MessageReceived(BMessage
*msg
)
187 if (msg
->WasDropped() && AcceptsDrops()) {
188 // We'll handle two types of drops: those from Tracker and those from ShowImage
189 if (msg
->what
== B_SIMPLE_DATA
) {
191 if (msg
->FindInt32("be:actions", &actions
) == B_OK
) {
192 // ShowImage drop. This is a negotiated drag&drop, so send a reply
193 BMessage
reply(B_COPY_TARGET
), response
;
194 reply
.AddString("be:types", "image/jpeg");
195 reply
.AddString("be:types", "image/png");
197 msg
->SendReply(&reply
, &response
);
199 // now, we've gotten the response
200 if (response
.what
== B_MIME_DATA
) {
201 // Obtain and translate the received data
206 if (response
.FindData("image/jpeg", B_MIME_DATA
,
207 (const void **)&imagedata
, &datasize
) != B_OK
) {
208 // Try PNG next and piddle out if unsuccessful
209 if (response
.FindData("image/png", B_PNG_FORMAT
,
210 (const void **)&imagedata
, &datasize
) != B_OK
)
214 // Set up to decode into memory
215 BMemoryIO
memio(imagedata
, datasize
);
216 BTranslatorRoster
*roster
= BTranslatorRoster::Default();
217 BBitmapStream bstream
;
219 if (roster
->Translate(&memio
, NULL
, NULL
, &bstream
, B_TRANSLATOR_BITMAP
) == B_OK
)
222 if (bstream
.DetachBitmap(&bmp
) != B_OK
)
235 if (msg
->FindRef("refs", &ref
) == B_OK
) {
237 BBitmap
*bmp
= BTranslationUtils::GetBitmap(&ref
);
252 case M_REMOVE_IMAGE
: {
253 BAlert
*alert
= new BAlert("ResEdit", "This cannot be undone. "
254 "Remove the image?", "Remove", "Cancel");
255 alert
->SetShortcut(1, B_ESCAPE
);
256 int32 value
= alert
->Go();
261 BMessenger
msgr(Target());
263 msgr
.SendMessage(new BMessage(M_BITMAP_REMOVED
));
274 BView::MessageReceived(msg
);
279 BitmapView::Draw(BRect rect
)
282 DrawBitmap(fBitmap
, fBitmap
->Bounds(), fBitmapRect
);
284 SetHighColor(0, 0, 0, 80);
285 SetDrawingMode(B_OP_ALPHA
);
286 DrawString("Drop", fNoPhotoOffsets
[0]);
287 DrawString("a", fNoPhotoOffsets
[1]);
288 DrawString("Photo", fNoPhotoOffsets
[2]);
289 DrawString("Here", fNoPhotoOffsets
[3]);
290 SetDrawingMode(B_OP_COPY
);
293 if (fBorderStyle
== B_FANCY_BORDER
) {
294 rgb_color base
= { 216, 216, 216, 255 };
298 StrokeRect(Bounds().InsetByCopy(2, 2));
304 work
= tint_color(base
, B_DARKEN_2_TINT
);
305 AddLine(r
.LeftTop(), r
.RightTop(), work
);
306 AddLine(r
.LeftTop(), r
.LeftBottom(), work
);
309 work
= tint_color(base
, B_DARKEN_4_TINT
);
310 AddLine(r
.RightTop(), r
.RightBottom(), work
);
311 AddLine(r
.LeftBottom(), r
.RightBottom(), work
);
318 work
= tint_color(base
, B_LIGHTEN_MAX_TINT
);
319 AddLine(r
.LeftTop(), r
.RightTop(), work
);
320 AddLine(r
.LeftTop(), r
.LeftBottom(), work
);
323 work
= tint_color(base
, B_DARKEN_3_TINT
);
324 AddLine(r
.RightTop(), r
.RightBottom(), work
);
325 AddLine(r
.LeftBottom(), r
.RightBottom(), work
);
327 // this rect handled by the above StrokeRect, so inset a total of 2 pixels
334 work
= tint_color(base
, B_DARKEN_3_TINT
);
335 AddLine(r
.LeftTop(), r
.RightTop(), work
);
336 AddLine(r
.LeftTop(), r
.LeftBottom(), work
);
339 work
= tint_color(base
, B_LIGHTEN_MAX_TINT
);
340 AddLine(r
.RightTop(), r
.RightBottom(), work
);
341 AddLine(r
.LeftBottom(), r
.RightBottom(), work
);
348 SetHighColor(tint_color(base
, B_DARKEN_2_TINT
));
352 SetHighColor(0, 0, 0);
353 StrokeRect(fBitmapRect
);
359 BitmapView::MouseDown(BPoint pt
)
364 GetMouse(&mousept
, &buttons
);
365 if (buttons
& B_SECONDARY_MOUSE_BUTTON
) {
366 ConvertToScreen(&mousept
);
368 mousept
.x
= (mousept
.x
>5) ? mousept
.x
-5 : 0;
369 mousept
.y
= (mousept
.y
>5) ? mousept
.y
-5 : 0;
371 if (AcceptsPaste() && ClipboardHasBitmap())
372 fPasteItem
->SetEnabled(true);
374 fPasteItem
->SetEnabled(false);
376 if (fRemovableBitmap
&& fBitmap
)
377 fRemoveItem
->SetEnabled(true);
379 fRemoveItem
->SetEnabled(false);
381 fPopUpMenu
->Go(mousept
, true, true, true);
387 BitmapView::FrameResized(float w
, float h
)
389 CalculateBitmapRect();
394 BitmapView::CalculateBitmapRect(void)
396 if (!fBitmap
|| fFixedSize
) {
397 fBitmapRect
= Bounds().InsetByCopy(1, 1);
401 uint8 borderwidth
= (fBorderStyle
== B_FANCY_BORDER
) ? 5 : 1;
404 fBitmapRect
= ScaleRectToFit(fBitmap
->Bounds(), r
.InsetByCopy(borderwidth
, borderwidth
));
409 BitmapView::SetAcceptDrops(bool accept
)
411 fAcceptDrops
= accept
;
416 BitmapView::SetAcceptPaste(bool accept
)
418 fAcceptPaste
= accept
;
423 BitmapView::SetConstrainDrops(bool value
)
425 fConstrainDrops
= value
;
430 BitmapView::MaxBitmapSize(float *width
, float *height
) const
433 *height
= fMaxHeight
;
438 BitmapView::SetMaxBitmapSize(const float &width
, const float &height
)
448 BitmapView::SetBitmapRemovable(bool isremovable
)
450 fRemovableBitmap
= isremovable
;
455 BitmapView::ConstrainBitmap(void)
457 if (!fBitmap
|| fMaxWidth
< 1 || fMaxHeight
< 1)
460 BRect r
= ScaleRectToFit(fBitmap
->Bounds(), BRect(0, 0, fMaxWidth
- 1, fMaxHeight
- 1));
463 BBitmap
*scaled
= new BBitmap(r
, fBitmap
->ColorSpace(), true);
464 BView
*view
= new BView(r
, "drawview", 0, 0);
467 scaled
->AddChild(view
);
468 view
->DrawBitmap(fBitmap
, fBitmap
->Bounds(), scaled
->Bounds());
472 fBitmap
= new BBitmap(scaled
, false);
477 BitmapView::ClipboardHasBitmap(void)
479 BMessage
*clip
= NULL
, flattened
;
480 uint8 clipval
= CLIP_NONE
;
483 if (be_clipboard
->Lock()) {
484 clip
= be_clipboard
->Data();
485 if (!clip
->IsEmpty()) {
486 returnval
= (clip
->FindMessage("image/bitmap", &flattened
) == B_OK
);
491 returnval
= (clip
->FindString("class", &string
) == B_OK
&& string
== "BBitmap");
493 // Try method Gobe Productive uses if that, too, didn't work
495 clipval
= CLIP_SHOWIMAGE
;
497 returnval
= (clip
->FindMessage("image/x-vnd.Be-bitmap", &flattened
) == B_OK
);
499 clipval
= CLIP_SHOWIMAGE
;
505 be_clipboard
->Unlock();
507 return (clipval
!= CLIP_NONE
)?true:false;
512 BitmapView::BitmapFromClipboard(void)
514 BMessage
*clip
= NULL
, flattened
;
517 if (!be_clipboard
->Lock())
520 clip
= be_clipboard
->Data();
524 uint8 clipval
= CLIP_NONE
;
526 // Try ArtPaint-style storage
527 status_t status
= clip
->FindMessage("image/bitmap", &flattened
);
529 // If that didn't work, try ShowImage-style
530 if (status
!= B_OK
) {
532 status
= clip
->FindString("class", &string
);
534 // Try method Gobe Productive uses if that, too, didn't work
535 if (status
== B_OK
&& string
== "BBitmap")
536 clipval
= CLIP_SHOWIMAGE
;
538 status
= clip
->FindMessage("image/x-vnd.Be-bitmap", &flattened
);
540 clipval
= CLIP_PRODUCTIVE
;
548 be_clipboard
->Unlock();
551 case CLIP_SHOWIMAGE
: {
552 // Showimage does it a slightly different way -- it dumps the BBitmap
553 // data directly to the clipboard message instead of packaging it in
554 // a bitmap like everyone else.
556 if (!be_clipboard
->Lock())
559 BMessage
datamsg(*be_clipboard
->Data());
561 be_clipboard
->Unlock();
564 ssize_t bufferLength
;
567 color_space cspace
= B_NO_COLOR_SPACE
;
569 status
= datamsg
.FindRect("_frame", &frame
);
573 status
= datamsg
.FindInt32("_cspace", (int32
)cspace
);
577 bitmap
= new BBitmap(frame
, cspace
, true);
579 status
= datamsg
.FindData("_data", B_RAW_TYPE
, (const void **)&buffer
, &bufferLength
);
580 if (status
!= B_OK
) {
585 memcpy(bitmap
->Bits(), buffer
, bufferLength
);
588 case CLIP_PRODUCTIVE
:
589 // Productive doesn't name the packaged BBitmap data message the same, but
590 // uses exactly the same data format.
594 ssize_t bufferLength
;
597 color_space cspace
= B_NO_COLOR_SPACE
;
599 status
= flattened
.FindRect("_frame", &frame
);
603 status
= flattened
.FindInt32("_cspace", (int32
)cspace
);
607 bitmap
= new BBitmap(frame
, cspace
, true);
609 status
= flattened
.FindData("_data", B_RAW_TYPE
, (const void **)&buffer
, &bufferLength
);
610 if (status
!= B_OK
) {
615 memcpy(bitmap
->Bits(), buffer
, bufferLength
);
622 // shut the compiler up
628 ScaleRectToFit(const BRect
&from
, const BRect
&to
)
630 // Dynamic sizing algorithm
631 // 1) Check to see if either dimension is bigger than the view's display area
632 // 2) If smaller along both axes, make bitmap rect centered and return
633 // 3) Check to see if scaling is to be horizontal or vertical on basis of longer axis
634 // 4) Calculate scaling factor
635 // 5) Scale both axes down by scaling factor, accounting for border width
636 // 6) Center the rectangle in the direction of the smaller axis
645 if ((from
.Width() <= r
.Width()) && (from
.Height() <= r
.Height())) {
646 // Smaller than view, so just center and return
648 r
.OffsetBy((to
.Width() - r
.Width()) / 2, (to
.Height() - r
.Height()) / 2);
652 float multiplier
= from
.Width()/from
.Height();
653 if (multiplier
> 1) {
654 // Landscape orientation
656 // Scale rectangle to bounds width and center height
657 r
.bottom
= r
.top
+ (r
.Width() / multiplier
);
658 r
.OffsetBy(0, (to
.Height() - r
.Height()) / 2);
660 // Portrait orientation
662 // Scale rectangle to bounds height and center width
663 r
.right
= r
.left
+ (r
.Height() * multiplier
);
664 r
.OffsetBy((to
.Width() - r
.Width()) / 2, 0);
671 BitmapView::RemoveBitmap(void)
678 BitmapView::PasteBitmap(void)
680 BBitmap
*bmp
= BitmapFromClipboard();