Proper check for rawzor libraries.
[rawtherapee-fixes.git] / rtgui / cropwindow.cc
blob414a5da53c68dda475f5957de5b32200fff11390
1 /*
2 * This file is part of RawTherapee.
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
19 #include <cropwindow.h>
20 #include <options.h>
21 #include <iomanip>
22 #include <guiutils.h>
23 #include <mytime.h>
24 #include <imagearea.h>
25 #include <cursormanager.h>
26 #include "config.h"
28 struct ZoomStep {
29 Glib::ustring label;
30 double zoom;
31 int czoom;
34 ZoomStep zoomSteps[] = {"10%", 0.1, 10,
35 "12.5%", 0.125, 8,
36 "16.6%", 1.0/6.0, 6,
37 "20%", 0.2, 5,
38 "25%", 0.25, 4,
39 "33%", 1.0/3.0, 3,
40 "50%", 0.5, 2,
41 "100%", 1.0, 1000,
42 "200%", 2.0, 2000,
43 "300%", 3.0, 3000,
44 "400%", 4.0, 4000,
45 "500%", 5.0, 5000,
46 "600%", 6.0, 6000,
47 "700%", 7.0, 7000,
48 "800%", 8.0, 8000};
49 #define MAXZOOMSTEPS 14
50 #define ZOOM11INDEX 7
52 CropWindow::CropWindow (ImageArea* parent, rtengine::StagedImageProcessor* ipc_)
53 : iarea(parent), cropgl(NULL), xpos(30), ypos(30), imgX(0), imgY(0), imgW(1), imgH(1),
54 titleHeight(30), sideBorderWidth(3), upperBorderWidth(1), lowerBorderWidth(3), sepWidth(2),
55 cropZoom(ZOOM11INDEX), deleted(false), onResizeArea(false), fitZoom(false),
56 fitZoomEnabled(true), decorated(true), backColor(0), observedCropWin(NULL),
57 pmlistener(NULL) {
59 Glib::RefPtr<Pango::Context> context = parent->get_pango_context () ;
60 Pango::FontDescription fontd = context->get_font_description ();
61 fontd.set_weight (Pango::WEIGHT_BOLD);
62 fontd.set_size(8*Pango::SCALE);
63 context->set_font_description (fontd);
64 cropLabel = "100%";
65 Glib::RefPtr<Pango::Layout> cllayout = parent->create_pango_layout("1000%");
67 int iw, ih;
68 cllayout->get_pixel_size (iw, ih);
70 titleHeight = ih;
72 resizeSurface = Cairo::ImageSurface::create_from_png (GET_DATA_PATH(argv0)+"/images/resize.png");
73 bZoomIn = new LWButton (Cairo::ImageSurface::create_from_png (GET_DATA_PATH(argv0)+"/images/gtk-zoom-in.png"), 0, NULL, LWButton::Left, LWButton::Center, "Zoom In");
74 bZoomOut = new LWButton (Cairo::ImageSurface::create_from_png (GET_DATA_PATH(argv0)+"/images/gtk-zoom-out.png"), 1, NULL, LWButton::Left, LWButton::Center, "Zoom Out");
75 bZoom100 = new LWButton (Cairo::ImageSurface::create_from_png (GET_DATA_PATH(argv0)+"/images/gtk-zoom-100.png"), 2, NULL, LWButton::Left, LWButton::Center, "Zoom 100/%");
76 bZoomFit = new LWButton (Cairo::ImageSurface::create_from_png (GET_DATA_PATH(argv0)+"/images/gtk-zoom-fit.png"), 3, NULL, LWButton::Left, LWButton::Center, "Zoom Fit");
77 bClose = new LWButton (Cairo::ImageSurface::create_from_png (GET_DATA_PATH(argv0)+"/images/gtk-close.png"), 4, NULL, LWButton::Right, LWButton::Center, "Close");
79 buttonSet.add (bZoomIn);
80 buttonSet.add (bZoomOut);
81 buttonSet.add (bZoom100);
82 buttonSet.add (bClose);
84 buttonSet.setColors (Gdk::Color("black"), Gdk::Color("white"));
85 buttonSet.setButtonListener (this);
87 int bsw, bsh;
88 buttonSet.getMinimalDimensions (bsw, bsh);
90 if (bsh>titleHeight)
91 titleHeight = bsh;
93 minWidth = bsw + iw + 2*sideBorderWidth;
95 setSize (300, 300);
96 cropHandler.newImage (ipc_);
97 cropHandler.setPosition (0,0);
98 cropHandler.setEnabled (true);
99 cropHandler.setCropHandlerListener (this);
100 state = SNormal;
103 CropWindow::~CropWindow () {
107 void CropWindow::setPosition (int x, int y) {
109 if (y<0)
110 y = 0;
111 xpos = x;
112 ypos = y;
113 if (decorated)
114 buttonSet.arrangeButtons (xpos + sideBorderWidth, ypos + upperBorderWidth, width - 2*sideBorderWidth, titleHeight);
117 void CropWindow::getPosition (int& x, int& y) {
119 x = xpos;
120 y = ypos;
123 void CropWindow::getCropPosition (int& x, int& y) {
125 int cropX, cropY;
126 cropHandler.getPosition (cropX, cropY);
127 if (state!=SCropImgMove) {
128 x = cropX;
129 y = cropY;
131 else {
132 x = cropX + action_x;
133 y = cropY + action_y;
137 void CropWindow::getCropRectangle (int& x, int& y, int& w, int& h) {
139 int cropX, cropY, cropW, cropH;
140 cropHandler.getPosition (cropX, cropY);
141 cropHandler.getSize (cropW, cropH);
142 if (state!=SCropImgMove) {
143 x = cropX;
144 y = cropY;
146 else {
147 x = cropX + action_x;
148 y = cropY + action_y;
150 if (state!=SCropWinResize) {
151 w = cropW;
152 h = cropH;
154 else {
155 w = imgAreaW;
156 h = imgAreaH;
158 cropHandler.cutRectToImgBounds (x, y, w, h);
161 void CropWindow::setCropPosition (int x, int y) {
163 cropHandler.setPosition (x, y);
164 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
165 (*i)->cropPositionChanged (this);
168 void CropWindow::setSize (int w, int h, bool norefresh) {
170 width = w;
171 height = h;
173 fitZoom = false;
175 if (width<minWidth)
176 width = minWidth;
177 if (height<64)
178 height = 64;
180 if (decorated) {
181 imgAreaX = sideBorderWidth;
182 imgAreaY = upperBorderWidth + titleHeight + sepWidth;
183 imgAreaW = width - 2*sideBorderWidth;
184 imgAreaH = height - lowerBorderWidth - titleHeight - sepWidth - upperBorderWidth;
185 buttonSet.arrangeButtons (xpos + sideBorderWidth, ypos + upperBorderWidth, width - 2*sideBorderWidth, titleHeight);
187 else {
188 imgAreaX = imgAreaY = 0;
189 imgAreaW = width;
190 imgAreaH = height;
194 if (!norefresh)
195 cropHandler.setWSize (imgAreaW, imgAreaH);
196 iarea->redraw ();
199 void CropWindow::getSize (int& w, int& h) {
201 w = width;
202 h = height;
205 void CropWindow::getCropSize (int& w, int& h) {
207 w = imgAreaW;
208 h = imgAreaH;
211 bool CropWindow::isInside (int x, int y) {
213 return x>=xpos && x<xpos+width && y>=ypos && y<ypos+height;
216 void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) {
218 iarea->grabFocus (this);
219 if (button==1 && type==GDK_2BUTTON_PRESS && onArea (CropImage, x, y) && (state==SNormal || state==SCropImgMove)) {
220 if (fitZoomEnabled) {
221 if (fitZoom) {
222 translateCoord (x, y, action_x, action_y);
223 changeZoom (ZOOM11INDEX, true, action_x, action_y);
224 fitZoom = false;
226 else
227 zoomFit ();
229 else
230 zoom11 ();
231 state = SNormal;
233 else if (button==1 && type==GDK_2BUTTON_PRESS && onArea (CropBorder, x, y)) {
234 backColor = (backColor+1) % 3;
236 else if (button==1 && type==GDK_BUTTON_PRESS && state==SNormal && onArea (CropToolBar, x, y)) {
237 if (!decorated || !buttonSet.pressNotify (x, y)) {
238 state = SCropWinMove;
239 action_x = x;
240 action_y = y;
241 press_x = xpos;
242 press_y = ypos;
245 else if (button==1 && type==GDK_BUTTON_PRESS && state==SNormal && onArea (CropResize, x, y)) {
246 state = SCropWinResize;
247 action_x = x;
248 action_y = y;
249 press_x = width;
250 press_y = height;
252 else if (button==1 && type==GDK_BUTTON_PRESS && state==SNormal && onArea (CropImage, x, y)) {
253 if (onArea (CropTop, x, y)) {
254 state = SResizeH1;
255 press_y = y;
256 action_y = cropHandler.cropParams.y;
258 else if (onArea (CropBottom, x, y)) {
259 state = SResizeH2;
260 press_y = y;
261 action_y = cropHandler.cropParams.h;
263 else if (onArea (CropLeft, x, y)) {
264 state = SResizeW1;
265 press_x = x;
266 action_x = cropHandler.cropParams.x;
268 else if (onArea (CropRight, x, y)) {
269 state = SResizeW2;
270 press_x = x;
271 action_x = cropHandler.cropParams.w;
273 else if (onArea (CropObserved, x, y)) {
274 state = SObservedMove;
275 press_x = x;
276 press_y = y;
278 else if ((bstate & GDK_SHIFT_MASK) && onArea (CropInside, x, y)) {
279 state = SCropMove;
280 press_x = x;
281 press_y = y;
282 action_x = cropHandler.cropParams.x;
283 action_y = cropHandler.cropParams.y;
285 else if (iarea->getToolMode () == TMHand) {
286 state = SCropImgMove;
287 action_x = 0;
288 action_y = 0;
289 press_x = x;
290 press_y = y;
292 else if (iarea->getToolMode () == TMStraighten) {
293 state = SRotateSelecting;
294 press_x = x;
295 press_y = y;
296 action_x = x;
297 action_y = y;
298 rot_deg = 0;
300 else if (iarea->getToolMode () == TMSpotWB) {
301 translateCoord (x, y, action_x, action_y);
302 iarea->spotWBSelected (action_x, action_y);
304 else if (iarea->getToolMode () == TMCropSelect && cropgl) {
305 state = SCropSelecting;
306 translateCoord (x, y, press_x, press_y);
307 cropHandler.cropParams.x = press_x;
308 cropHandler.cropParams.y = press_y;
309 cropHandler.cropParams.w = cropHandler.cropParams.h = 1;
310 cropgl->cropInit (cropHandler.cropParams.x, cropHandler.cropParams.y, cropHandler.cropParams.w, cropHandler.cropParams.h);
313 if (button==3) {
314 state = SNormal;
315 iarea->setToolHand ();
317 iarea->redraw ();
318 updateCursor (x, y);
321 void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) {
323 if (state==SCropWinResize) {
324 setSize (press_x + x - action_x, press_y + y - action_y);
325 state = SNormal;
326 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
327 (*i)->cropWindowSizeChanged (this);
329 else if (state==SCropImgMove) {
330 int cropX, cropY;
331 cropHandler.getPosition (cropX, cropY);
332 cropHandler.setPosition (cropX + action_x, cropY + action_y);
333 cropHandler.getPosition (cropX, cropY);
334 state = SNormal;
335 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
336 (*i)->cropPositionChanged (this);
338 else if (state==SRotateSelecting) {
339 iarea->straightenReady (rot_deg);
340 iarea->setToolHand ();
342 else if (state==SObservedMove) {
343 observedCropWin->remoteMoveReady ();
344 state = SNormal;
346 if (cropgl && (state==SCropSelecting || state==SResizeH1 || state==SResizeH2 || state==SResizeW1 || state==SResizeW2 || state==SCropMove)) {
347 cropgl->cropManipReady ();
348 iarea->setToolHand ();
351 if (decorated)
352 buttonSet.releaseNotify (x, y);
353 if (deleted)
354 return;
356 state = SNormal;
357 iarea->grabFocus (NULL);
358 iarea->redraw ();
359 updateCursor (x, y);
362 void CropWindow::pointerMoved (int x, int y) {
364 if (state==SCropWinMove) {
365 setPosition (press_x + x - action_x, press_y + y - action_y);
366 iarea->redraw ();
368 else if (state==SCropWinResize) {
369 setSize (press_x + x - action_x, press_y + y - action_y, true);
370 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
371 (*i)->cropWindowSizeChanged (this);
372 iarea->redraw ();
374 else if (state==SCropImgMove) {
375 action_x = (press_x - x) / zoomSteps[cropZoom].zoom;
376 action_y = (press_y - y) / zoomSteps[cropZoom].zoom;
377 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
378 (*i)->cropPositionChanged (this);
379 iarea->redraw ();
381 else if (state==SRotateSelecting) {
382 action_x = x;
383 action_y = y;
384 iarea->redraw ();
386 else if (state==SNormal && iarea->getToolMode () == TMSpotWB) {
387 action_x = x;
388 action_y = y;
389 iarea->redraw ();
391 else if (state==SResizeH1 && cropgl) {
392 int oy = cropHandler.cropParams.y;
393 cropHandler.cropParams.y = action_y + (y-press_y) / zoomSteps[cropZoom].zoom;
394 cropHandler.cropParams.h += oy - cropHandler.cropParams.y;
395 cropgl->cropHeight1Resized (cropHandler.cropParams.x, cropHandler.cropParams.y, cropHandler.cropParams.w, cropHandler.cropParams.h);
396 iarea->redraw ();
398 else if (state==SResizeH2 && cropgl) {
399 cropHandler.cropParams.h = action_y + (y-press_y) / zoomSteps[cropZoom].zoom;
400 cropgl->cropHeight2Resized (cropHandler.cropParams.x, cropHandler.cropParams.y, cropHandler.cropParams.w, cropHandler.cropParams.h);
401 iarea->redraw ();
403 else if (state==SResizeW1 && cropgl) {
404 int ox = cropHandler.cropParams.x;
405 cropHandler.cropParams.x = action_x + (x-press_x) / zoomSteps[cropZoom].zoom;
406 cropHandler.cropParams.w += ox - cropHandler.cropParams.x;
407 cropgl->cropWidth1Resized (cropHandler.cropParams.x, cropHandler.cropParams.y, cropHandler.cropParams.w, cropHandler.cropParams.h);
408 iarea->redraw ();
410 else if (state==SResizeW2 && cropgl) {
411 cropHandler.cropParams.w = action_x + (x-press_x) / zoomSteps[cropZoom].zoom;
412 cropgl->cropWidth2Resized (cropHandler.cropParams.x, cropHandler.cropParams.y, cropHandler.cropParams.w, cropHandler.cropParams.h);
413 iarea->redraw ();
415 else if (state==SCropMove && cropgl) {
416 cropHandler.cropParams.x = action_x + (x-press_x) / zoomSteps[cropZoom].zoom;
417 cropHandler.cropParams.y = action_y + (y-press_y) / zoomSteps[cropZoom].zoom;
418 cropgl->cropMoved (cropHandler.cropParams.x, cropHandler.cropParams.y, cropHandler.cropParams.w, cropHandler.cropParams.h);
419 iarea->redraw ();
421 else if (state==SCropSelecting && cropgl) {
422 translateCoord (x, y, action_x, action_y);
423 int cx1 = press_x, cy1 = press_y;
424 int cx2 = action_x, cy2 = action_y;
425 cropgl->cropResized (cx1, cy1, cx2, cy2);
426 if (cx2 > cx1) {
427 cropHandler.cropParams.x = cx1;
428 cropHandler.cropParams.w = cx2 - cx1 + 1;
430 else {
431 cropHandler.cropParams.x = cx2;
432 cropHandler.cropParams.w = cx1 - cx2 + 1;
434 if (cy2 > cy1) {
435 cropHandler.cropParams.y = cy1;
436 cropHandler.cropParams.h = cy2 - cy1 + 1;
438 else {
439 cropHandler.cropParams.y = cy2;
440 cropHandler.cropParams.h = cy1 - cy2 + 1;
442 iarea->redraw ();
444 else if (state==SObservedMove) {
445 observedCropWin->remoteMove ((x - press_x)/zoomSteps[cropZoom].zoom, (y - press_y)/zoomSteps[cropZoom].zoom);
446 iarea->redraw ();
448 updateCursor (x, y);
450 bool oRA = onArea (CropResize, x, y);
451 if (oRA!=onResizeArea) {
452 onResizeArea = oRA;
453 iarea->redraw ();
456 if (decorated)
457 buttonSet.motionNotify (x, y);
459 if (pmlistener) {
460 int mx, my;
461 translateCoord (x, y, mx, my);
462 if (!onArea (CropImage, x, y) || !cropHandler.cropPixbuf)
463 pmlistener->pointerMoved (false, mx, my, -1, -1, -1);
464 else {
465 cropHandler.cimg.lock ();
466 int vx = x - xpos - imgX;
467 int vy = y - ypos - imgY;
468 guint8* pix = cropHandler.cropPixbuf->get_pixels() + vy*cropHandler.cropPixbuf->get_rowstride() + vx*3;
469 if (vx < cropHandler.cropPixbuf->get_width() && vy < cropHandler.cropPixbuf->get_height())
470 pmlistener->pointerMoved (true, mx, my, pix[0], pix[1], pix[2]);
471 cropHandler.cimg.unlock ();
476 bool CropWindow::onArea (CursorArea a, int x, int y) {
478 int CROPRESIZEBORDER = 6 / zoomSteps[cropZoom].zoom;
479 int x1, y1, w, h;
480 switch (a) {
481 case CropWinButtons:
482 return decorated && buttonSet.inside (x, y);
483 case CropToolBar:
484 return x>xpos && y>ypos && x<xpos+width-1 && y<ypos+imgAreaY;
485 case CropImage:
486 return x>=xpos+imgX && y>=ypos+imgY && x<xpos+imgX+imgW && y<ypos+imgY+imgH;
487 case CropBorder:
488 return
489 (x>=xpos+imgAreaX && y>=ypos+imgAreaY && x<xpos+imgAreaX+imgAreaW && y<ypos+imgAreaY+imgAreaH) &&
490 !(x>=xpos+imgX && y>=ypos+imgY && x<xpos+imgX+imgW && y<ypos+imgY+imgH);
491 case CropTop:
492 translateCoord (x, y, x1, y1);
493 return cropHandler.cropParams.enabled &&
494 x1>cropHandler.cropParams.x+CROPRESIZEBORDER &&
495 x1<cropHandler.cropParams.x+cropHandler.cropParams.w-1-CROPRESIZEBORDER &&
496 y1>cropHandler.cropParams.y-CROPRESIZEBORDER &&
497 y1<cropHandler.cropParams.y+CROPRESIZEBORDER;
498 case CropBottom:
499 translateCoord (x, y, x1, y1);
500 return cropHandler.cropParams.enabled &&
501 x1>cropHandler.cropParams.x+CROPRESIZEBORDER &&
502 x1<cropHandler.cropParams.x+cropHandler.cropParams.w-1-CROPRESIZEBORDER &&
503 y1>cropHandler.cropParams.y+cropHandler.cropParams.h-1-CROPRESIZEBORDER &&
504 y1<cropHandler.cropParams.y+cropHandler.cropParams.h-1+CROPRESIZEBORDER;
505 case CropLeft:
506 translateCoord (x, y, x1, y1);
507 return cropHandler.cropParams.enabled &&
508 y1>cropHandler.cropParams.y+CROPRESIZEBORDER &&
509 y1<cropHandler.cropParams.y+cropHandler.cropParams.h-1-CROPRESIZEBORDER &&
510 x1>cropHandler.cropParams.x-CROPRESIZEBORDER &&
511 x1<cropHandler.cropParams.x+CROPRESIZEBORDER;
512 case CropRight:
513 translateCoord (x, y, x1, y1);
514 return cropHandler.cropParams.enabled &&
515 y1>cropHandler.cropParams.y+CROPRESIZEBORDER &&
516 y1<cropHandler.cropParams.y+cropHandler.cropParams.h-1-CROPRESIZEBORDER &&
517 x1>cropHandler.cropParams.x+cropHandler.cropParams.w-1-CROPRESIZEBORDER &&
518 x1<cropHandler.cropParams.x+cropHandler.cropParams.w-1+CROPRESIZEBORDER;
519 case CropInside:
520 translateCoord (x, y, x1, y1);
521 return cropHandler.cropParams.enabled &&
522 y1>cropHandler.cropParams.y &&
523 y1<cropHandler.cropParams.y+cropHandler.cropParams.h-1 &&
524 x1>cropHandler.cropParams.x &&
525 x1<cropHandler.cropParams.x+cropHandler.cropParams.w-1;
526 case CropResize:
527 return decorated && x>=xpos+width-16 && y>=ypos+height-16 && x<xpos+width && y<ypos+height;
528 case CropObserved:
529 if (!observedCropWin)
530 return false;
531 getObservedFrameArea (x1, y1, w, h);
532 return x>x1-6 && y>y1-6 && x<x1+w-1+6 && y<y1+h-1+6 &&
533 !(x>x1+2 && y>y1+2 && x<x1+w-1-2 && y<y1+h-1-2);
535 return false;
538 void CropWindow::updateCursor (int x, int y) {
540 ToolMode tm = iarea->getToolMode ();
542 if (state==SNormal) {
543 if (onArea (CropWinButtons, x, y))
544 cursorManager.setCursor (iarea->get_window(), CSArrow);
545 else if (onArea (CropToolBar, x, y))
546 cursorManager.setCursor (iarea->get_window(), CSMove);
547 else if (onArea (CropResize, x, y))
548 cursorManager.setCursor (iarea->get_window(), CSResizeDiagonal);
549 else if (tm==TMHand && (onArea (CropTop, x, y) || onArea (CropBottom, x, y)))
550 cursorManager.setCursor (iarea->get_window(), CSResizeHeight);
551 else if (tm==TMHand && (onArea (CropLeft, x, y) || onArea (CropRight, x, y)))
552 cursorManager.setCursor (iarea->get_window(), CSResizeWidth);
553 else if (onArea (CropImage, x, y)) {
554 if (tm==TMHand) {
555 if (onArea (CropObserved, x, y))
556 cursorManager.setCursor (iarea->get_window(), CSMove);
557 else
558 cursorManager.setCursor (iarea->get_window(), CSOpenHand);
560 else if (tm==TMSpotWB)
561 cursorManager.setCursor (iarea->get_window(), CSSpotWB);
562 else if (tm==TMCropSelect)
563 cursorManager.setCursor (iarea->get_window(), CSCropSelect);
564 else if (tm==TMStraighten)
565 cursorManager.setCursor (iarea->get_window(), CSStraighten);
567 else
568 cursorManager.setCursor (iarea->get_window(), CSArrow);
570 else if (state==SCropSelecting)
571 cursorManager.setCursor (iarea->get_window(), CSCropSelect);
572 else if (state==SRotateSelecting)
573 cursorManager.setCursor (iarea->get_window(), CSStraighten);
574 else if (state==SCropMove || state==SCropWinMove || state==SObservedMove)
575 cursorManager.setCursor (iarea->get_window(), CSMove);
576 else if (state==SHandMove || state==SCropImgMove)
577 cursorManager.setCursor (iarea->get_window(), CSClosedHand);
578 else if (state==SResizeW1 || state==SResizeW2)
579 cursorManager.setCursor (iarea->get_window(), CSResizeWidth);
580 else if (state==SResizeH1 || state==SResizeH2)
581 cursorManager.setCursor (iarea->get_window(), CSResizeHeight);
582 else if (state==SCropWinResize)
583 cursorManager.setCursor (iarea->get_window(), CSResizeDiagonal);
586 void CropWindow::expose (Cairo::RefPtr<Cairo::Context> cr) {
588 MyTime t1, t2, t3, t4;
590 t1.set ();
592 if (decorated)
593 drawDecoration (cr);
595 int x = xpos, y = ypos, h = height, w = width;
597 // draw border
598 if (backColor==0) {
599 Gdk::Color cback = iarea->get_style()->get_bg(Gtk::STATE_NORMAL);
600 cr->set_source_rgb (cback.get_red_p(), cback.get_green_p(), cback.get_blue_p());
602 else if (backColor==1)
603 cr->set_source_rgb (0,0,0);
604 else if (backColor==2)
605 cr->set_source_rgb (1,1,1);
607 cr->rectangle (x+imgAreaX+0.5, y+imgAreaY+0.5, imgAreaW, imgAreaH);
608 cr->stroke_preserve ();
609 cr->fill ();
611 cropHandler.cimg.lock ();
612 // draw image
613 if (state==SCropImgMove || state==SCropWinResize) {
614 // draw a rough image
615 int cropX, cropY;
616 cropHandler.getPosition (cropX, cropY);
617 if (state==SCropImgMove) {
618 cropX += action_x;
619 cropY += action_y;
621 Glib::RefPtr<Gdk::Pixbuf> rough = iarea->getPreviewHandler()->getRoughImage (cropX, cropY, imgAreaW, imgAreaH, zoomSteps[cropZoom].zoom);
622 if (rough) {
623 iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), rough, 0, 0, x+imgAreaX+(imgAreaW-rough->get_width())/2, y+imgAreaY+(imgAreaH-rough->get_height())/2, -1, -1, Gdk::RGB_DITHER_NORMAL, 0, 0);
624 // if (cropHandler.cropParams.enabled)
625 // drawCrop (cr, x+imgX, y+imgY, imgW, imgH, cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams);
628 else {
629 if (cropHandler.cropPixbuf) {
630 imgW = cropHandler.cropPixbuf->get_width ();
631 imgH = cropHandler.cropPixbuf->get_height ();
632 imgX = imgAreaX + (imgAreaW-imgW)/2;
633 imgY = imgAreaY + (imgAreaH-imgH)/2;
634 // PERFORMANCE BOTTLENECK STARTS HERE
635 t3.set ();
636 bool showcs = iarea->indClippedPanel->showClippedShadows();
637 bool showch = iarea->indClippedPanel->showClippedHighlights();
638 if (showcs || showch) {
639 Glib::RefPtr<Gdk::Pixbuf> tmp = cropHandler.cropPixbuf->copy ();
640 guint8* pix = tmp->get_pixels();
641 for (int i=0; i<tmp->get_height(); i++)
642 for (int j=0; j<tmp->get_width(); j++) {
643 guint8* curr = pix + i*tmp->get_rowstride () + j*3;
644 if (showch && (curr[0]>=options.highlightThreshold || curr[1]>=options.highlightThreshold || curr[2]>=options.highlightThreshold))
645 curr[0] = curr[1] = curr[2] = 0;
646 else if (showcs && (curr[0]<=options.shadowThreshold || curr[1]<=options.shadowThreshold || curr[2]<=options.shadowThreshold))
647 curr[0] = curr[1] = curr[2] = 255;
649 iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), tmp, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0);
651 else
652 iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), cropHandler.cropPixbuf, 0, 0, x+imgX, y+imgY, -1, -1, Gdk::RGB_DITHER_NONE, 0, 0);
653 t4.set ();
654 // END OF BOTTLENECK
655 if (cropHandler.cropParams.enabled) {
656 int cropX, cropY;
657 cropHandler.getPosition (cropX, cropY);
658 drawCrop (cr, x+imgX, y+imgY, imgW, imgH, cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams);
661 else {
662 int cropX, cropY;
663 cropHandler.getPosition (cropX, cropY);
664 Glib::RefPtr<Gdk::Pixbuf> rough = iarea->getPreviewHandler()->getRoughImage (cropX, cropY, imgAreaW, imgAreaH, zoomSteps[cropZoom].zoom);
665 if (rough) {
666 iarea->get_window()->draw_pixbuf (iarea->get_style()->get_base_gc(Gtk::STATE_NORMAL), rough, 0, 0, x+imgAreaX+(imgAreaW-rough->get_width())/2, y+imgAreaY+(imgAreaH-rough->get_height())/2, -1, -1, Gdk::RGB_DITHER_NORMAL, 0, 0);
667 if (cropHandler.cropParams.enabled) {
668 int cropX, cropY;
669 cropHandler.getPosition (cropX, cropY);
670 drawCrop (cr, x+imgX, y+imgY, imgW, imgH, cropX, cropY, zoomSteps[cropZoom].zoom, cropHandler.cropParams);
676 if (observedCropWin)
677 drawObservedFrame (cr);
679 // if cursor stays above resize area, draw the icon
680 if (decorated && (state==SCropWinResize || onResizeArea)) {
681 int rw = resizeSurface->get_width ();
682 int rh = resizeSurface->get_height ();
683 cr->set_source_rgb (0.5,0.5,0.5);
684 cr->rectangle (x+w-1.5-rw-1, y+h-1.5-rh-1, rw+1, rh+1);
685 cr->stroke_preserve ();
686 cr->fill ();
687 cr->set_source (resizeSurface, x+w-1.5-rw, y+h-1.5-rh);
688 cr->paint ();
689 cr->set_source_rgb (0,0,0);
690 cr->move_to (x+w-2.5-rw, y+h-1.5);
691 cr->line_to (x+w-2.5-rw, y+h-2.5-rh);
692 cr->line_to (x+w-1.5, y+h-2.5-rh);
693 cr->stroke ();
695 if (state==SRotateSelecting)
696 drawStraightenGuide (cr);
697 if (state==SNormal && iarea->getToolMode () == TMSpotWB)
698 drawSpotWBRectangle (cr);
700 t2.set ();
701 cropHandler.cimg.unlock ();
702 // printf ("etime --> %d, %d\n", t2.etime (t1), t4.etime (t3));
705 void CropWindow::zoomIn () {
707 changeZoom (cropZoom+1);
708 fitZoom = false;
711 void CropWindow::zoomOut () {
713 changeZoom (cropZoom-1);
714 fitZoom = false;
717 void CropWindow::zoom11 () {
719 changeZoom (ZOOM11INDEX);
720 fitZoom = false;
723 double CropWindow::getZoom () {
725 return zoomSteps[cropZoom].zoom;
728 void CropWindow::setZoom (double zoom) {
730 int cz = MAXZOOMSTEPS;
731 if (zoom < zoomSteps[0].zoom)
732 cz = 0;
733 else
734 for (int i=0; i<MAXZOOMSTEPS; i++)
735 if (zoomSteps[i].zoom <= zoom && zoomSteps[i+1].zoom > zoom) {
736 cz = i;
737 break;
739 changeZoom (cz, false);
742 void CropWindow::zoomFit () {
744 double z = cropHandler.getFitZoom ();
745 int cz = MAXZOOMSTEPS;
746 if (z < zoomSteps[0].zoom)
747 cz = 0;
748 else
749 for (int i=0; i<MAXZOOMSTEPS; i++)
750 if (zoomSteps[i].zoom <= z && zoomSteps[i+1].zoom > z) {
751 cz = i;
752 break;
754 changeZoom (cz);
755 fitZoom = true;
758 void CropWindow::buttonPressed (LWButton* button, int actionCode, void* actionData) {
760 if (button==bZoomIn) // zoom in
761 zoomIn ();
762 else if (button==bZoomOut) // zoom out
763 zoomOut ();
764 else if (button==bZoom100) // zoom 100
765 zoom11 ();
766 else if (button==bClose) {// close
767 deleted = true;
768 iarea->cropWindowClosed (this);
772 void CropWindow::redrawNeeded (LWButton* button) {
774 iarea->redraw ();
777 void CropWindow::changeZoom (int zoom, bool notify, int centerx, int centery) {
779 cropZoom = zoom;
780 if (cropZoom<0)
781 cropZoom = 0;
782 else if (cropZoom>MAXZOOMSTEPS)
783 cropZoom = MAXZOOMSTEPS;
785 cropLabel = zoomSteps[cropZoom].label;
786 cropHandler.setZoom (zoomSteps[cropZoom].czoom, centerx, centery);
787 if (notify)
788 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
789 (*i)->cropZoomChanged (this);
790 iarea->redraw ();
793 void CropWindow::translateCoord (int phyx, int phyy, int& imgx, int& imgy) {
795 int cropX, cropY;
796 cropHandler.getPosition (cropX, cropY);
797 imgx = cropX + (phyx - xpos - imgX)/zoomSteps[cropZoom].zoom;
798 imgy = cropY + (phyy - ypos - imgY)/zoomSteps[cropZoom].zoom;
801 void CropWindow::drawDecoration (Cairo::RefPtr<Cairo::Context> cr) {
803 int x = xpos, y = ypos;
804 // prepare label
805 Glib::RefPtr<Pango::Context> context = iarea->get_pango_context () ;
806 Pango::FontDescription fontd = context->get_font_description ();
807 fontd.set_weight (Pango::WEIGHT_BOLD);
808 fontd.set_size(8*Pango::SCALE);
809 context->set_font_description (fontd);
810 Glib::RefPtr<Pango::Layout> cllayout = iarea->create_pango_layout(cropLabel);
811 int iw, ih;
812 cllayout->get_pixel_size (iw, ih);
814 // draw decoration (border)
815 int h = height, w = width;
816 cr->set_source_rgb (0,0,0);
817 cr->set_line_width (1.0);
818 cr->move_to (x+0.5, y+h-0.5);
819 cr->line_to (x+0.5, y+0.5);
820 cr->line_to (x+w-0.5, y+0.5);
821 cr->stroke ();
822 cr->set_source_rgb (1,1,1);
823 cr->move_to (x+w-0.5, y+0.5);
824 cr->line_to (x+w-0.5, y+h-0.5);
825 cr->line_to (x+0.5, y+h-0.5);
826 cr->stroke ();
827 cr->set_source_rgb (0.5,0.5,0.5);
828 cr->rectangle (x+1.5, y+1.5+titleHeight, w-3, h-titleHeight-3);
829 cr->stroke ();
830 cr->set_source_rgb (1,1,1);
831 cr->move_to (x+2.5, y+h-2.5);
832 cr->line_to (x+2.5, y+titleHeight+2.5);
833 cr->line_to (x+w-2.5, y+titleHeight+2.5);
834 cr->stroke ();
835 cr->set_source_rgb (0,0,0);
836 cr->move_to (x+w-2.5, y+titleHeight+2.5);
837 cr->line_to (x+w-2.5, y+h-2.5);
838 cr->line_to (x+2.5, y+h-2.5);
839 cr->stroke ();
840 cr->set_source_rgb (0.5,0.5,0.5);
841 cr->rectangle (x+1.5, y+1.5, w-3, titleHeight);
842 cr->stroke_preserve ();
843 cr->fill ();
845 // draw label
846 cr->set_source_rgb (1,1,1);
847 cr->move_to (x+6+sideBorderWidth+bZoomIn->getIcon()->get_width()+bZoomOut->getIcon()->get_width()+bZoom100->getIcon()->get_width(), y+upperBorderWidth+(titleHeight-ih)/2);
848 cllayout->add_to_cairo_context (cr);
849 cr->fill ();
851 buttonSet.redraw (cr);
854 void CropWindow::drawStraightenGuide (Cairo::RefPtr<Cairo::Context> cr) {
856 if (action_x!=press_x || action_y!=press_y) {
857 double arg = (press_x-action_x) / sqrt((press_x-action_x)*(press_x-action_x)+(press_y-action_y)*(press_y-action_y));
858 double sol1, sol2;
859 double pi = M_PI;
860 if (press_y>action_y) {
861 sol1 = acos(arg)*180/pi;
862 sol2 = -acos(-arg)*180/pi;
864 else {
865 sol1 = acos(-arg)*180/pi;
866 sol2 = -acos(arg)*180/pi;
868 if (fabs(sol1)<fabs(sol2))
869 rot_deg = sol1;
870 else
871 rot_deg = sol2;
873 if (rot_deg<-45)
874 rot_deg = 90.0 + rot_deg;
875 else if (rot_deg>45)
876 rot_deg = - 90.0 + rot_deg;
878 else
879 rot_deg = 0;
881 Glib::RefPtr<Pango::Context> context = iarea->get_pango_context () ;
882 Pango::FontDescription fontd = context->get_font_description ();
883 fontd.set_weight (Pango::WEIGHT_BOLD);
884 fontd.set_size (8*Pango::SCALE);
885 context->set_font_description (fontd);
886 Glib::RefPtr<Pango::Layout> deglayout = iarea->create_pango_layout(Glib::ustring::compose ("%1 deg", Glib::ustring::format(std::setprecision(2), rot_deg)));
888 int x1 = press_x;
889 int y1 = press_y;
890 int y2 = action_y;
891 int x2 = action_x;
892 /* if (x1<0) x1 = 0;
893 if (y1<0) y1 = 0;
894 if (x2<0) x2 = 0;
895 if (y2<0) y2 = 0;
896 if (x2>=image->getWidth()) x2 = image->getWidth()-1;
897 if (y2>=image->getHeight()) y2 = image->getHeight()-1;
898 if (x1>=image->getWidth()) x1 = image->getWidth()-1;
899 if (y1>=image->getHeight()) y1 = image->getHeight()-1;
902 cr->set_line_width (1.5);
903 cr->set_source_rgb (1.0, 1.0, 1.0);
904 cr->move_to (x1, y1);
905 cr->line_to (x2, y2);
906 cr->stroke ();
907 cr->set_source_rgb (0.0, 0.0, 0.0);
908 std::valarray<double> ds (1);
909 ds[0] = 4;
910 cr->set_dash (ds, 0);
911 cr->move_to (x1, y1);
912 cr->line_to (x2, y2);
913 cr->stroke ();
915 if (press_x!=action_x && press_y!=action_y) {
916 cr->set_source_rgb (0.0, 0.0, 0.0);
917 cr->move_to ((x1+x2)/2+1, (y1+y2)/2+1);
918 deglayout->add_to_cairo_context (cr);
919 cr->move_to ((x1+x2)/2+1, (y1+y2)/2-1);
920 deglayout->add_to_cairo_context (cr);
921 cr->move_to ((x1+x2)/2-1, (y1+y2)/2+1);
922 deglayout->add_to_cairo_context (cr);
923 cr->move_to ((x1+x2)/2+1, (y1+y2)/2+1);
924 deglayout->add_to_cairo_context (cr);
925 cr->fill ();
926 cr->set_source_rgb (1.0, 1.0, 1.0);
927 cr->move_to ((x1+x2)/2, (y1+y2)/2);
928 deglayout->add_to_cairo_context (cr);
929 cr->fill ();
933 void CropWindow::drawSpotWBRectangle (Cairo::RefPtr<Cairo::Context> cr) {
935 int rectsize = iarea->getSpotWBRectSize ();
936 int x1 = action_x/zoomSteps[cropZoom].zoom - rectsize;
937 int y1 = action_y/zoomSteps[cropZoom].zoom - rectsize;
938 int y2 = action_y/zoomSteps[cropZoom].zoom + rectsize;
939 int x2 = action_x/zoomSteps[cropZoom].zoom + rectsize;
941 cr->set_line_width (1.0);
942 cr->rectangle (xpos+imgX-0.5, ypos+imgY-0.5, imgW, imgH);
943 cr->clip ();
945 cr->set_source_rgb (1.0, 1.0, 1.0);
946 cr->rectangle (x1*zoomSteps[cropZoom].zoom-1.5, y1*zoomSteps[cropZoom].zoom-1.5, x2*zoomSteps[cropZoom].zoom-x1*zoomSteps[cropZoom].zoom+2, y2*zoomSteps[cropZoom].zoom-y1*zoomSteps[cropZoom].zoom+2);
947 cr->stroke ();
948 cr->set_source_rgb (0.0, 0.0, 0.0);
949 cr->rectangle (x1*zoomSteps[cropZoom].zoom-0.5, y1*zoomSteps[cropZoom].zoom-0.5, x2*zoomSteps[cropZoom].zoom-x1*zoomSteps[cropZoom].zoom, y2*zoomSteps[cropZoom].zoom-y1*zoomSteps[cropZoom].zoom);
950 cr->stroke ();
952 cr->reset_clip ();
955 void CropWindow::getObservedFrameArea (int& x, int& y, int& w, int& h) {
957 int cropX, cropY, cropW, cropH;
958 observedCropWin->getCropRectangle (cropX, cropY, cropW, cropH);
959 int myCropX, myCropY, myCropW, myCropH;
960 getCropRectangle (myCropX, myCropY, myCropW, myCropH);
962 // translate it to screen coordinates
963 x = xpos + imgX + (cropX-myCropX)*zoomSteps[cropZoom].zoom;
964 y = ypos + imgY + (cropY-myCropY)*zoomSteps[cropZoom].zoom;
965 w = cropW * zoomSteps[cropZoom].zoom;
966 h = cropH * zoomSteps[cropZoom].zoom;
969 void CropWindow::drawObservedFrame (Cairo::RefPtr<Cairo::Context> cr) {
971 int x, y, w, h;
972 getObservedFrameArea (x, y, w, h);
974 cr->set_source_rgb (1.0, 1.0, 1.0);
975 cr->set_line_width (4);
976 cr->rectangle (x-2, y-2, w+4, h+4);
977 cr->stroke ();
978 cr->set_source_rgb (1.0, 0.0, 0.0);
979 cr->set_line_width (2);
980 cr->rectangle (x-2, y-2, w+4, h+4);
981 cr->stroke ();
984 void CropWindow::cropImageUpdated () {
986 iarea->redraw ();
989 void CropWindow::cropWindowChanged () {
991 if (!decorated)
992 iarea->updateScrollbars ();
993 iarea->redraw ();
996 void CropWindow::initialImageArrived () {
998 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
999 (*i)->initialImageArrived (this);
1003 void CropWindow::remoteMove (int deltaX, int deltaY) {
1005 state = SCropImgMove;
1006 action_x = deltaX;
1007 action_y = deltaY;
1008 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
1009 (*i)->cropPositionChanged (this);
1012 void CropWindow::remoteMoveReady () {
1014 int cropX, cropY;
1015 cropHandler.getPosition (cropX, cropY);
1016 cropHandler.setPosition (cropX + action_x, cropY + action_y);
1017 cropHandler.getPosition (cropX, cropY);
1018 state = SNormal;
1019 for (std::list<CropWindowListener*>::iterator i=listeners.begin(); i!=listeners.end(); i++)
1020 (*i)->cropPositionChanged (this);
1023 void CropWindow::delCropWindowListener (CropWindowListener* l) {
1025 std::list<CropWindowListener*>::iterator i=listeners.begin();
1026 while (i!=listeners.end())
1027 if (*i==l)
1028 i = listeners.erase (i);
1029 else
1030 i++;