BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / showimage / ShowImageView.cpp
blob3279972f5a07bc70e7c010b4a524a95cb90d22a6
1 /*
2 * Copyright 2003-2011, Haiku, Inc. All Rights Reserved.
3 * Copyright 2004-2005 yellowTAB GmbH. All Rights Reserverd.
4 * Copyright 2006 Bernd Korz. All Rights Reserved
5 * Distributed under the terms of the MIT License.
7 * Authors:
8 * Fernando Francisco de Oliveira
9 * Michael Wilber
10 * Michael Pfeiffer
11 * Ryan Leavengood
12 * yellowTAB GmbH
13 * Bernd Korz
14 * Stephan Aßmus <superstippi@gmx.de>
15 * Axel Dörfler, axeld@pinc-software.de
19 #include "ShowImageView.h"
21 #include <math.h>
22 #include <new>
23 #include <stdio.h>
25 #include <Alert.h>
26 #include <Application.h>
27 #include <Bitmap.h>
28 #include <BitmapStream.h>
29 #include <Catalog.h>
30 #include <Clipboard.h>
31 #include <Cursor.h>
32 #include <Debug.h>
33 #include <Directory.h>
34 #include <Entry.h>
35 #include <File.h>
36 #include <Locale.h>
37 #include <MenuBar.h>
38 #include <MenuItem.h>
39 #include <Message.h>
40 #include <NodeInfo.h>
41 #include <Path.h>
42 #include <PopUpMenu.h>
43 #include <Rect.h>
44 #include <Region.h>
45 #include <Roster.h>
46 #include <Screen.h>
47 #include <ScrollBar.h>
48 #include <StopWatch.h>
49 #include <SupportDefs.h>
50 #include <TranslatorRoster.h>
52 #include <tracker_private.h>
54 #include "ImageCache.h"
55 #include "ShowImageApp.h"
56 #include "ShowImageWindow.h"
59 using std::nothrow;
62 class PopUpMenu : public BPopUpMenu {
63 public:
64 PopUpMenu(const char* name, BMessenger target);
65 virtual ~PopUpMenu();
67 private:
68 BMessenger fTarget;
72 // the delay time for hiding the cursor in 1/10 seconds (the pulse rate)
73 #define HIDE_CURSOR_DELAY_TIME 20
74 #define STICKY_ZOOM_DELAY_TIME 5
75 #define SHOW_IMAGE_ORIENTATION_ATTRIBUTE "ShowImage:orientation"
78 const rgb_color kBorderColor = { 0, 0, 0, 255 };
80 enum ShowImageView::image_orientation
81 ShowImageView::fTransformation[ImageProcessor::kNumberOfAffineTransformations]
82 [kNumberOfOrientations] = {
83 // rotate 90°
84 {k90, k180, k270, k0, k270V, k0V, k90V, k0H},
85 // rotate -90°
86 {k270, k0, k90, k180, k90V, k0H, k270V, k0V},
87 // mirror vertical
88 {k0H, k270V, k0V, k90V, k180, k270, k0, k90},
89 // mirror horizontal
90 {k0V, k90V, k0H, k270V, k0, k90, k180, k270}
93 const rgb_color kAlphaLow = (rgb_color) { 0xbb, 0xbb, 0xbb, 0xff };
94 const rgb_color kAlphaHigh = (rgb_color) { 0xe0, 0xe0, 0xe0, 0xff };
96 const uint32 kMsgPopUpMenuClosed = 'pmcl';
99 inline void
100 blend_colors(uint8* d, uint8 r, uint8 g, uint8 b, uint8 a)
102 d[0] = ((b - d[0]) * a + (d[0] << 8)) >> 8;
103 d[1] = ((g - d[1]) * a + (d[1] << 8)) >> 8;
104 d[2] = ((r - d[2]) * a + (d[2] << 8)) >> 8;
108 BBitmap*
109 compose_checker_background(const BBitmap* bitmap)
111 BBitmap* result = new (nothrow) BBitmap(bitmap);
112 if (result && !result->IsValid()) {
113 delete result;
114 result = NULL;
116 if (!result)
117 return NULL;
119 uint8* bits = (uint8*)result->Bits();
120 uint32 bpr = result->BytesPerRow();
121 uint32 width = result->Bounds().IntegerWidth() + 1;
122 uint32 height = result->Bounds().IntegerHeight() + 1;
124 for (uint32 i = 0; i < height; i++) {
125 uint8* p = bits;
126 for (uint32 x = 0; x < width; x++) {
127 uint8 alpha = p[3];
128 if (alpha < 255) {
129 p[3] = 255;
130 alpha = 255 - alpha;
131 if (x % 10 >= 5) {
132 if (i % 10 >= 5)
133 blend_colors(p, kAlphaLow.red, kAlphaLow.green, kAlphaLow.blue, alpha);
134 else
135 blend_colors(p, kAlphaHigh.red, kAlphaHigh.green, kAlphaHigh.blue, alpha);
137 } else {
138 if (i % 10 >= 5)
139 blend_colors(p, kAlphaHigh.red, kAlphaHigh.green, kAlphaHigh.blue, alpha);
140 else
141 blend_colors(p, kAlphaLow.red, kAlphaLow.green, kAlphaLow.blue, alpha);
144 p += 4;
146 bits += bpr;
148 return result;
152 // #pragma mark -
155 PopUpMenu::PopUpMenu(const char* name, BMessenger target)
157 BPopUpMenu(name, false, false),
158 fTarget(target)
160 SetAsyncAutoDestruct(true);
164 PopUpMenu::~PopUpMenu()
166 fTarget.SendMessage(kMsgPopUpMenuClosed);
170 // #pragma mark -
173 ShowImageView::ShowImageView(BRect rect, const char* name, uint32 resizingMode,
174 uint32 flags)
176 BView(rect, name, resizingMode, flags),
177 fBitmapOwner(NULL),
178 fBitmap(NULL),
179 fDisplayBitmap(NULL),
180 fSelectionBitmap(NULL),
182 fZoom(1.0),
184 fScaleBilinear(true),
186 fBitmapLocationInView(0.0, 0.0),
188 fStretchToBounds(false),
189 fForceOriginalSize(false),
190 fHideCursor(false),
191 fScrollingBitmap(false),
192 fCreatingSelection(false),
193 fFirstPoint(0.0, 0.0),
194 fSelectionMode(false),
195 fAnimateSelection(true),
196 fHasSelection(false),
197 fShowCaption(false),
198 fShowingPopUpMenu(false),
199 fHideCursorCountDown(HIDE_CURSOR_DELAY_TIME),
200 fStickyZoomCountDown(0),
201 fIsActiveWin(true),
202 fDefaultCursor(NULL),
203 fGrabCursor(NULL)
205 ShowImageSettings* settings = my_app->Settings();
206 if (settings->Lock()) {
207 fStretchToBounds = settings->GetBool("StretchToBounds",
208 fStretchToBounds);
209 fScaleBilinear = settings->GetBool("ScaleBilinear", fScaleBilinear);
210 settings->Unlock();
213 fDefaultCursor = new BCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
214 fGrabCursor = new BCursor(B_CURSOR_ID_GRABBING);
216 SetViewColor(B_TRANSPARENT_COLOR);
217 SetHighColor(kBorderColor);
218 SetLowColor(0, 0, 0);
222 ShowImageView::~ShowImageView()
224 _DeleteBitmap();
226 delete fDefaultCursor;
227 delete fGrabCursor;
231 void
232 ShowImageView::_AnimateSelection(bool enabled)
234 fAnimateSelection = enabled;
238 void
239 ShowImageView::Pulse()
241 // animate marching ants
242 if (fHasSelection && fAnimateSelection && fIsActiveWin) {
243 fSelectionBox.Animate();
244 fSelectionBox.Draw(this, Bounds());
247 if (fHideCursor && !fHasSelection && !fShowingPopUpMenu && fIsActiveWin) {
248 if (fHideCursorCountDown == 0) {
249 // Go negative so this isn't triggered again
250 fHideCursorCountDown--;
252 BPoint mousePos;
253 uint32 buttons;
254 GetMouse(&mousePos, &buttons, false);
255 if (Bounds().Contains(mousePos))
256 be_app->ObscureCursor();
257 } else if (fHideCursorCountDown > 0)
258 fHideCursorCountDown--;
261 if (fStickyZoomCountDown > 0)
262 fStickyZoomCountDown--;
267 void
268 ShowImageView::_SendMessageToWindow(BMessage* message)
270 BMessenger target(Window());
271 target.SendMessage(message);
275 void
276 ShowImageView::_SendMessageToWindow(uint32 code)
278 BMessage message(code);
279 _SendMessageToWindow(&message);
283 //! send message to parent about new image
284 void
285 ShowImageView::_Notify()
287 BMessage msg(MSG_UPDATE_STATUS);
289 msg.AddInt32("width", fBitmap->Bounds().IntegerWidth() + 1);
290 msg.AddInt32("height", fBitmap->Bounds().IntegerHeight() + 1);
292 msg.AddInt32("colors", fBitmap->ColorSpace());
293 _SendMessageToWindow(&msg);
295 FixupScrollBars();
296 Invalidate();
300 void
301 ShowImageView::_UpdateStatusText()
303 BMessage msg(MSG_UPDATE_STATUS_TEXT);
305 if (fHasSelection) {
306 char size[50];
307 snprintf(size, sizeof(size), "(%.0fx%.0f)",
308 fSelectionBox.Bounds().Width() + 1.0,
309 fSelectionBox.Bounds().Height() + 1.0);
311 msg.AddString("status", size);
314 _SendMessageToWindow(&msg);
318 void
319 ShowImageView::_DeleteBitmap()
321 _DeleteSelectionBitmap();
323 if (fDisplayBitmap != fBitmap)
324 delete fDisplayBitmap;
325 fDisplayBitmap = NULL;
327 if (fBitmapOwner != NULL)
328 fBitmapOwner->ReleaseReference();
329 else
330 delete fBitmap;
332 fBitmapOwner = NULL;
333 fBitmap = NULL;
337 void
338 ShowImageView::_DeleteSelectionBitmap()
340 delete fSelectionBitmap;
341 fSelectionBitmap = NULL;
345 status_t
346 ShowImageView::SetImage(const BMessage* message)
348 BBitmap* bitmap;
349 entry_ref ref;
350 if (message->FindPointer("bitmap", (void**)&bitmap) != B_OK
351 || message->FindRef("ref", &ref) != B_OK || bitmap == NULL)
352 return B_ERROR;
354 BitmapOwner* bitmapOwner;
355 message->FindPointer("bitmapOwner", (void**)&bitmapOwner);
357 status_t status = SetImage(&ref, bitmap, bitmapOwner);
358 if (status == B_OK) {
359 fFormatDescription = message->FindString("type");
360 fMimeType = message->FindString("mime");
363 return status;
367 status_t
368 ShowImageView::SetImage(const entry_ref* ref, BBitmap* bitmap,
369 BitmapOwner* bitmapOwner)
371 // Delete the old one, and clear everything
372 _SetHasSelection(false);
373 fCreatingSelection = false;
374 _DeleteBitmap();
376 fBitmap = bitmap;
377 fBitmapOwner = bitmapOwner;
378 if (ref == NULL)
379 fCurrentRef.device = -1;
380 else
381 fCurrentRef = *ref;
383 if (fBitmap != NULL) {
384 // prepare the display bitmap
385 if (fBitmap->ColorSpace() == B_RGBA32)
386 fDisplayBitmap = compose_checker_background(fBitmap);
387 if (fDisplayBitmap == NULL)
388 fDisplayBitmap = fBitmap;
390 BNode node(ref);
392 // restore orientation
393 int32 orientation;
394 fImageOrientation = k0;
395 if (node.ReadAttr(SHOW_IMAGE_ORIENTATION_ATTRIBUTE, B_INT32_TYPE, 0,
396 &orientation, sizeof(orientation)) == sizeof(orientation)) {
397 orientation &= 255;
398 switch (orientation) {
399 case k0:
400 break;
401 case k90:
402 _DoImageOperation(ImageProcessor::kRotateClockwise, true);
403 break;
404 case k180:
405 _DoImageOperation(ImageProcessor::kRotateClockwise, true);
406 _DoImageOperation(ImageProcessor::kRotateClockwise, true);
407 break;
408 case k270:
409 _DoImageOperation(ImageProcessor::kRotateCounterClockwise, true);
410 break;
411 case k0V:
412 _DoImageOperation(ImageProcessor::ImageProcessor::kFlipTopToBottom, true);
413 break;
414 case k90V:
415 _DoImageOperation(ImageProcessor::kRotateClockwise, true);
416 _DoImageOperation(ImageProcessor::ImageProcessor::kFlipTopToBottom, true);
417 break;
418 case k0H:
419 _DoImageOperation(ImageProcessor::ImageProcessor::kFlipLeftToRight, true);
420 break;
421 case k270V:
422 _DoImageOperation(ImageProcessor::kRotateCounterClockwise, true);
423 _DoImageOperation(ImageProcessor::ImageProcessor::kFlipTopToBottom, true);
424 break;
429 BPath path(ref);
430 fCaption = path.Path();
431 fFormatDescription = "Bitmap";
432 fMimeType = "image/x-be-bitmap";
434 be_roster->AddToRecentDocuments(ref, kApplicationSignature);
436 FitToBounds();
437 _Notify();
438 return B_OK;
442 BPoint
443 ShowImageView::ImageToView(BPoint p) const
445 p.x = floorf(fZoom * p.x + fBitmapLocationInView.x);
446 p.y = floorf(fZoom * p.y + fBitmapLocationInView.y);
447 return p;
451 BPoint
452 ShowImageView::ViewToImage(BPoint p) const
454 p.x = floorf((p.x - fBitmapLocationInView.x) / fZoom);
455 p.y = floorf((p.y - fBitmapLocationInView.y) / fZoom);
456 return p;
460 BRect
461 ShowImageView::ImageToView(BRect r) const
463 BPoint leftTop(ImageToView(BPoint(r.left, r.top)));
464 BPoint rightBottom(r.right, r.bottom);
465 rightBottom += BPoint(1, 1);
466 rightBottom = ImageToView(rightBottom);
467 rightBottom -= BPoint(1, 1);
468 return BRect(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y);
472 void
473 ShowImageView::ConstrainToImage(BPoint& point) const
475 point.ConstrainTo(fBitmap->Bounds());
479 void
480 ShowImageView::ConstrainToImage(BRect& rect) const
482 rect = rect & fBitmap->Bounds();
486 void
487 ShowImageView::SetShowCaption(bool show)
489 if (fShowCaption != show) {
490 fShowCaption = show;
491 _UpdateCaption();
496 void
497 ShowImageView::SetStretchToBounds(bool enable)
499 if (fStretchToBounds != enable) {
500 _SettingsSetBool("StretchToBounds", enable);
501 fStretchToBounds = enable;
502 if (enable || fZoom > 1.0)
503 FitToBounds();
508 void
509 ShowImageView::SetHideIdlingCursor(bool hide)
511 fHideCursor = hide;
515 BBitmap*
516 ShowImageView::Bitmap()
518 return fBitmap;
522 void
523 ShowImageView::SetScaleBilinear(bool enabled)
525 if (fScaleBilinear != enabled) {
526 _SettingsSetBool("ScaleBilinear", enabled);
527 fScaleBilinear = enabled;
528 Invalidate();
533 void
534 ShowImageView::AttachedToWindow()
536 FitToBounds();
537 FixupScrollBars();
541 void
542 ShowImageView::FrameResized(float width, float height)
544 FixupScrollBars();
548 float
549 ShowImageView::_FitToBoundsZoom() const
551 if (fBitmap == NULL)
552 return 1.0f;
554 // the width/height of the bitmap (in pixels)
555 float bitmapWidth = fBitmap->Bounds().Width() + 1;
556 float bitmapHeight = fBitmap->Bounds().Height() + 1;
558 // the available width/height for layouting the bitmap (in pixels)
559 float width = Bounds().Width() + 1;
560 float height = Bounds().Height() + 1;
562 float zoom = width / bitmapWidth;
564 if (zoom * bitmapHeight <= height)
565 return zoom;
567 return height / bitmapHeight;
571 BRect
572 ShowImageView::_AlignBitmap()
574 BRect rect(fBitmap->Bounds());
576 // the width/height of the bitmap (in pixels)
577 float bitmapWidth = rect.Width() + 1;
578 float bitmapHeight = rect.Height() + 1;
580 // the available width/height for layouting the bitmap (in pixels)
581 float width = Bounds().Width() + 1;
582 float height = Bounds().Height() + 1;
584 if (width == 0 || height == 0)
585 return rect;
587 // zoom image
588 rect.right = floorf(bitmapWidth * fZoom) - 1;
589 rect.bottom = floorf(bitmapHeight * fZoom) - 1;
591 // update the bitmap size after the zoom
592 bitmapWidth = rect.Width() + 1.0;
593 bitmapHeight = rect.Height() + 1.0;
595 // always align in the center if the bitmap is smaller than the window
596 if (width > bitmapWidth)
597 rect.OffsetBy(floorf((width - bitmapWidth) / 2.0), 0);
599 if (height > bitmapHeight)
600 rect.OffsetBy(0, floorf((height - bitmapHeight) / 2.0));
602 return rect;
606 void
607 ShowImageView::_DrawBackground(BRect border)
609 BRect bounds(Bounds());
610 // top
611 FillRect(BRect(0, 0, bounds.right, border.top - 1), B_SOLID_LOW);
612 // left
613 FillRect(BRect(0, border.top, border.left - 1, border.bottom), B_SOLID_LOW);
614 // right
615 FillRect(BRect(border.right + 1, border.top, bounds.right, border.bottom), B_SOLID_LOW);
616 // bottom
617 FillRect(BRect(0, border.bottom + 1, bounds.right, bounds.bottom), B_SOLID_LOW);
621 void
622 ShowImageView::_LayoutCaption(BFont& font, BPoint& pos, BRect& rect)
624 font_height fontHeight;
625 float width, height;
626 BRect bounds(Bounds());
627 font = be_plain_font;
628 width = font.StringWidth(fCaption.String());
629 font.GetHeight(&fontHeight);
630 height = fontHeight.ascent + fontHeight.descent;
631 // center text horizontally
632 pos.x = (bounds.left + bounds.right - width) / 2;
633 // flush bottom
634 pos.y = bounds.bottom - fontHeight.descent - 7;
636 // background rectangle
637 rect.Set(0, 0, width + 4, height + 4);
638 rect.OffsetTo(pos);
639 rect.OffsetBy(-2, -2 - fontHeight.ascent); // -2 for border
643 void
644 ShowImageView::_DrawCaption()
646 BFont font;
647 BPoint position;
648 BRect rect;
649 _LayoutCaption(font, position, rect);
651 PushState();
653 // draw background
654 SetDrawingMode(B_OP_ALPHA);
655 SetHighColor(255, 255, 255, 160);
656 FillRect(rect);
658 // draw text
659 SetDrawingMode(B_OP_OVER);
660 SetFont(&font);
661 SetLowColor(B_TRANSPARENT_COLOR);
662 SetHighColor(0, 0, 0);
663 DrawString(fCaption.String(), position);
665 PopState();
669 void
670 ShowImageView::_UpdateCaption()
672 BFont font;
673 BPoint pos;
674 BRect rect;
675 _LayoutCaption(font, pos, rect);
677 // draw over portion of image where caption is located
678 BRegion clip(rect);
679 PushState();
680 ConstrainClippingRegion(&clip);
681 Draw(rect);
682 PopState();
686 void
687 ShowImageView::_DrawImage(BRect rect)
689 // TODO: fix composing of fBitmap with other bitmaps
690 // with regard to alpha channel
691 if (!fDisplayBitmap)
692 fDisplayBitmap = fBitmap;
694 uint32 options = fScaleBilinear ? B_FILTER_BITMAP_BILINEAR : 0;
695 DrawBitmap(fDisplayBitmap, fDisplayBitmap->Bounds(), rect, options);
699 void
700 ShowImageView::Draw(BRect updateRect)
702 if (fBitmap == NULL)
703 return;
705 if (IsPrinting()) {
706 DrawBitmap(fBitmap);
707 return;
710 BRect rect = _AlignBitmap();
711 fBitmapLocationInView.x = floorf(rect.left);
712 fBitmapLocationInView.y = floorf(rect.top);
714 _DrawBackground(rect);
715 _DrawImage(rect);
717 if (fShowCaption)
718 _DrawCaption();
720 if (fHasSelection) {
721 if (fSelectionBitmap != NULL) {
722 BRect srcRect;
723 BRect dstRect;
724 _GetSelectionMergeRects(srcRect, dstRect);
725 dstRect = ImageToView(dstRect);
726 DrawBitmap(fSelectionBitmap, srcRect, dstRect);
728 fSelectionBox.Draw(this, updateRect);
733 BBitmap*
734 ShowImageView::_CopySelection(uchar alpha, bool imageSize)
736 bool hasAlpha = alpha != 255;
738 if (!fHasSelection)
739 return NULL;
741 BRect rect = fSelectionBox.Bounds().OffsetToCopy(B_ORIGIN);
742 if (!imageSize) {
743 // scale image to view size
744 rect.right = floorf((rect.right + 1.0) * fZoom - 1.0);
745 rect.bottom = floorf((rect.bottom + 1.0) * fZoom - 1.0);
747 BView view(rect, NULL, B_FOLLOW_NONE, B_WILL_DRAW);
748 BBitmap* bitmap = new(nothrow) BBitmap(rect, hasAlpha ? B_RGBA32
749 : fBitmap->ColorSpace(), true);
750 if (bitmap == NULL || !bitmap->IsValid()) {
751 delete bitmap;
752 return NULL;
755 if (bitmap->Lock()) {
756 bitmap->AddChild(&view);
757 #ifdef __HAIKU__
758 // On Haiku, B_OP_SUBSTRACT does not affect alpha like it did on BeOS.
759 // Don't know if it's better to fix it or not (stippi).
760 if (hasAlpha) {
761 view.SetHighColor(0, 0, 0, 0);
762 view.FillRect(view.Bounds());
763 view.SetDrawingMode(B_OP_ALPHA);
764 view.SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
765 view.SetHighColor(0, 0, 0, alpha);
767 if (fSelectionBitmap) {
768 view.DrawBitmap(fSelectionBitmap,
769 fSelectionBitmap->Bounds().OffsetToCopy(B_ORIGIN), rect);
770 } else
771 view.DrawBitmap(fBitmap, fCopyFromRect, rect);
772 #else
773 if (fSelectionBitmap) {
774 view.DrawBitmap(fSelectionBitmap,
775 fSelectionBitmap->Bounds().OffsetToCopy(B_ORIGIN), rect);
776 } else
777 view.DrawBitmap(fBitmap, fCopyFromRect, rect);
778 if (hasAlpha) {
779 view.SetDrawingMode(B_OP_SUBTRACT);
780 view.SetHighColor(0, 0, 0, 255 - alpha);
781 view.FillRect(rect, B_SOLID_HIGH);
783 #endif
784 view.Sync();
785 bitmap->RemoveChild(&view);
786 bitmap->Unlock();
789 return bitmap;
793 bool
794 ShowImageView::_AddSupportedTypes(BMessage* msg, BBitmap* bitmap)
796 BTranslatorRoster* roster = BTranslatorRoster::Default();
797 if (roster == NULL)
798 return false;
800 // add the current image mime first, will make it the preferred format on
801 // left mouse drag
802 msg->AddString("be:types", fMimeType);
803 msg->AddString("be:filetypes", fMimeType);
804 msg->AddString("be:type_descriptions", fFormatDescription);
806 bool foundOther = false;
807 bool foundCurrent = false;
809 int32 infoCount;
810 translator_info* info;
811 BBitmapStream stream(bitmap);
812 if (roster->GetTranslators(&stream, NULL, &info, &infoCount) == B_OK) {
813 for (int32 i = 0; i < infoCount; i++) {
814 const translation_format* formats;
815 int32 count;
816 roster->GetOutputFormats(info[i].translator, &formats, &count);
817 for (int32 j = 0; j < count; j++) {
818 if (fMimeType == formats[j].MIME)
819 foundCurrent = true;
820 else if (strcmp(formats[j].MIME, "image/x-be-bitmap") != 0) {
821 foundOther = true;
822 // needed to send data in message
823 msg->AddString("be:types", formats[j].MIME);
824 // needed to pass data via file
825 msg->AddString("be:filetypes", formats[j].MIME);
826 msg->AddString("be:type_descriptions", formats[j].name);
831 stream.DetachBitmap(&bitmap);
833 if (!foundCurrent) {
834 msg->RemoveData("be:types", 0);
835 msg->RemoveData("be:filetypes", 0);
836 msg->RemoveData("be:type_descriptions", 0);
839 return foundOther || foundCurrent;
843 void
844 ShowImageView::_BeginDrag(BPoint sourcePoint)
846 BBitmap* bitmap = _CopySelection(128, false);
847 if (bitmap == NULL)
848 return;
850 SetMouseEventMask(B_POINTER_EVENTS);
852 // fill the drag message
853 BMessage drag(B_SIMPLE_DATA);
854 drag.AddInt32("be:actions", B_COPY_TARGET);
855 drag.AddString("be:clip_name", "Bitmap Clip");
856 // ShowImage specific fields
857 drag.AddPoint("be:_source_point", sourcePoint);
858 drag.AddRect("be:_frame", fSelectionBox.Bounds());
859 if (_AddSupportedTypes(&drag, bitmap)) {
860 // we also support "Passing Data via File" protocol
861 drag.AddString("be:types", B_FILE_MIME_TYPE);
862 // avoid flickering of dragged bitmap caused by drawing into the window
863 _AnimateSelection(false);
864 // only use a transparent bitmap on selections less than 400x400
865 // (taking into account zooming)
866 BRect selectionRect = fSelectionBox.Bounds();
867 if (selectionRect.Width() * fZoom < 400.0
868 && selectionRect.Height() * fZoom < 400.0) {
869 sourcePoint -= selectionRect.LeftTop();
870 sourcePoint.x *= fZoom;
871 sourcePoint.y *= fZoom;
872 // DragMessage takes ownership of bitmap
873 DragMessage(&drag, bitmap, B_OP_ALPHA, sourcePoint);
874 bitmap = NULL;
875 } else {
876 delete bitmap;
877 // Offset and scale the rect
878 BRect rect(selectionRect);
879 rect = ImageToView(rect);
880 rect.InsetBy(-1, -1);
881 DragMessage(&drag, rect);
887 bool
888 ShowImageView::_OutputFormatForType(BBitmap* bitmap, const char* type,
889 translation_format* format)
891 bool found = false;
893 BTranslatorRoster* roster = BTranslatorRoster::Default();
894 if (roster == NULL)
895 return false;
897 BBitmapStream stream(bitmap);
899 translator_info* outInfo;
900 int32 outNumInfo;
901 if (roster->GetTranslators(&stream, NULL, &outInfo, &outNumInfo) == B_OK) {
902 for (int32 i = 0; i < outNumInfo; i++) {
903 const translation_format* formats;
904 int32 formatCount;
905 roster->GetOutputFormats(outInfo[i].translator, &formats,
906 &formatCount);
907 for (int32 j = 0; j < formatCount; j++) {
908 if (strcmp(formats[j].MIME, type) == 0) {
909 *format = formats[j];
910 found = true;
911 break;
916 stream.DetachBitmap(&bitmap);
917 return found;
921 #undef B_TRANSLATION_CONTEXT
922 #define B_TRANSLATION_CONTEXT "SaveToFile"
925 void
926 ShowImageView::SaveToFile(BDirectory* dir, const char* name, BBitmap* bitmap,
927 const translation_format* format)
929 if (bitmap == NULL) {
930 // If no bitmap is supplied, write out the whole image
931 bitmap = fBitmap;
934 BBitmapStream stream(bitmap);
936 bool loop = true;
937 while (loop) {
938 BTranslatorRoster* roster = BTranslatorRoster::Default();
939 if (!roster)
940 break;
941 // write data
942 BFile file(dir, name, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
943 if (file.InitCheck() != B_OK)
944 break;
945 if (roster->Translate(&stream, NULL, NULL, &file, format->type) < B_OK)
946 break;
947 // set mime type
948 BNodeInfo info(&file);
949 if (info.InitCheck() == B_OK)
950 info.SetType(format->MIME);
952 loop = false;
953 // break out of loop gracefully (indicates no errors)
955 if (loop) {
956 // If loop terminated because of a break, there was an error
957 char buffer[512];
958 snprintf(buffer, sizeof(buffer), B_TRANSLATE("The file '%s' could not "
959 "be written."), name);
960 BAlert* palert = new BAlert("", buffer, B_TRANSLATE("OK"));
961 palert->SetFlags(palert->Flags() | B_CLOSE_ON_ESCAPE);
962 palert->Go();
965 stream.DetachBitmap(&bitmap);
966 // Don't allow the bitmap to be deleted, this is
967 // especially important when using fBitmap as the bitmap
971 void
972 ShowImageView::_SendInMessage(BMessage* msg, BBitmap* bitmap,
973 translation_format* format)
975 BMessage reply(B_MIME_DATA);
976 BBitmapStream stream(bitmap); // destructor deletes bitmap
977 BTranslatorRoster* roster = BTranslatorRoster::Default();
978 BMallocIO memStream;
979 if (roster->Translate(&stream, NULL, NULL, &memStream, format->type) == B_OK) {
980 reply.AddData(format->MIME, B_MIME_TYPE, memStream.Buffer(),
981 memStream.BufferLength());
982 msg->SendReply(&reply);
987 void
988 ShowImageView::_HandleDrop(BMessage* msg)
990 entry_ref dirRef;
991 BString name, type;
992 bool saveToFile = msg->FindString("be:filetypes", &type) == B_OK
993 && msg->FindRef("directory", &dirRef) == B_OK
994 && msg->FindString("name", &name) == B_OK;
996 bool sendInMessage = !saveToFile
997 && msg->FindString("be:types", &type) == B_OK;
999 BBitmap* bitmap = _CopySelection();
1000 if (bitmap == NULL)
1001 return;
1003 translation_format format;
1004 if (!_OutputFormatForType(bitmap, type.String(), &format)) {
1005 delete bitmap;
1006 return;
1009 if (saveToFile) {
1010 BDirectory dir(&dirRef);
1011 SaveToFile(&dir, name.String(), bitmap, &format);
1012 delete bitmap;
1013 } else if (sendInMessage) {
1014 _SendInMessage(msg, bitmap, &format);
1015 } else {
1016 delete bitmap;
1021 void
1022 ShowImageView::_ScrollBitmap(BPoint point)
1024 point = ConvertToScreen(point);
1025 BPoint delta = fFirstPoint - point;
1026 fFirstPoint = point;
1027 _ScrollRestrictedBy(delta.x, delta.y);
1031 void
1032 ShowImageView::_GetMergeRects(BBitmap* merge, BRect selection, BRect& srcRect,
1033 BRect& dstRect)
1035 // Constrain dstRect to target image size and apply the same edge offsets
1036 // to the srcRect.
1038 dstRect = selection;
1040 BRect clippedDstRect(dstRect);
1041 ConstrainToImage(clippedDstRect);
1043 srcRect = merge->Bounds().OffsetToCopy(B_ORIGIN);
1045 srcRect.left += clippedDstRect.left - dstRect.left;
1046 srcRect.top += clippedDstRect.top - dstRect.top;
1047 srcRect.right += clippedDstRect.right - dstRect.right;
1048 srcRect.bottom += clippedDstRect.bottom - dstRect.bottom;
1050 dstRect = clippedDstRect;
1054 void
1055 ShowImageView::_GetSelectionMergeRects(BRect& srcRect, BRect& dstRect)
1057 _GetMergeRects(fSelectionBitmap, fSelectionBox.Bounds(), srcRect, dstRect);
1061 void
1062 ShowImageView::_MergeWithBitmap(BBitmap* merge, BRect selection)
1064 BView view(fBitmap->Bounds(), NULL, B_FOLLOW_NONE, B_WILL_DRAW);
1065 BBitmap* bitmap = new(nothrow) BBitmap(fBitmap->Bounds(),
1066 fBitmap->ColorSpace(), true);
1067 if (bitmap == NULL || !bitmap->IsValid()) {
1068 delete bitmap;
1069 return;
1072 if (bitmap->Lock()) {
1073 bitmap->AddChild(&view);
1074 view.DrawBitmap(fBitmap, fBitmap->Bounds());
1075 BRect srcRect;
1076 BRect dstRect;
1077 _GetMergeRects(merge, selection, srcRect, dstRect);
1078 view.DrawBitmap(merge, srcRect, dstRect);
1080 view.Sync();
1081 bitmap->RemoveChild(&view);
1082 bitmap->Unlock();
1084 _DeleteBitmap();
1085 fBitmap = bitmap;
1087 _SendMessageToWindow(MSG_MODIFIED);
1088 } else
1089 delete bitmap;
1093 void
1094 ShowImageView::MouseDown(BPoint position)
1096 MakeFocus(true);
1098 BPoint point = ViewToImage(position);
1099 int32 clickCount = 0;
1100 uint32 buttons = 0;
1101 if (Window() != NULL && Window()->CurrentMessage() != NULL) {
1102 clickCount = Window()->CurrentMessage()->FindInt32("clicks");
1103 buttons = Window()->CurrentMessage()->FindInt32("buttons");
1106 // Using clickCount >= 2 and the modulo 2 accounts for quickly repeated
1107 // double-clicks
1108 if (buttons == B_PRIMARY_MOUSE_BUTTON && clickCount >= 2 &&
1109 clickCount % 2 == 0) {
1110 Window()->PostMessage(MSG_FULL_SCREEN);
1111 return;
1114 if (fHasSelection && fSelectionBox.Bounds().Contains(point)
1115 && (buttons
1116 & (B_PRIMARY_MOUSE_BUTTON | B_SECONDARY_MOUSE_BUTTON)) != 0) {
1117 if (!fSelectionBitmap)
1118 fSelectionBitmap = _CopySelection();
1120 _BeginDrag(point);
1121 } else if (buttons == B_PRIMARY_MOUSE_BUTTON
1122 && (fSelectionMode
1123 || (modifiers() & (B_COMMAND_KEY | B_CONTROL_KEY)) != 0)) {
1124 // begin new selection
1125 _SetHasSelection(true);
1126 fCreatingSelection = true;
1127 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
1128 ConstrainToImage(point);
1129 fFirstPoint = point;
1130 fCopyFromRect.Set(point.x, point.y, point.x, point.y);
1131 fSelectionBox.SetBounds(this, fCopyFromRect);
1132 Invalidate();
1133 } else if (buttons == B_SECONDARY_MOUSE_BUTTON) {
1134 _ShowPopUpMenu(ConvertToScreen(position));
1135 } else if (buttons == B_PRIMARY_MOUSE_BUTTON
1136 || buttons == B_TERTIARY_MOUSE_BUTTON) {
1137 // move image in window
1138 SetMouseEventMask(B_POINTER_EVENTS, B_NO_POINTER_HISTORY);
1139 fScrollingBitmap = true;
1140 fFirstPoint = ConvertToScreen(position);
1141 be_app->SetCursor(fGrabCursor);
1146 void
1147 ShowImageView::_UpdateSelectionRect(BPoint point, bool final)
1149 BRect oldSelection = fCopyFromRect;
1150 point = ViewToImage(point);
1151 ConstrainToImage(point);
1152 fCopyFromRect.left = min_c(fFirstPoint.x, point.x);
1153 fCopyFromRect.right = max_c(fFirstPoint.x, point.x);
1154 fCopyFromRect.top = min_c(fFirstPoint.y, point.y);
1155 fCopyFromRect.bottom = max_c(fFirstPoint.y, point.y);
1156 fSelectionBox.SetBounds(this, fCopyFromRect);
1158 if (final) {
1159 // selection must be at least 2 pixels wide or 2 pixels tall
1160 if (fCopyFromRect.Width() < 1.0 && fCopyFromRect.Height() < 1.0)
1161 _SetHasSelection(false);
1162 } else
1163 _UpdateStatusText();
1165 if (oldSelection != fCopyFromRect || !fHasSelection) {
1166 BRect updateRect;
1167 updateRect = oldSelection | fCopyFromRect;
1168 updateRect = ImageToView(updateRect);
1169 updateRect.InsetBy(-1, -1);
1170 Invalidate(updateRect);
1175 void
1176 ShowImageView::MouseMoved(BPoint point, uint32 state, const BMessage* message)
1178 fHideCursorCountDown = HIDE_CURSOR_DELAY_TIME;
1179 if (fHideCursor) {
1180 // Show toolbar when mouse hits top 15 pixels, hide otherwise
1181 _ShowToolBarIfEnabled(ConvertToScreen(point).y <= 15);
1183 if (fCreatingSelection)
1184 _UpdateSelectionRect(point, false);
1185 else if (fScrollingBitmap)
1186 _ScrollBitmap(point);
1190 void
1191 ShowImageView::MouseUp(BPoint point)
1193 if (fCreatingSelection) {
1194 _UpdateSelectionRect(point, true);
1195 fCreatingSelection = false;
1196 } else if (fScrollingBitmap) {
1197 _ScrollBitmap(point);
1198 fScrollingBitmap = false;
1199 be_app->SetCursor(fDefaultCursor);
1201 _AnimateSelection(true);
1205 float
1206 ShowImageView::_LimitToRange(float v, orientation o, bool absolute)
1208 BScrollBar* psb = ScrollBar(o);
1209 if (psb) {
1210 float min, max, pos;
1211 pos = v;
1212 if (!absolute)
1213 pos += psb->Value();
1215 psb->GetRange(&min, &max);
1216 if (pos < min)
1217 pos = min;
1218 else if (pos > max)
1219 pos = max;
1221 v = pos;
1222 if (!absolute)
1223 v -= psb->Value();
1225 return v;
1229 void
1230 ShowImageView::_ScrollRestricted(float x, float y, bool absolute)
1232 if (x != 0)
1233 x = _LimitToRange(x, B_HORIZONTAL, absolute);
1235 if (y != 0)
1236 y = _LimitToRange(y, B_VERTICAL, absolute);
1238 // We invalidate before we scroll to avoid the caption messing up the
1239 // image, and to prevent it from flickering
1240 if (fShowCaption)
1241 Invalidate();
1243 ScrollBy(x, y);
1247 // XXX method is not unused
1248 void
1249 ShowImageView::_ScrollRestrictedTo(float x, float y)
1251 _ScrollRestricted(x, y, true);
1255 void
1256 ShowImageView::_ScrollRestrictedBy(float x, float y)
1258 _ScrollRestricted(x, y, false);
1262 void
1263 ShowImageView::KeyDown(const char* bytes, int32 numBytes)
1265 if (numBytes != 1) {
1266 BView::KeyDown(bytes, numBytes);
1267 return;
1270 bool shiftKeyDown = (modifiers() & B_SHIFT_KEY) != 0;
1272 switch (*bytes) {
1273 case B_DOWN_ARROW:
1274 if (shiftKeyDown)
1275 _ScrollRestrictedBy(0, 10);
1276 else
1277 _SendMessageToWindow(MSG_FILE_NEXT);
1278 break;
1279 case B_RIGHT_ARROW:
1280 if (shiftKeyDown)
1281 _ScrollRestrictedBy(10, 0);
1282 else
1283 _SendMessageToWindow(MSG_FILE_NEXT);
1284 break;
1285 case B_UP_ARROW:
1286 if (shiftKeyDown)
1287 _ScrollRestrictedBy(0, -10);
1288 else
1289 _SendMessageToWindow(MSG_FILE_PREV);
1290 break;
1291 case B_LEFT_ARROW:
1292 if (shiftKeyDown)
1293 _ScrollRestrictedBy(-10, 0);
1294 else
1295 _SendMessageToWindow(MSG_FILE_PREV);
1296 break;
1297 case B_BACKSPACE:
1298 _SendMessageToWindow(MSG_FILE_PREV);
1299 break;
1300 case B_HOME:
1301 break;
1302 case B_END:
1303 break;
1304 case B_SPACE:
1305 _ToggleSlideShow();
1306 break;
1307 case B_ESCAPE:
1308 // stop slide show
1309 _StopSlideShow();
1310 _ExitFullScreen();
1312 ClearSelection();
1313 break;
1314 case B_DELETE:
1315 if (fHasSelection)
1316 ClearSelection();
1317 else
1318 _SendMessageToWindow(kMsgDeleteCurrentFile);
1319 break;
1320 case '0':
1321 FitToBounds();
1322 break;
1323 case '1':
1324 SetZoom(1.0f);
1325 break;
1326 case '+':
1327 case '=':
1328 ZoomIn();
1329 break;
1330 case '-':
1331 ZoomOut();
1332 break;
1333 case '[':
1334 Rotate(270);
1335 break;
1336 case ']':
1337 Rotate(90);
1338 break;
1343 void
1344 ShowImageView::_MouseWheelChanged(BMessage* message)
1346 // The BeOS driver does not currently support
1347 // X wheel scrolling, therefore, deltaX is zero.
1348 // |deltaY| is the number of notches scrolled up or down.
1349 // When the wheel is scrolled down (towards the user) deltaY > 0
1350 // When the wheel is scrolled up (away from the user) deltaY < 0
1351 const float kscrollBy = 40;
1352 float deltaY;
1353 float deltaX;
1354 float x = 0;
1355 float y = 0;
1357 if (message->FindFloat("be:wheel_delta_x", &deltaX) == B_OK)
1358 x = deltaX * kscrollBy;
1360 if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK)
1361 y = deltaY * kscrollBy;
1363 if ((modifiers() & B_SHIFT_KEY) != 0) {
1364 // scroll up and down
1365 _ScrollRestrictedBy(x, y);
1366 } else if ((modifiers() & B_CONTROL_KEY) != 0) {
1367 // scroll left and right
1368 _ScrollRestrictedBy(y, x);
1369 } else {
1370 // zoom at location
1371 BPoint where;
1372 uint32 buttons;
1373 GetMouse(&where, &buttons);
1375 if (fStickyZoomCountDown <= 0) {
1376 if (deltaY < 0)
1377 ZoomIn(where);
1378 else if (deltaY > 0)
1379 ZoomOut(where);
1381 if (fZoom == 1.0)
1382 fStickyZoomCountDown = STICKY_ZOOM_DELAY_TIME;
1389 void
1390 ShowImageView::_ShowPopUpMenu(BPoint screen)
1392 if (!fShowingPopUpMenu) {
1393 PopUpMenu* menu = new PopUpMenu("PopUpMenu", this);
1395 ShowImageWindow* window = dynamic_cast<ShowImageWindow*>(Window());
1396 if (window != NULL)
1397 window->BuildContextMenu(menu);
1399 menu->Go(screen, true, true, true);
1400 fShowingPopUpMenu = true;
1405 void
1406 ShowImageView::_SettingsSetBool(const char* name, bool value)
1408 ShowImageSettings* settings;
1409 settings = my_app->Settings();
1410 if (settings->Lock()) {
1411 settings->SetBool(name, value);
1412 settings->Unlock();
1417 void
1418 ShowImageView::MessageReceived(BMessage* message)
1420 switch (message->what) {
1421 case B_COPY_TARGET:
1422 _HandleDrop(message);
1423 break;
1425 case B_MOUSE_WHEEL_CHANGED:
1426 _MouseWheelChanged(message);
1427 break;
1429 case kMsgPopUpMenuClosed:
1430 fShowingPopUpMenu = false;
1431 break;
1433 default:
1434 BView::MessageReceived(message);
1435 break;
1440 void
1441 ShowImageView::FixupScrollBar(orientation o, float bitmapLength,
1442 float viewLength)
1444 float prop, range;
1445 BScrollBar* psb;
1447 psb = ScrollBar(o);
1448 if (psb) {
1449 range = bitmapLength - viewLength;
1450 if (range < 0.0)
1451 range = 0.0;
1453 prop = viewLength / bitmapLength;
1454 if (prop > 1.0)
1455 prop = 1.0;
1457 psb->SetRange(0, range);
1458 psb->SetProportion(prop);
1459 psb->SetSteps(10, 100);
1464 void
1465 ShowImageView::FixupScrollBars()
1467 BRect viewRect = Bounds();
1468 BRect bitmapRect;
1469 if (fBitmap != NULL) {
1470 bitmapRect = _AlignBitmap();
1471 bitmapRect.OffsetTo(0, 0);
1474 FixupScrollBar(B_HORIZONTAL, bitmapRect.Width(), viewRect.Width());
1475 FixupScrollBar(B_VERTICAL, bitmapRect.Height(), viewRect.Height());
1479 void
1480 ShowImageView::SetSelectionMode(bool selectionMode)
1482 // The mode only has an effect in MouseDown()
1483 fSelectionMode = selectionMode;
1487 void
1488 ShowImageView::SelectAll()
1490 fCopyFromRect.Set(0, 0, fBitmap->Bounds().Width(),
1491 fBitmap->Bounds().Height());
1492 fSelectionBox.SetBounds(this, fCopyFromRect);
1493 _SetHasSelection(true);
1494 Invalidate();
1498 void
1499 ShowImageView::ClearSelection()
1501 if (!fHasSelection)
1502 return;
1504 _SetHasSelection(false);
1505 Invalidate();
1509 void
1510 ShowImageView::_SetHasSelection(bool hasSelection)
1512 _DeleteSelectionBitmap();
1513 fHasSelection = hasSelection;
1515 _UpdateStatusText();
1517 BMessage msg(MSG_SELECTION);
1518 msg.AddBool("has_selection", fHasSelection);
1519 _SendMessageToWindow(&msg);
1523 void
1524 ShowImageView::CopySelectionToClipboard()
1526 if (!fHasSelection || !be_clipboard->Lock())
1527 return;
1529 be_clipboard->Clear();
1531 BMessage* data = be_clipboard->Data();
1532 if (data != NULL) {
1533 BBitmap* bitmap = _CopySelection();
1534 if (bitmap != NULL) {
1535 BMessage bitmapArchive;
1536 bitmap->Archive(&bitmapArchive);
1537 // NOTE: Possibly "image/x-be-bitmap" is more correct.
1538 // This works with WonderBrush, though, which in turn had been
1539 // tested with other apps.
1540 data->AddMessage("image/bitmap", &bitmapArchive);
1541 data->AddPoint("be:location", fSelectionBox.Bounds().LeftTop());
1543 delete bitmap;
1545 be_clipboard->Commit();
1548 be_clipboard->Unlock();
1552 void
1553 ShowImageView::SetZoom(float zoom, BPoint where)
1555 float fitToBoundsZoom = _FitToBoundsZoom();
1556 if (zoom > 32)
1557 zoom = 32;
1558 if (zoom < fitToBoundsZoom / 2 && zoom < 0.25)
1559 zoom = min_c(fitToBoundsZoom / 2, 0.25);
1561 if (zoom == fZoom) {
1562 // window size might have changed
1563 FixupScrollBars();
1564 return;
1567 // Invalidate before scrolling, as that prevents the app_server
1568 // to do the scrolling server side
1569 Invalidate();
1571 // zoom to center if not otherwise specified
1572 BPoint offset;
1573 if (where.x == -1) {
1574 where.Set(Bounds().Width() / 2, Bounds().Height() / 2);
1575 offset = where;
1576 where += Bounds().LeftTop();
1577 } else
1578 offset = where - Bounds().LeftTop();
1580 float oldZoom = fZoom;
1581 fZoom = zoom;
1583 FixupScrollBars();
1585 if (fBitmap != NULL) {
1586 offset.x = (int)(where.x * fZoom / oldZoom + 0.5) - offset.x;
1587 offset.y = (int)(where.y * fZoom / oldZoom + 0.5) - offset.y;
1588 ScrollTo(offset);
1591 BMessage message(MSG_UPDATE_STATUS_ZOOM);
1592 message.AddFloat("zoom", fZoom);
1593 _SendMessageToWindow(&message);
1597 void
1598 ShowImageView::ZoomIn(BPoint where)
1600 // snap zoom to "fit to bounds", and "original size"
1601 float zoom = fZoom * 1.2;
1602 float zoomSnap = fZoom * 1.25;
1603 float fitToBoundsZoom = _FitToBoundsZoom();
1604 if (fZoom < fitToBoundsZoom - 0.001 && zoomSnap > fitToBoundsZoom)
1605 zoom = fitToBoundsZoom;
1606 if (fZoom < 1.0 && zoomSnap > 1.0)
1607 zoom = 1.0;
1609 SetZoom(zoom, where);
1613 void
1614 ShowImageView::ZoomOut(BPoint where)
1616 // snap zoom to "fit to bounds", and "original size"
1617 float zoom = fZoom / 1.2;
1618 float zoomSnap = fZoom / 1.25;
1619 float fitToBoundsZoom = _FitToBoundsZoom();
1620 if (fZoom > fitToBoundsZoom + 0.001 && zoomSnap < fitToBoundsZoom)
1621 zoom = fitToBoundsZoom;
1622 if (fZoom > 1.0 && zoomSnap < 1.0)
1623 zoom = 1.0;
1625 SetZoom(zoom, where);
1629 /*! Fits the image to the view bounds.
1631 void
1632 ShowImageView::FitToBounds()
1634 if (fBitmap == NULL)
1635 return;
1637 float fitToBoundsZoom = _FitToBoundsZoom();
1638 if ((!fStretchToBounds && fitToBoundsZoom > 1.0f) || fForceOriginalSize)
1639 SetZoom(1.0f);
1640 else
1641 SetZoom(fitToBoundsZoom);
1643 FixupScrollBars();
1647 void
1648 ShowImageView::_DoImageOperation(ImageProcessor::operation op, bool quiet)
1650 BMessenger msgr;
1651 ImageProcessor imageProcessor(op, fBitmap, msgr, 0);
1652 imageProcessor.Start(false);
1653 BBitmap* bm = imageProcessor.DetachBitmap();
1654 if (bm == NULL) {
1655 // operation failed
1656 return;
1659 // update orientation state
1660 if (op != ImageProcessor::kInvert) {
1661 // Note: If one of these fails, check its definition in class ImageProcessor.
1662 // ASSERT(ImageProcessor::kRotateClockwise <
1663 // ImageProcessor::kNumberOfAffineTransformations);
1664 // ASSERT(ImageProcessor::kRotateCounterClockwise <
1665 // ImageProcessor::kNumberOfAffineTransformations);
1666 // ASSERT(ImageProcessor::kFlipLeftToRight <
1667 // ImageProcessor::kNumberOfAffineTransformations);
1668 // ASSERT(ImageProcessor::kFlipTopToBottom <
1669 // ImageProcessor::kNumberOfAffineTransformations);
1670 fImageOrientation = fTransformation[op][fImageOrientation];
1673 if (!quiet) {
1674 // write orientation state
1675 BNode node(&fCurrentRef);
1676 int32 orientation = fImageOrientation;
1677 if (orientation != k0) {
1678 node.WriteAttr(SHOW_IMAGE_ORIENTATION_ATTRIBUTE, B_INT32_TYPE, 0,
1679 &orientation, sizeof(orientation));
1680 } else
1681 node.RemoveAttr(SHOW_IMAGE_ORIENTATION_ATTRIBUTE);
1684 // set new bitmap
1685 _DeleteBitmap();
1686 fBitmap = bm;
1688 if (fBitmap->ColorSpace() == B_RGBA32)
1689 fDisplayBitmap = compose_checker_background(fBitmap);
1691 if (!quiet) {
1692 // remove selection
1693 _SetHasSelection(false);
1694 _Notify();
1699 //! Image operation initiated by user
1700 void
1701 ShowImageView::_UserDoImageOperation(ImageProcessor::operation op, bool quiet)
1703 _DoImageOperation(op, quiet);
1707 void
1708 ShowImageView::Rotate(int degree)
1710 _UserDoImageOperation(degree == 90 ? ImageProcessor::kRotateClockwise
1711 : ImageProcessor::kRotateCounterClockwise);
1713 FitToBounds();
1717 void
1718 ShowImageView::Flip(bool vertical)
1720 if (vertical)
1721 _UserDoImageOperation(ImageProcessor::kFlipLeftToRight);
1722 else
1723 _UserDoImageOperation(ImageProcessor::kFlipTopToBottom);
1727 void
1728 ShowImageView::ResizeImage(int w, int h)
1730 if (fBitmap == NULL || w < 1 || h < 1)
1731 return;
1733 Scaler scaler(fBitmap, BRect(0, 0, w - 1, h - 1), BMessenger(), 0, false);
1734 scaler.Start(false);
1735 BBitmap* scaled = scaler.DetachBitmap();
1736 if (scaled == NULL) {
1737 // operation failed
1738 return;
1741 // remove selection
1742 _SetHasSelection(false);
1743 _DeleteBitmap();
1744 fBitmap = scaled;
1746 _SendMessageToWindow(MSG_MODIFIED);
1748 _Notify();
1752 void
1753 ShowImageView::_SetIcon(bool clear, icon_size which)
1755 int32 size;
1756 switch (which) {
1757 case B_MINI_ICON: size = 16;
1758 break;
1759 case B_LARGE_ICON: size = 32;
1760 break;
1761 default:
1762 return;
1765 BRect rect(fBitmap->Bounds());
1766 float s;
1767 s = size / (rect.Width() + 1.0);
1769 if (s * (rect.Height() + 1.0) <= size) {
1770 rect.right = size - 1;
1771 rect.bottom = static_cast<int>(s * (rect.Height() + 1.0)) - 1;
1772 // center vertically
1773 rect.OffsetBy(0, (size - rect.IntegerHeight()) / 2);
1774 } else {
1775 s = size / (rect.Height() + 1.0);
1776 rect.right = static_cast<int>(s * (rect.Width() + 1.0)) - 1;
1777 rect.bottom = size - 1;
1778 // center horizontally
1779 rect.OffsetBy((size - rect.IntegerWidth()) / 2, 0);
1782 // scale bitmap to thumbnail size
1783 BMessenger msgr;
1784 Scaler scaler(fBitmap, rect, msgr, 0, true);
1785 BBitmap* thumbnail = scaler.GetBitmap();
1786 scaler.Start(false);
1787 ASSERT(thumbnail->ColorSpace() == B_CMAP8);
1788 // create icon from thumbnail
1789 BBitmap icon(BRect(0, 0, size - 1, size - 1), B_CMAP8);
1790 memset(icon.Bits(), B_TRANSPARENT_MAGIC_CMAP8, icon.BitsLength());
1791 BScreen screen;
1792 const uchar* src = (uchar*)thumbnail->Bits();
1793 uchar* dest = (uchar*)icon.Bits();
1794 const int32 srcBPR = thumbnail->BytesPerRow();
1795 const int32 destBPR = icon.BytesPerRow();
1796 const int32 deltaX = (int32)rect.left;
1797 const int32 deltaY = (int32)rect.top;
1799 for (int32 y = 0; y <= rect.IntegerHeight(); y++) {
1800 for (int32 x = 0; x <= rect.IntegerWidth(); x++) {
1801 const uchar* s = src + y * srcBPR + x;
1802 uchar* d = dest + (y + deltaY) * destBPR + (x + deltaX);
1803 *d = *s;
1807 // set icon
1808 BNode node(&fCurrentRef);
1809 BNodeInfo info(&node);
1810 info.SetIcon(clear ? NULL : &icon, which);
1814 void
1815 ShowImageView::SetIcon(bool clear)
1817 _SetIcon(clear, B_MINI_ICON);
1818 _SetIcon(clear, B_LARGE_ICON);
1822 void
1823 ShowImageView::_ToggleSlideShow()
1825 _SendMessageToWindow(MSG_SLIDE_SHOW);
1829 void
1830 ShowImageView::_StopSlideShow()
1832 _SendMessageToWindow(kMsgStopSlideShow);
1836 void
1837 ShowImageView::_ExitFullScreen()
1839 be_app->ShowCursor();
1840 _SendMessageToWindow(MSG_EXIT_FULL_SCREEN);
1844 void
1845 ShowImageView::_ShowToolBarIfEnabled(bool show)
1847 BMessage message(kShowToolBarIfEnabled);
1848 message.AddBool("show", show);
1849 Window()->PostMessage(&message);
1853 void
1854 ShowImageView::WindowActivated(bool active)
1856 fIsActiveWin = active;
1857 fHideCursorCountDown = HIDE_CURSOR_DELAY_TIME;