HaikuDepot: notify work status from main window
[haiku.git] / src / apps / icon-o-matic / CanvasView.cpp
blob277cf6deb8b14783c290ca057f7c0b4eac4b17bf
1 /*
2 * Copyright 2006-2007, 2011, Stephan Aßmus <superstippi@gmx.de>
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
7 #include "CanvasView.h"
9 #include <Bitmap.h>
10 #include <Cursor.h>
11 #include <Message.h>
12 #include <Region.h>
13 #include <Window.h>
15 #include <stdio.h>
17 #include "cursors.h"
18 #include "ui_defines.h"
20 #include "CommandStack.h"
21 #include "IconRenderer.h"
24 using std::nothrow;
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)),
33 fIcon(NULL),
34 fRenderer(new IconRenderer(fBitmap)),
35 fDirtyIconArea(fBitmap->Bounds()),
37 fCanvasOrigin(0.0, 0.0),
38 fZoomLevel(8.0),
40 fSpaceHeldDown(false),
41 fInScrollTo(false),
42 fScrollTracking(false),
43 fScrollTrackingStart(0.0, 0.0),
45 fMouseFilterMode(SNAPPING_OFF)
47 _MakeBackground();
48 fRenderer->SetBackground(fBackground);
52 CanvasView::~CanvasView()
54 SetIcon(NULL);
55 delete fRenderer;
56 delete fBitmap;
57 delete fBackground;
61 // #pragma mark -
64 void
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);
88 void
89 CanvasView::FrameResized(float width, float height)
91 // keep canvas centered
92 BPoint oldCanvasOrigin = fCanvasOrigin;
93 SetDataRect(_LayoutCanvas());
94 if (oldCanvasOrigin != fCanvasOrigin)
95 Invalidate();
99 void
100 CanvasView::Draw(BRect updateRect)
102 _DrawInto(this, updateRect);
106 // #pragma mark -
109 void
110 CanvasView::MouseDown(BPoint where)
112 if (!IsFocus())
113 MakeFocus(true);
115 int32 buttons;
116 if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) < B_OK)
117 buttons = 0;
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;
128 _UpdateToolCursor();
129 SetMouseEventMask(B_POINTER_EVENTS,
130 B_LOCK_WINDOW_FOCUS | B_SUSPEND_VIEW_FOCUS);
131 } else {
132 StateView::MouseDown(where);
137 void
138 CanvasView::MouseUp(BPoint where)
140 if (fScrollTracking) {
141 // stop scroll tracking and update cursor
142 fScrollTracking = false;
143 _UpdateToolCursor();
144 // update StateView mouse position
145 uint32 transit = Bounds().Contains(where) ?
146 B_INSIDE_VIEW : B_OUTSIDE_VIEW;
147 StateView::MouseMoved(where, transit, NULL);
148 } else {
149 StateView::MouseUp(where);
154 void
155 CanvasView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
157 if (fScrollTracking) {
158 uint32 buttons;
159 GetMouse(&where, &buttons, false);
160 if (!buttons) {
161 MouseUp(where);
162 return;
164 where.x = roundf(where.x);
165 where.y = roundf(where.y);
166 where -= ScrollOffset();
167 BPoint offset = where - fScrollTrackingStart;
168 SetScrollOffset(fScrollOffsetStart - offset);
169 } else {
170 // normal mouse movement handled by StateView
171 if (!fSpaceHeldDown)
172 StateView::MouseMoved(where, transit, dragMessage);
177 void
178 CanvasView::FilterMouse(BPoint* where) const
180 switch (fMouseFilterMode) {
182 case SNAPPING_64:
183 ConvertToCanvas(where);
184 where->x = floorf(where->x + 0.5);
185 where->y = floorf(where->y + 0.5);
186 ConvertFromCanvas(where);
187 break;
189 case SNAPPING_32:
190 ConvertToCanvas(where);
191 where->x /= 2.0;
192 where->y /= 2.0;
193 where->x = floorf(where->x + 0.5);
194 where->y = floorf(where->y + 0.5);
195 where->x *= 2.0;
196 where->y *= 2.0;
197 ConvertFromCanvas(where);
198 break;
200 case SNAPPING_16:
201 ConvertToCanvas(where);
202 where->x /= 4.0;
203 where->y /= 4.0;
204 where->x = floorf(where->x + 0.5);
205 where->y = floorf(where->y + 0.5);
206 where->x *= 4.0;
207 where->y *= 4.0;
208 ConvertFromCanvas(where);
209 break;
211 case SNAPPING_OFF:
212 default:
213 break;
218 bool
219 CanvasView::MouseWheelChanged(BPoint where, float x, float y)
221 if (!Bounds().Contains(where))
222 return false;
224 if (y > 0.0) {
225 _SetZoom(_NextZoomOutLevel(fZoomLevel), true);
226 return true;
227 } else if (y < 0.0) {
228 _SetZoom(_NextZoomInLevel(fZoomLevel), true);
229 return true;
231 return false;
235 // #pragma mark -
238 void
239 CanvasView::SetScrollOffset(BPoint newOffset)
241 if (fInScrollTo)
242 return;
244 fInScrollTo = true;
246 newOffset = ValidScrollOffsetFor(newOffset);
247 if (!fScrollTracking) {
248 BPoint mouseOffset = newOffset - ScrollOffset();
249 MouseMoved(fMouseInfo.position + mouseOffset, fMouseInfo.transit,
250 NULL);
253 Scrollable::SetScrollOffset(newOffset);
255 fInScrollTo = false;
259 void
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...)
266 return;
269 ScrollBy(offset.x, offset.y);
273 void
274 CanvasView::VisibleSizeChanged(float oldWidth, float oldHeight, float newWidth,
275 float newHeight)
277 BRect dataRect(_LayoutCanvas());
278 SetDataRect(dataRect);
282 // #pragma mark -
285 void
286 CanvasView::AreaInvalidated(const BRect& area)
288 if (fDirtyIconArea.Contains(area))
289 return;
291 fDirtyIconArea = fDirtyIconArea | area;
293 BRect viewArea(area);
294 ConvertFromCanvas(&viewArea);
295 Invalidate(viewArea);
299 // #pragma mark -
302 void
303 CanvasView::SetIcon(Icon* icon)
305 if (fIcon == icon)
306 return;
308 if (fIcon)
309 fIcon->RemoveListener(this);
311 fIcon = icon;
312 fRenderer->SetIcon(icon);
314 if (fIcon)
315 fIcon->AddListener(this);
319 void
320 CanvasView::SetMouseFilterMode(uint32 mode)
322 if (fMouseFilterMode == mode)
323 return;
325 fMouseFilterMode = mode;
326 Invalidate(_CanvasRect());
330 void
331 CanvasView::ConvertFromCanvas(BPoint* point) const
333 point->x = point->x * fZoomLevel + fCanvasOrigin.x;
334 point->y = point->y * fZoomLevel + fCanvasOrigin.y;
338 void
339 CanvasView::ConvertToCanvas(BPoint* point) const
341 point->x = (point->x - fCanvasOrigin.x) / fZoomLevel;
342 point->y = (point->y - fCanvasOrigin.y) / fZoomLevel;
346 void
347 CanvasView::ConvertFromCanvas(BRect* r) const
349 r->left = r->left * fZoomLevel + fCanvasOrigin.x;
350 r->top = r->top * fZoomLevel + fCanvasOrigin.y;
351 r->right++;
352 r->bottom++;
353 r->right = r->right * fZoomLevel + fCanvasOrigin.x;
354 r->bottom = r->bottom * fZoomLevel + fCanvasOrigin.y;
355 r->right--;
356 r->bottom--;
360 void
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;
370 // #pragma mark -
373 bool
374 CanvasView::_HandleKeyDown(uint32 key, uint32 modifiers)
376 switch (key) {
377 case 'z':
378 case 'y':
379 if (modifiers & B_SHIFT_KEY)
380 CommandStack()->Redo();
381 else
382 CommandStack()->Undo();
383 break;
385 case '+':
386 _SetZoom(_NextZoomInLevel(fZoomLevel));
387 break;
388 case '-':
389 _SetZoom(_NextZoomOutLevel(fZoomLevel));
390 break;
392 case B_SPACE:
393 fSpaceHeldDown = true;
394 _UpdateToolCursor();
395 break;
397 default:
398 return StateView::_HandleKeyDown(key, modifiers);
401 return true;
405 bool
406 CanvasView::_HandleKeyUp(uint32 key, uint32 modifiers)
408 switch (key) {
409 case B_SPACE:
410 fSpaceHeldDown = false;
411 _UpdateToolCursor();
412 break;
414 default:
415 return StateView::_HandleKeyUp(key, modifiers);
418 return true;
422 BRect
423 CanvasView::_CanvasRect() const
425 BRect r;
426 if (fBitmap == NULL)
427 return r;
428 r = fBitmap->Bounds();
429 ConvertFromCanvas(&r);
430 return r;
434 void
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);
442 // icon
443 BRect canvas(_CanvasRect());
444 view->DrawBitmap(fBitmap, fBitmap->Bounds(), canvas);
446 // grid
447 int32 gridLines = 0;
448 int32 scale = 1;
449 switch (fMouseFilterMode) {
450 case SNAPPING_64:
451 gridLines = 63;
452 break;
453 case SNAPPING_32:
454 gridLines = 31;
455 scale = 2;
456 break;
457 case SNAPPING_16:
458 gridLines = 15;
459 scale = 4;
460 break;
461 case SNAPPING_OFF:
462 default:
463 break;
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);
476 // outside icon
477 BRegion outside(Bounds() & updateRect);
478 outside.Exclude(canvas);
479 view->FillRegion(&outside, kStripes);
481 StateView::Draw(view, updateRect);
485 void
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++) {
502 uint8* p = row;
503 for (uint32 x = 0; x < width; x++) {
504 p[3] = 255;
505 if (x % 8 >= 4) {
506 if (y % 8 >= 4) {
507 p[0] = blueLow;
508 p[1] = greenLow;
509 p[2] = redLow;
510 } else {
511 p[0] = blueHigh;
512 p[1] = greenHigh;
513 p[2] = redHigh;
515 } else {
516 if (y % 8 >= 4) {
517 p[0] = blueHigh;
518 p[1] = greenHigh;
519 p[2] = redHigh;
520 } else {
521 p[0] = blueLow;
522 p[1] = greenLow;
523 p[2] = redLow;
526 p += 4;
528 row += bpr;
533 void
534 CanvasView::_UpdateToolCursor()
536 if (fIcon) {
537 if (fScrollTracking || fSpaceHeldDown) {
538 // indicate scrolling mode
539 const uchar* cursorData = fScrollTracking ? kGrabCursor : kHandCursor;
540 BCursor cursor(cursorData);
541 SetViewCursor(&cursor, true);
542 } else {
543 // pass on to current state of StateView
544 UpdateStateCursor();
546 } else {
547 BCursor cursor(kStopCursor);
548 SetViewCursor(&cursor, true);
553 // #pragma mark -
556 double
557 CanvasView::_NextZoomInLevel(double zoom) const
559 if (zoom < 1)
560 return 1;
561 if (zoom < 1.5)
562 return 1.5;
563 if (zoom < 2)
564 return 2;
565 if (zoom < 3)
566 return 3;
567 if (zoom < 4)
568 return 4;
569 if (zoom < 6)
570 return 6;
571 if (zoom < 8)
572 return 8;
573 if (zoom < 16)
574 return 16;
575 if (zoom < 32)
576 return 32;
577 return 64;
581 double
582 CanvasView::_NextZoomOutLevel(double zoom) const
584 if (zoom > 32)
585 return 32;
586 if (zoom > 16)
587 return 16;
588 if (zoom > 8)
589 return 8;
590 if (zoom > 6)
591 return 6;
592 if (zoom > 4)
593 return 4;
594 if (zoom > 3)
595 return 3;
596 if (zoom > 2)
597 return 2;
598 if (zoom > 1.5)
599 return 1.5;
600 return 1;
604 void
605 CanvasView::_SetZoom(double zoomLevel, bool mouseIsAnchor)
607 if (fZoomLevel == zoomLevel)
608 return;
610 BPoint anchor;
611 if (mouseIsAnchor) {
612 // zoom into mouse position
613 anchor = MouseInfo()->position;
614 } else {
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);
633 Invalidate();
635 SetDataRectAndScrollOffset(dataRect, offset);
639 BRect
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)
652 r.InsetBy(-50, -50);
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);
663 r = r | stateBounds;
666 return r;