BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / resedit / BitmapView.cpp
blob1b781743aa10fd87bc74072b5a77841dd8c166ba
1 #include "BitmapView.h"
2 #include <Alert.h>
3 #include <BitmapStream.h>
4 #include <Clipboard.h>
5 #include <Font.h>
6 #include <MenuItem.h>
7 #include <Entry.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'
17 enum
19 CLIP_NONE = 0,
20 CLIP_BEOS = 1,
21 CLIP_SHOWIMAGE = 2,
22 CLIP_PRODUCTIVE = 3
26 inline void SetRGBColor(rgb_color *col, uint8 r, uint8 g, uint8 b, uint8 a = 255);
29 void
30 SetRGBColor(rgb_color *col, uint8 r, uint8 g, uint8 b, uint8 a)
32 if (col) {
33 col->red = r;
34 col->green = g;
35 col->blue = b;
36 col->alpha = 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())
48 fBitmap = bitmap;
49 else
50 fBitmap = NULL;
52 if (mod)
53 SetMessage(mod);
55 fLabel = label;
56 fBorderStyle = borderstyle;
57 fFixedSize = false;
58 fEnabled = true;
59 fRemovableBitmap = false;
60 fAcceptDrops = true;
61 fAcceptPaste = true;
62 fConstrainDrops = true;
63 fMaxWidth = 100;
64 fMaxHeight = 100;
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");
86 font_height fh;
87 GetFontHeight(&fh);
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)
102 delete fPopUpMenu;
106 void
107 BitmapView::AttachedToWindow(void)
109 SetTarget((BHandler*)Window());
110 fPopUpMenu->SetTargetForItems(this);
114 void
115 BitmapView::SetBitmap(BBitmap *bitmap)
117 if (bitmap && bitmap->IsValid()) {
118 if (fBitmap == bitmap)
119 return;
120 fBitmap = bitmap;
121 } else {
122 if (!fBitmap)
123 return;
124 fBitmap = NULL;
127 CalculateBitmapRect();
128 if (!IsHidden())
129 Invalidate();
133 void
134 BitmapView::SetEnabled(bool value)
136 if (fEnabled != value) {
137 fEnabled = value;
138 Invalidate();
144 void
145 BitmapView::SetLabel(const char *label)
147 if (fLabel.Compare(label) != 0) {
148 fLabel = label;
150 CalculateBitmapRect();
151 if (!IsHidden())
152 Invalidate();
158 void
159 BitmapView::SetStyle(border_style style)
161 if (fBorderStyle != style) {
162 fBorderStyle = style;
164 CalculateBitmapRect();
165 if (!IsHidden())
166 Invalidate();
171 void
172 BitmapView::SetFixedSize(bool isfixed)
174 if (fFixedSize != isfixed) {
175 fFixedSize = isfixed;
177 CalculateBitmapRect();
178 if (!IsHidden())
179 Invalidate();
184 void
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) {
190 int32 actions;
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
202 uint8 *imagedata;
203 ssize_t datasize;
205 // Try JPEG first
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)
211 return;
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)
221 BBitmap *bmp;
222 if (bstream.DetachBitmap(&bmp) != B_OK)
223 return;
224 SetBitmap(bmp);
226 if (fConstrainDrops)
227 ConstrainBitmap();
228 Invoke();
231 return;
234 entry_ref ref;
235 if (msg->FindRef("refs", &ref) == B_OK) {
236 // Tracker drop
237 BBitmap *bmp = BTranslationUtils::GetBitmap(&ref);
238 if (bmp) {
239 SetBitmap(bmp);
241 if (fConstrainDrops)
242 ConstrainBitmap();
243 Invoke();
247 return;
250 switch (msg->what)
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();
257 if (value == 0) {
258 SetBitmap(NULL);
260 if (Target()) {
261 BMessenger msgr(Target());
263 msgr.SendMessage(new BMessage(M_BITMAP_REMOVED));
264 return;
268 case M_PASTE_IMAGE:
270 PasteBitmap();
271 Invoke();
274 BView::MessageReceived(msg);
278 void
279 BitmapView::Draw(BRect rect)
281 if (fBitmap)
282 DrawBitmap(fBitmap, fBitmap->Bounds(), fBitmapRect);
283 else {
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 };
295 rgb_color work;
297 SetHighColor(base);
298 StrokeRect(Bounds().InsetByCopy(2, 2));
300 BeginLineArray(12);
302 BRect r(Bounds());
304 work = tint_color(base, B_DARKEN_2_TINT);
305 AddLine(r.LeftTop(), r.RightTop(), work);
306 AddLine(r.LeftTop(), r.LeftBottom(), work);
307 r.left++;
309 work = tint_color(base, B_DARKEN_4_TINT);
310 AddLine(r.RightTop(), r.RightBottom(), work);
311 AddLine(r.LeftBottom(), r.RightBottom(), work);
313 r.right--;
314 r.top++;
315 r.bottom--;
318 work = tint_color(base, B_LIGHTEN_MAX_TINT);
319 AddLine(r.LeftTop(), r.RightTop(), work);
320 AddLine(r.LeftTop(), r.LeftBottom(), work);
321 r.left++;
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
328 r.left++;
329 r.right -= 2;
330 r.top += 2;
331 r.bottom -= 2;
334 work = tint_color(base, B_DARKEN_3_TINT);
335 AddLine(r.LeftTop(), r.RightTop(), work);
336 AddLine(r.LeftTop(), r.LeftBottom(), work);
337 r.left++;
339 work = tint_color(base, B_LIGHTEN_MAX_TINT);
340 AddLine(r.RightTop(), r.RightBottom(), work);
341 AddLine(r.LeftBottom(), r.RightBottom(), work);
343 r.right--;
344 r.top++;
345 r.bottom--;
346 EndLineArray();
348 SetHighColor(tint_color(base, B_DARKEN_2_TINT));
349 StrokeRect(r);
350 } else {
351 // Plain border
352 SetHighColor(0, 0, 0);
353 StrokeRect(fBitmapRect);
358 void
359 BitmapView::MouseDown(BPoint pt)
361 BPoint mousept;
362 uint32 buttons;
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);
373 else
374 fPasteItem->SetEnabled(false);
376 if (fRemovableBitmap && fBitmap)
377 fRemoveItem->SetEnabled(true);
378 else
379 fRemoveItem->SetEnabled(false);
381 fPopUpMenu->Go(mousept, true, true, true);
386 void
387 BitmapView::FrameResized(float w, float h)
389 CalculateBitmapRect();
393 void
394 BitmapView::CalculateBitmapRect(void)
396 if (!fBitmap || fFixedSize) {
397 fBitmapRect = Bounds().InsetByCopy(1, 1);
398 return;
401 uint8 borderwidth = (fBorderStyle == B_FANCY_BORDER) ? 5 : 1;
403 BRect r(Bounds());
404 fBitmapRect= ScaleRectToFit(fBitmap->Bounds(), r.InsetByCopy(borderwidth, borderwidth));
408 void
409 BitmapView::SetAcceptDrops(bool accept)
411 fAcceptDrops = accept;
415 void
416 BitmapView::SetAcceptPaste(bool accept)
418 fAcceptPaste = accept;
422 void
423 BitmapView::SetConstrainDrops(bool value)
425 fConstrainDrops = value;
429 void
430 BitmapView::MaxBitmapSize(float *width, float *height) const
432 *width = fMaxWidth;
433 *height = fMaxHeight;
437 void
438 BitmapView::SetMaxBitmapSize(const float &width, const float &height)
440 fMaxWidth = width;
441 fMaxHeight = height;
443 ConstrainBitmap();
447 void
448 BitmapView::SetBitmapRemovable(bool isremovable)
450 fRemovableBitmap = isremovable;
454 void
455 BitmapView::ConstrainBitmap(void)
457 if (!fBitmap || fMaxWidth < 1 || fMaxHeight < 1)
458 return;
460 BRect r = ScaleRectToFit(fBitmap->Bounds(), BRect(0, 0, fMaxWidth - 1, fMaxHeight - 1));
461 r.OffsetTo(0, 0);
463 BBitmap *scaled = new BBitmap(r, fBitmap->ColorSpace(), true);
464 BView *view = new BView(r, "drawview", 0, 0);
466 scaled->Lock();
467 scaled->AddChild(view);
468 view->DrawBitmap(fBitmap, fBitmap->Bounds(), scaled->Bounds());
469 scaled->Unlock();
471 delete fBitmap;
472 fBitmap = new BBitmap(scaled, false);
476 bool
477 BitmapView::ClipboardHasBitmap(void)
479 BMessage *clip = NULL, flattened;
480 uint8 clipval = CLIP_NONE;
481 bool returnval;
483 if (be_clipboard->Lock()) {
484 clip = be_clipboard->Data();
485 if (!clip->IsEmpty()) {
486 returnval = (clip->FindMessage("image/bitmap", &flattened) == B_OK);
487 if (returnval)
488 clipval = CLIP_BEOS;
489 else {
490 BString string;
491 returnval = (clip->FindString("class", &string) == B_OK && string == "BBitmap");
493 // Try method Gobe Productive uses if that, too, didn't work
494 if (returnval)
495 clipval = CLIP_SHOWIMAGE;
496 else {
497 returnval = (clip->FindMessage("image/x-vnd.Be-bitmap", &flattened) == B_OK);
498 if (returnval)
499 clipval = CLIP_SHOWIMAGE;
500 else
501 clipval = CLIP_NONE;
505 be_clipboard->Unlock();
507 return (clipval != CLIP_NONE)?true:false;
511 BBitmap *
512 BitmapView::BitmapFromClipboard(void)
514 BMessage *clip = NULL, flattened;
515 BBitmap *bitmap;
517 if (!be_clipboard->Lock())
518 return NULL;
520 clip = be_clipboard->Data();
521 if (!clip)
522 return NULL;
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) {
531 BString string;
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;
537 else {
538 status = clip->FindMessage("image/x-vnd.Be-bitmap", &flattened);
539 if (status == B_OK)
540 clipval = CLIP_PRODUCTIVE;
541 else
542 clipval = CLIP_NONE;
545 else
546 clipval = CLIP_BEOS;
548 be_clipboard->Unlock();
550 switch (clipval) {
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())
557 return NULL;
559 BMessage datamsg(*be_clipboard->Data());
561 be_clipboard->Unlock();
563 const void *buffer;
564 ssize_t bufferLength;
566 BRect frame;
567 color_space cspace = B_NO_COLOR_SPACE;
569 status = datamsg.FindRect("_frame", &frame);
570 if (status != B_OK)
571 return NULL;
573 status = datamsg.FindInt32("_cspace", (int32)cspace);
574 if (status != B_OK)
575 return NULL;
576 cspace = B_RGBA32;
577 bitmap = new BBitmap(frame, cspace, true);
579 status = datamsg.FindData("_data", B_RAW_TYPE, (const void **)&buffer, &bufferLength);
580 if (status != B_OK) {
581 delete bitmap;
582 return NULL;
585 memcpy(bitmap->Bits(), buffer, bufferLength);
586 return bitmap;
588 case CLIP_PRODUCTIVE:
589 // Productive doesn't name the packaged BBitmap data message the same, but
590 // uses exactly the same data format.
592 case CLIP_BEOS: {
593 const void *buffer;
594 ssize_t bufferLength;
596 BRect frame;
597 color_space cspace = B_NO_COLOR_SPACE;
599 status = flattened.FindRect("_frame", &frame);
600 if (status != B_OK)
601 return NULL;
603 status = flattened.FindInt32("_cspace", (int32)cspace);
604 if (status != B_OK)
605 return NULL;
606 cspace = B_RGBA32;
607 bitmap = new BBitmap(frame, cspace, true);
609 status = flattened.FindData("_data", B_RAW_TYPE, (const void **)&buffer, &bufferLength);
610 if (status != B_OK) {
611 delete bitmap;
612 return NULL;
615 memcpy(bitmap->Bits(), buffer, bufferLength);
616 return bitmap;
618 default:
619 return NULL;
622 // shut the compiler up
623 return NULL;
627 BRect
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
638 if (!to.IsValid())
639 return from;
640 if (!from.IsValid())
641 return to;
643 BRect r(to);
645 if ((from.Width() <= r.Width()) && (from.Height() <= r.Height())) {
646 // Smaller than view, so just center and return
647 r = from;
648 r.OffsetBy((to.Width() - r.Width()) / 2, (to.Height() - r.Height()) / 2);
649 return r;
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);
659 } else {
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);
666 return r;
670 void
671 BitmapView::RemoveBitmap(void)
673 SetBitmap(NULL);
677 void
678 BitmapView::PasteBitmap(void)
680 BBitmap *bmp = BitmapFromClipboard();
681 if (bmp)
682 SetBitmap(bmp);
684 if (fConstrainDrops)
685 ConstrainBitmap();