2 * Copyright 2006-2007, 2011, Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT License.
7 #include "CanvasView.h"
18 #include "ui_defines.h"
20 #include "CommandStack.h"
21 #include "IconRenderer.h"
27 CanvasView::CanvasView(BRect frame
)
29 StateView(frame
, "canvas view", B_FOLLOW_ALL
,
30 B_WILL_DRAW
| B_FRAME_EVENTS
),
31 fBitmap(new BBitmap(BRect(0, 0, 63, 63), 0, B_RGB32
)),
32 fBackground(new BBitmap(BRect(0, 0, 63, 63), 0, B_RGB32
)),
34 fRenderer(new IconRenderer(fBitmap
)),
35 fDirtyIconArea(fBitmap
->Bounds()),
37 fCanvasOrigin(0.0, 0.0),
40 fSpaceHeldDown(false),
42 fScrollTracking(false),
43 fScrollTrackingStart(0.0, 0.0),
45 fMouseFilterMode(SNAPPING_OFF
)
48 fRenderer
->SetBackground(fBackground
);
52 CanvasView::~CanvasView()
65 CanvasView::AttachedToWindow()
67 StateView::AttachedToWindow();
69 SetViewColor(B_TRANSPARENT_COLOR
);
70 SetLowColor(kStripesHigh
);
71 SetHighColor(kStripesLow
);
73 // init data rect for scrolling and center bitmap in the view
74 BRect dataRect
= _LayoutCanvas();
75 SetDataRect(dataRect
);
76 BRect
bounds(Bounds());
77 BPoint
dataRectCenter((dataRect
.left
+ dataRect
.right
) / 2,
78 (dataRect
.top
+ dataRect
.bottom
) / 2);
79 BPoint
boundsCenter((bounds
.left
+ bounds
.right
) / 2,
80 (bounds
.top
+ bounds
.bottom
) / 2);
81 BPoint offset
= ScrollOffset();
82 offset
.x
= roundf(offset
.x
+ dataRectCenter
.x
- boundsCenter
.x
);
83 offset
.y
= roundf(offset
.y
+ dataRectCenter
.y
- boundsCenter
.y
);
84 SetScrollOffset(offset
);
89 CanvasView::FrameResized(float width
, float height
)
91 // keep canvas centered
92 BPoint oldCanvasOrigin
= fCanvasOrigin
;
93 SetDataRect(_LayoutCanvas());
94 if (oldCanvasOrigin
!= fCanvasOrigin
)
100 CanvasView::Draw(BRect updateRect
)
102 _DrawInto(this, updateRect
);
110 CanvasView::MouseDown(BPoint where
)
116 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons
) < B_OK
)
119 // handle clicks of the third mouse button ourselves (panning),
120 // otherwise have StateView handle it (normal clicks)
121 if (fSpaceHeldDown
|| (buttons
& B_TERTIARY_MOUSE_BUTTON
) != 0) {
122 // switch into scrolling mode and update cursor
123 fScrollTracking
= true;
124 where
.x
= roundf(where
.x
);
125 where
.y
= roundf(where
.y
);
126 fScrollOffsetStart
= ScrollOffset();
127 fScrollTrackingStart
= where
- fScrollOffsetStart
;
129 SetMouseEventMask(B_POINTER_EVENTS
,
130 B_LOCK_WINDOW_FOCUS
| B_SUSPEND_VIEW_FOCUS
);
132 StateView::MouseDown(where
);
138 CanvasView::MouseUp(BPoint where
)
140 if (fScrollTracking
) {
141 // stop scroll tracking and update cursor
142 fScrollTracking
= false;
144 // update StateView mouse position
145 uint32 transit
= Bounds().Contains(where
) ?
146 B_INSIDE_VIEW
: B_OUTSIDE_VIEW
;
147 StateView::MouseMoved(where
, transit
, NULL
);
149 StateView::MouseUp(where
);
155 CanvasView::MouseMoved(BPoint where
, uint32 transit
, const BMessage
* dragMessage
)
157 if (fScrollTracking
) {
159 GetMouse(&where
, &buttons
, false);
164 where
.x
= roundf(where
.x
);
165 where
.y
= roundf(where
.y
);
166 where
-= ScrollOffset();
167 BPoint offset
= where
- fScrollTrackingStart
;
168 SetScrollOffset(fScrollOffsetStart
- offset
);
170 // normal mouse movement handled by StateView
172 StateView::MouseMoved(where
, transit
, dragMessage
);
178 CanvasView::FilterMouse(BPoint
* where
) const
180 switch (fMouseFilterMode
) {
183 ConvertToCanvas(where
);
184 where
->x
= floorf(where
->x
+ 0.5);
185 where
->y
= floorf(where
->y
+ 0.5);
186 ConvertFromCanvas(where
);
190 ConvertToCanvas(where
);
193 where
->x
= floorf(where
->x
+ 0.5);
194 where
->y
= floorf(where
->y
+ 0.5);
197 ConvertFromCanvas(where
);
201 ConvertToCanvas(where
);
204 where
->x
= floorf(where
->x
+ 0.5);
205 where
->y
= floorf(where
->y
+ 0.5);
208 ConvertFromCanvas(where
);
219 CanvasView::MouseWheelChanged(BPoint where
, float x
, float y
)
221 if (!Bounds().Contains(where
))
225 _SetZoom(_NextZoomOutLevel(fZoomLevel
), true);
227 } else if (y
< 0.0) {
228 _SetZoom(_NextZoomInLevel(fZoomLevel
), true);
239 CanvasView::SetScrollOffset(BPoint newOffset
)
246 newOffset
= ValidScrollOffsetFor(newOffset
);
247 if (!fScrollTracking
) {
248 BPoint mouseOffset
= newOffset
- ScrollOffset();
249 MouseMoved(fMouseInfo
.position
+ mouseOffset
, fMouseInfo
.transit
,
253 Scrollable::SetScrollOffset(newOffset
);
260 CanvasView::ScrollOffsetChanged(BPoint oldOffset
, BPoint newOffset
)
262 BPoint offset
= newOffset
- oldOffset
;
264 if (offset
== B_ORIGIN
) {
265 // prevent circular code (MouseMoved might call ScrollBy...)
269 ScrollBy(offset
.x
, offset
.y
);
274 CanvasView::VisibleSizeChanged(float oldWidth
, float oldHeight
, float newWidth
,
277 BRect
dataRect(_LayoutCanvas());
278 SetDataRect(dataRect
);
286 CanvasView::AreaInvalidated(const BRect
& area
)
288 if (fDirtyIconArea
.Contains(area
))
291 fDirtyIconArea
= fDirtyIconArea
| area
;
293 BRect
viewArea(area
);
294 ConvertFromCanvas(&viewArea
);
295 Invalidate(viewArea
);
303 CanvasView::SetIcon(Icon
* icon
)
309 fIcon
->RemoveListener(this);
312 fRenderer
->SetIcon(icon
);
315 fIcon
->AddListener(this);
320 CanvasView::SetMouseFilterMode(uint32 mode
)
322 if (fMouseFilterMode
== mode
)
325 fMouseFilterMode
= mode
;
326 Invalidate(_CanvasRect());
331 CanvasView::ConvertFromCanvas(BPoint
* point
) const
333 point
->x
= point
->x
* fZoomLevel
+ fCanvasOrigin
.x
;
334 point
->y
= point
->y
* fZoomLevel
+ fCanvasOrigin
.y
;
339 CanvasView::ConvertToCanvas(BPoint
* point
) const
341 point
->x
= (point
->x
- fCanvasOrigin
.x
) / fZoomLevel
;
342 point
->y
= (point
->y
- fCanvasOrigin
.y
) / fZoomLevel
;
347 CanvasView::ConvertFromCanvas(BRect
* r
) const
349 r
->left
= r
->left
* fZoomLevel
+ fCanvasOrigin
.x
;
350 r
->top
= r
->top
* fZoomLevel
+ fCanvasOrigin
.y
;
353 r
->right
= r
->right
* fZoomLevel
+ fCanvasOrigin
.x
;
354 r
->bottom
= r
->bottom
* fZoomLevel
+ fCanvasOrigin
.y
;
361 CanvasView::ConvertToCanvas(BRect
* r
) const
363 r
->left
= (r
->left
- fCanvasOrigin
.x
) / fZoomLevel
;
364 r
->top
= (r
->top
- fCanvasOrigin
.y
) / fZoomLevel
;
365 r
->right
= (r
->right
- fCanvasOrigin
.x
) / fZoomLevel
;
366 r
->bottom
= (r
->bottom
- fCanvasOrigin
.y
) / fZoomLevel
;
374 CanvasView::_HandleKeyDown(uint32 key
, uint32 modifiers
)
379 if (modifiers
& B_SHIFT_KEY
)
380 CommandStack()->Redo();
382 CommandStack()->Undo();
386 _SetZoom(_NextZoomInLevel(fZoomLevel
));
389 _SetZoom(_NextZoomOutLevel(fZoomLevel
));
393 fSpaceHeldDown
= true;
398 return StateView::_HandleKeyDown(key
, modifiers
);
406 CanvasView::_HandleKeyUp(uint32 key
, uint32 modifiers
)
410 fSpaceHeldDown
= false;
415 return StateView::_HandleKeyUp(key
, modifiers
);
423 CanvasView::_CanvasRect() const
428 r
= fBitmap
->Bounds();
429 ConvertFromCanvas(&r
);
435 CanvasView::_DrawInto(BView
* view
, BRect updateRect
)
437 if (fDirtyIconArea
.IsValid()) {
438 fRenderer
->Render(fDirtyIconArea
);
439 fDirtyIconArea
.Set(LONG_MAX
, LONG_MAX
, LONG_MIN
, LONG_MIN
);
443 BRect
canvas(_CanvasRect());
444 view
->DrawBitmap(fBitmap
, fBitmap
->Bounds(), canvas
);
449 switch (fMouseFilterMode
) {
465 view
->SetDrawingMode(B_OP_BLEND
);
466 for (int32 i
= 1; i
<= gridLines
; i
++) {
467 BPoint
cross(i
* scale
, i
* scale
);
468 ConvertFromCanvas(&cross
);
469 view
->StrokeLine(BPoint(canvas
.left
, cross
.y
),
470 BPoint(canvas
.right
, cross
.y
));
471 view
->StrokeLine(BPoint(cross
.x
, canvas
.top
),
472 BPoint(cross
.x
, canvas
.bottom
));
474 view
->SetDrawingMode(B_OP_COPY
);
477 BRegion
outside(Bounds() & updateRect
);
478 outside
.Exclude(canvas
);
479 view
->FillRegion(&outside
, kStripes
);
481 StateView::Draw(view
, updateRect
);
486 CanvasView::_MakeBackground()
488 uint8
* row
= (uint8
*)fBackground
->Bits();
489 uint32 bpr
= fBackground
->BytesPerRow();
490 uint32 width
= fBackground
->Bounds().IntegerWidth() + 1;
491 uint32 height
= fBackground
->Bounds().IntegerHeight() + 1;
493 const GammaTable
& lut
= fRenderer
->GammaTable();
494 uint8 redLow
= lut
.dir(kAlphaLow
.red
);
495 uint8 greenLow
= lut
.dir(kAlphaLow
.blue
);
496 uint8 blueLow
= lut
.dir(kAlphaLow
.green
);
497 uint8 redHigh
= lut
.dir(kAlphaHigh
.red
);
498 uint8 greenHigh
= lut
.dir(kAlphaHigh
.blue
);
499 uint8 blueHigh
= lut
.dir(kAlphaHigh
.green
);
501 for (uint32 y
= 0; y
< height
; y
++) {
503 for (uint32 x
= 0; x
< width
; x
++) {
534 CanvasView::_UpdateToolCursor()
537 if (fScrollTracking
|| fSpaceHeldDown
) {
538 // indicate scrolling mode
539 const uchar
* cursorData
= fScrollTracking
? kGrabCursor
: kHandCursor
;
540 BCursor
cursor(cursorData
);
541 SetViewCursor(&cursor
, true);
543 // pass on to current state of StateView
547 BCursor
cursor(kStopCursor
);
548 SetViewCursor(&cursor
, true);
557 CanvasView::_NextZoomInLevel(double zoom
) const
582 CanvasView::_NextZoomOutLevel(double zoom
) const
605 CanvasView::_SetZoom(double zoomLevel
, bool mouseIsAnchor
)
607 if (fZoomLevel
== zoomLevel
)
612 // zoom into mouse position
613 anchor
= MouseInfo()->position
;
615 // zoom into center of view
616 BRect
bounds(Bounds());
617 anchor
.x
= (bounds
.left
+ bounds
.right
+ 1) / 2.0;
618 anchor
.y
= (bounds
.top
+ bounds
.bottom
+ 1) / 2.0;
621 BPoint canvasAnchor
= anchor
;
622 ConvertToCanvas(&canvasAnchor
);
624 fZoomLevel
= zoomLevel
;
625 BRect dataRect
= _LayoutCanvas();
627 ConvertFromCanvas(&canvasAnchor
);
629 BPoint offset
= ScrollOffset();
630 offset
.x
= roundf(offset
.x
+ canvasAnchor
.x
- anchor
.x
);
631 offset
.y
= roundf(offset
.y
+ canvasAnchor
.y
- anchor
.y
);
635 SetDataRectAndScrollOffset(dataRect
, offset
);
640 CanvasView::_LayoutCanvas()
642 // size of zoomed bitmap
643 BRect
r(_CanvasRect());
644 r
.OffsetTo(B_ORIGIN
);
646 // ask current view state to extend size
647 // TODO: Ask StateViewState to extend bounds...
648 BRect stateBounds
= r
; //ViewStateBounds();
650 // resize for empty area around bitmap
651 // (the size we want, but might still be much smaller than view)
654 // center data rect in bounds
655 BRect
bounds(Bounds());
656 if (bounds
.Width() > r
.Width())
657 r
.InsetBy(-ceilf((bounds
.Width() - r
.Width()) / 2), 0);
658 if (bounds
.Height() > r
.Height())
659 r
.InsetBy(0, -ceilf((bounds
.Height() - r
.Height()) / 2));
661 if (stateBounds
.IsValid()) {
662 stateBounds
.InsetBy(-20, -20);