1 /*
2  *  This file is part of RawTherapee.
3  *
4  *  Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
5  *
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.
10  *
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.
15  *
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/>.
18  */
19 #include "imagearea.h"
20 #include <ctime>
21 #include <cmath>
22 #include "options.h"
23 #include "multilangmgr.h"
24 #include "cropwindow.h"
25 #include "../rtengine/refreshmap.h"
26 #include "options.h"
27 
ImageArea(ImageAreaPanel * p)28 ImageArea::ImageArea(ImageAreaPanel* p):
29     parent(p), fullImageWidth(0), fullImageHeight(0),
30     alp_(nullptr)
31 {
32     get_style_context()->add_class("drawingarea");
33 
34     infotext = "";
35     cropgl = nullptr;
36     pmlistener = nullptr;
37     pmhlistener = nullptr;
38     focusGrabber = nullptr;
39     flawnOverWindow = nullptr;
40     mainCropWindow = nullptr;
41     previewHandler = nullptr;
42     showClippedH = false;
43     showClippedS = false;
44     listener = nullptr;
45 
46     zoomPanel = Gtk::manage (new ZoomPanel (this));
47     indClippedPanel = Gtk::manage (new IndicateClippedPanel (this));
48     previewModePanel =  Gtk::manage (new PreviewModePanel (this));
49     previewModePanel->get_style_context()->add_class("narrowbuttonbox");
50 
51     add_events(Gdk::LEAVE_NOTIFY_MASK);
52 
53     signal_size_allocate().connect( sigc::mem_fun(*this, &ImageArea::on_resized) );
54 
55     dirty = false;
56     ipc = nullptr;
57     iLinkedImageArea = nullptr;
58 }
59 
~ImageArea()60 ImageArea::~ImageArea ()
61 {
62 
63     for (auto cropWin : cropWins) {
64         delete cropWin;
65     }
66 
67     cropWins.clear ();
68 
69     if (mainCropWindow) {
70         delete mainCropWindow;
71     }
72 }
73 
on_realize()74 void ImageArea::on_realize()
75 {
76     Gtk::DrawingArea::on_realize();
77 
78 #if defined (__APPLE__)
79     // Workaround: disabling POINTER_MOTION_HINT_MASK as for gtk 2.24.22 the get_pointer() function is buggy for quartz and modifier mask is not updated correctly.
80     // This workaround should be removed when bug is fixed in GTK2 or when migrating to GTK3
81     add_events(Gdk::EXPOSURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK);
82 #else
83     add_events(Gdk::EXPOSURE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::POINTER_MOTION_HINT_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::SCROLL_MASK | Gdk::SMOOTH_SCROLL_MASK);
84 #endif
85 
86     Cairo::FontOptions cfo;
87     cfo.set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
88     get_pango_context ()->set_cairo_font_options (cfo);
89 }
90 
on_resized(Gtk::Allocation & req)91 void ImageArea::on_resized (Gtk::Allocation& req)
92 {
93     if (ipc && get_width() > 1) { // sometimes on_resize is called in some init state, causing wrong sizes
94         if (!mainCropWindow) {
95             mainCropWindow = new CropWindow (this, false, false);
96             mainCropWindow->setDecorated (false);
97             mainCropWindow->setFitZoomEnabled (true);
98             mainCropWindow->addCropWindowListener (this);
99             mainCropWindow->setCropGUIListener (cropgl);
100             mainCropWindow->setPointerMotionListener (pmlistener);
101             mainCropWindow->setPointerMotionHListener (pmhlistener);
102             mainCropWindow->setPosition (0, 0);
103             mainCropWindow->setSize (get_width(), get_height());  // this execute the refresh itself
104             mainCropWindow->enable();  // start processing !
105             if (alp_) {
106                 alp_->setAreaDrawListener(mainCropWindow);
107             }
108         } else {
109             mainCropWindow->setSize (get_width(), get_height());  // this execute the refresh itself
110         }
111 
112         parent->syncBeforeAfterViews();
113     }
114 }
115 
getImProcCoordinator() const116 rtengine::StagedImageProcessor* ImageArea::getImProcCoordinator() const
117 {
118     return ipc;
119 }
120 
setImProcCoordinator(rtengine::StagedImageProcessor * ipc_)121 void ImageArea::setImProcCoordinator(rtengine::StagedImageProcessor* ipc_)
122 {
123     if( !ipc_ ) {
124         focusGrabber = nullptr;
125 
126         for (auto cropWin : cropWins) {
127             delete cropWin;
128         }
129 
130         cropWins.clear();
131 
132         mainCropWindow->deleteColorPickers ();
133         mainCropWindow->setObservedCropWin (nullptr);
134     }
135 
136     ipc = ipc_;
137 
138 }
139 
setPreviewHandler(PreviewHandler * ph)140 void ImageArea::setPreviewHandler (PreviewHandler* ph)
141 {
142 
143     previewHandler = ph;
144 }
145 
on_style_updated()146 void ImageArea::on_style_updated ()
147 {
148 
149     // TODO: notify all crop windows that the style has been changed
150     queue_draw ();
151 }
152 
setInfoText(Glib::ustring text)153 void ImageArea::setInfoText (Glib::ustring text)
154 {
155 
156     infotext = text;
157 
158     Glib::RefPtr<Pango::Context> context = get_pango_context () ;
159     Pango::FontDescription fontd(get_style_context()->get_font());
160 
161     // update font
162     fontd.set_weight (Pango::WEIGHT_BOLD);
163     fontd.set_size (10 * Pango::SCALE);
164     context->set_font_description (fontd);
165 
166     // create text layout
167     Glib::RefPtr<Pango::Layout> ilayout = create_pango_layout("");
168     ilayout->set_markup(text);
169 
170     // get size of the text block
171     int iw, ih;
172     ilayout->get_pixel_size (iw, ih);
173 
174     // create BackBuffer
175     iBackBuffer.setDrawRectangle(Cairo::FORMAT_ARGB32, 0, 0, iw + 16, ih + 16, true);
176     iBackBuffer.setDestPosition(8, 8);
177 
178     Cairo::RefPtr<Cairo::Context> cr = iBackBuffer.getContext();
179 
180     // cleaning the back buffer (make it full transparent)
181     cr->set_source_rgba (0., 0., 0., 0.);
182     cr->set_operator (Cairo::OPERATOR_CLEAR);
183     cr->paint ();
184     cr->set_operator (Cairo::OPERATOR_OVER);
185 
186     // paint transparent black background
187     cr->set_source_rgba (0., 0., 0., 0.5);
188     cr->paint ();
189 
190     // paint text
191     cr->set_source_rgb (1.0, 1.0, 1.0);
192     cr->move_to (8, 8);
193     ilayout->add_to_cairo_context (cr);
194     cr->fill ();
195 
196 }
197 
infoEnabled(bool e)198 void ImageArea::infoEnabled (bool e)
199 {
200 
201     if (options.showInfo != e) {
202         options.showInfo = e;
203         queue_draw ();
204     }
205 }
206 
getCropWindow(int x,int y)207 CropWindow* ImageArea::getCropWindow (int x, int y)
208 {
209 
210     CropWindow* cw = mainCropWindow;
211 
212     for (auto cropWin : cropWins) {
213         if (cropWin->isInside (x, y)) {
214             return cropWin;
215         }
216     }
217 
218     return cw;
219 }
220 
redraw()221 void ImageArea::redraw ()
222 {
223     // dirty prevents multiple updates queued up
224     if (!dirty) {
225         dirty = true;
226         queue_draw ();
227     }
228 }
229 
switchPickerVisibility(bool isVisible)230 void ImageArea::switchPickerVisibility (bool isVisible)
231 {
232     redraw();
233 }
234 
on_draw(const::Cairo::RefPtr<Cairo::Context> & cr)235 bool ImageArea::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
236 {
237     dirty = false;
238 
239     /* HOMBRE: How do we replace that??
240 
241     if (event->count) {
242         return true;
243     }
244 
245      */
246 
247     if (mainCropWindow) {
248         mainCropWindow->expose (cr);
249     }
250 
251     for (std::list<CropWindow*>::reverse_iterator i = cropWins.rbegin(); i != cropWins.rend(); ++i) {
252         (*i)->expose (cr);
253     }
254 
255     if (options.showInfo && infotext != "") {
256         iBackBuffer.copySurface(cr);
257     }
258 
259 
260     return true;
261 }
262 
263 
on_motion_notify_event(GdkEventMotion * event)264 bool ImageArea::on_motion_notify_event (GdkEventMotion* event)
265 {
266     auto device = gdk_event_get_source_device(reinterpret_cast<GdkEvent *>(event));
267     double pressure = rtengine::RT_NAN;
268     if (device && event->axes) {
269         if (!gdk_device_get_axis(device, event->axes, GDK_AXIS_PRESSURE, &pressure)) {
270             pressure = rtengine::RT_NAN;
271         }
272     }
273 
274     if (focusGrabber) {
275         focusGrabber->pointerMoved (event->state, event->x, event->y, pressure);
276     } else {
277         CropWindow* cw = getCropWindow (event->x, event->y);
278 
279         if (cw) {
280             if (cw != flawnOverWindow) {
281                 if (flawnOverWindow) {
282                     flawnOverWindow->flawnOver(false);
283                 }
284 
285                 cw->flawnOver(true);
286                 flawnOverWindow = cw;
287             }
288 
289             cw->pointerMoved (event->state, event->x, event->y, pressure);
290         } else if (flawnOverWindow) {
291             flawnOverWindow->flawnOver(false);
292             flawnOverWindow = nullptr;
293         }
294     }
295 
296     return true;
297 }
298 
on_button_press_event(GdkEventButton * event)299 bool ImageArea::on_button_press_event (GdkEventButton* event)
300 {
301     auto device = gdk_event_get_source_device(reinterpret_cast<GdkEvent *>(event));
302     double pressure = rtengine::RT_NAN;
303     if (device && event->axes) {
304         if (!gdk_device_get_axis(device, event->axes, GDK_AXIS_PRESSURE, &pressure)) {
305             pressure = rtengine::RT_NAN;
306         }
307     }
308 
309     if (focusGrabber) {
310         focusGrabber->buttonPress(event->button, event->type, event->state, event->x, event->y, pressure);
311     } else {
312         CropWindow* cw = getCropWindow(event->x, event->y);
313 
314         if (cw) {
315             cw->buttonPress(event->button, event->type, event->state, event->x, event->y, pressure);
316         }
317     }
318 
319     return true;
320 }
321 
on_scroll_event(GdkEventScroll * event)322 bool ImageArea::on_scroll_event (GdkEventScroll* event)
323 {
324 
325 //    printf("ImageArea::on_scroll_event / delta_x=%.5f, delta_y=%.5f, direction=%d, type=%d, send_event=%d\n",
326 //            event->delta_x, event->delta_y, (int)event->direction, (int)event->type, event->send_event);
327 
328     CropWindow* cw = getCropWindow (event->x, event->y);
329     if (cw) {
330         cw->scroll (event->state, event->direction, event->x, event->y, event->delta_x, event->delta_y);
331     }
332 
333     return true;
334 }
335 
on_button_release_event(GdkEventButton * event)336 bool ImageArea::on_button_release_event (GdkEventButton* event)
337 {
338 
339     if (focusGrabber) {
340         focusGrabber->buttonRelease (event->button, event->type, event->state, event->x, event->y);
341     } else {
342         CropWindow* cw = getCropWindow (event->x, event->y);
343 
344         if (cw) {
345             cw->buttonRelease (event->button, event->type, event->state, event->x, event->y);
346         }
347     }
348 
349     return true;
350 }
351 
on_leave_notify_event(GdkEventCrossing * event)352 bool ImageArea::on_leave_notify_event  (GdkEventCrossing* event)
353 {
354     if (flawnOverWindow) {
355         flawnOverWindow->flawnOver(false);
356         flawnOverWindow = nullptr;
357     }
358 
359     if (focusGrabber) {
360         focusGrabber->flawnOver(false);
361         focusGrabber->leaveNotify (event);
362     } else {
363         CropWindow* cw = getCropWindow (event->x, event->y);
364 
365         if (cw) {
366             cw->flawnOver(false);
367             cw->leaveNotify (event);
368         }
369     }
370 
371     return true;
372 }
373 
subscribe(EditSubscriber * subscriber)374 void ImageArea::subscribe(EditSubscriber *subscriber)
375 {
376     EditDataProvider::subscribe(subscriber);
377 
378     mainCropWindow->setEditSubscriber(subscriber);
379     for (auto cropWin : cropWins) {
380         cropWin->setEditSubscriber(subscriber);
381     }
382 
383     if (listener && listener->getToolBar()) {
384         listener->getToolBar()->startEditMode ();
385     }
386 
387     if (subscriber && subscriber->getEditingType() == ET_OBJECTS) {
388         // In this case, no need to reprocess the image, so we redraw the image to display the geometry
389         queue_draw();
390     }
391 }
392 
unsubscribe()393 void ImageArea::unsubscribe()
394 {
395     bool wasObjectType = false;
396     EditSubscriber*  oldSubscriber = EditDataProvider::getCurrSubscriber();
397 
398     if (oldSubscriber && oldSubscriber->getEditingType() == ET_OBJECTS) {
399         wasObjectType = true;
400     }
401 
402     EditDataProvider::unsubscribe();
403 
404     // Ask the Crops to free-up edit mode buffers
405     mainCropWindow->setEditSubscriber(nullptr);
406     for (auto cropWin : cropWins) {
407         cropWin->setEditSubscriber(nullptr);
408     }
409 
410     setToolHand();
411 
412     if (listener && listener->getToolBar()) {
413         listener->getToolBar()->stopEditMode ();
414     }
415 
416     if (wasObjectType) {
417         queue_draw();
418     }
419 }
420 
getImageSize(int & w,int & h)421 void ImageArea::getImageSize (int &w, int&h)
422 {
423     if (ipc) {
424         w = ipc->getFullWidth();
425         h = ipc->getFullHeight();
426     } else {
427         w = h = 0;
428     }
429 }
430 
grabFocus(CropWindow * cw)431 void ImageArea::grabFocus (CropWindow* cw)
432 {
433 
434     focusGrabber = cw;
435 
436     if (cw && cw != mainCropWindow) {
437         cropWindowSelected (cw);
438     }
439 }
440 
unGrabFocus()441 void ImageArea::unGrabFocus ()
442 {
443 
444     focusGrabber = nullptr;
445 }
446 
addCropWindow()447 void ImageArea::addCropWindow ()
448 {
449     if (!mainCropWindow) {
450         return;    // if called but no image is loaded, it would crash
451     }
452 
453     CropWindow* cw = new CropWindow (this, true, true);
454     cw->zoom11(false);
455     cw->setCropGUIListener (cropgl);
456     cw->setPointerMotionListener (pmlistener);
457     cw->setPointerMotionHListener (pmhlistener);
458     int lastWidth = options.detailWindowWidth;
459     int lastHeight = options.detailWindowHeight;
460 
461     if (lastWidth < lastHeight) {
462         lastHeight = lastWidth;
463     }
464 
465     if (lastHeight < lastWidth) {
466         lastWidth = lastHeight;
467     }
468 
469     if(!cropWins.empty()) {
470         CropWindow *lastCrop;
471         lastCrop = cropWins.front();
472 
473         if(lastCrop) {
474             lastCrop->getSize(lastWidth, lastHeight);
475         }
476     }
477 
478     cropWins.push_front (cw);
479 
480     // Position the new crop window this way: start from top right going down to bottom. When bottom is reached, continue top left going down......
481     int N = cropWins.size() - 1;
482     int cropwidth, cropheight;
483 
484     if(lastWidth <= 0) { // this is only the case for very first start of RT 4.1 or when options file is deleted
485         cropwidth = 200;
486         cropheight = 200;
487     } else {
488         cropwidth = lastWidth;
489         cropheight = lastHeight;
490     }
491 
492     cw->setSize (cropwidth, cropheight);
493     int x, y;
494     int maxRows = get_height() / cropheight;
495 
496     if(maxRows == 0) {
497         maxRows = 1;
498     }
499 
500     int col = N / maxRows;
501 
502     if(col % 2) { // from left side
503         col = col / 2;
504         x = col * cropwidth;
505 
506         if(x >= get_width() - 50) {
507             x = get_width() - 50;
508         }
509     } else {    // from right side
510         col /= 2;
511         col++;
512         x = get_width() - col * cropwidth;
513 
514         if(x <= 0) {
515             x = 0;
516         }
517     }
518 
519     y = cropheight * (N % maxRows);
520     cw->setPosition (x, y);
521     cw->setEditSubscriber (getCurrSubscriber());
522     cw->enable(); // start processing!
523 
524     {
525     int anchorX = 0;
526     int anchorY = 0;
527     mainCropWindow->getCropAnchorPosition(anchorX, anchorY);
528     cw->setCropAnchorPosition(anchorX, anchorY);
529     }
530 
531     mainCropWindow->setObservedCropWin (cropWins.front());
532 
533     if(!ipc->getHighQualComputed()) {
534         ipc->startProcessing(M_HIGHQUAL);
535         ipc->setHighQualComputed();
536     }
537 }
538 
539 
cropWindowSelected(CropWindow * cw)540 void ImageArea::cropWindowSelected (CropWindow* cw)
541 {
542 
543     std::list<CropWindow*>::iterator i = std::find (cropWins.begin(), cropWins.end(), cw);
544 
545     if (i != cropWins.end()) {
546         cropWins.erase (i);
547     }
548 
549     cropWins.push_front (cw);
550     mainCropWindow->setObservedCropWin (cropWins.front());
551 }
552 
cropWindowClosed(CropWindow * cw)553 void ImageArea::cropWindowClosed (CropWindow* cw)
554 {
555 
556     focusGrabber = nullptr;
557     std::list<CropWindow*>::iterator i = std::find (cropWins.begin(), cropWins.end(), cw);
558 
559     if (i != cropWins.end()) {
560         cropWins.erase (i);
561     }
562 
563     if (!cropWins.empty()) {
564         mainCropWindow->setObservedCropWin (cropWins.front());
565     } else {
566         mainCropWindow->setObservedCropWin (nullptr);
567     }
568 
569     queue_draw ();
570 }
571 
straightenReady(double rotDeg)572 void ImageArea::straightenReady (double rotDeg)
573 {
574 
575     if (listener) {
576         listener->rotateSelectionReady (rotDeg);
577     }
578 }
579 
spotWBSelected(int x,int y)580 void ImageArea::spotWBSelected (int x, int y)
581 {
582 
583     if (listener) {
584         listener->spotWBselected (x, y);
585     }
586 }
587 
sharpMaskSelected(bool sharpMask)588 void ImageArea::sharpMaskSelected (bool sharpMask)
589 {
590 
591     if (listener) {
592         listener->sharpMaskSelected (sharpMask);
593     }
594 }
595 
getScrollImageSize(int & w,int & h)596 void ImageArea::getScrollImageSize (int& w, int& h)
597 {
598 
599     if (mainCropWindow && ipc) {
600         w = ipc->getFullWidth();
601         h = ipc->getFullHeight();
602     } else {
603         w = h = 0;
604     }
605 }
606 
getScrollPosition(int & x,int & y)607 void ImageArea::getScrollPosition (int& x, int& y)
608 {
609 
610     if (mainCropWindow) {
611         mainCropWindow->getCropAnchorPosition (x, y);
612     } else {
613         x = y = 0;
614     }
615 }
616 
setScrollPosition(int x,int y)617 void ImageArea::setScrollPosition (int x, int y)
618 {
619 
620     if (mainCropWindow) {
621         mainCropWindow->delCropWindowListener (this);
622         mainCropWindow->setCropAnchorPosition (x, y);
623         mainCropWindow->addCropWindowListener (this);
624     }
625 }
626 
cropPositionChanged(CropWindow * cw)627 void ImageArea::cropPositionChanged (CropWindow* cw)
628 {
629 
630     syncBeforeAfterViews ();
631 }
632 
cropWindowSizeChanged(CropWindow * cw)633 void ImageArea::cropWindowSizeChanged (CropWindow* cw)
634 {
635 
636     syncBeforeAfterViews ();
637 }
638 
cropZoomChanged(CropWindow * cw)639 void ImageArea::cropZoomChanged (CropWindow* cw)
640 {
641 
642     if (cw == mainCropWindow) {
643         parent->zoomChanged ();
644         syncBeforeAfterViews ();
645         zoomPanel->refreshZoomLabel ();
646     }
647 }
648 
getZoom()649 double ImageArea::getZoom ()
650 {
651 
652     if (mainCropWindow) {
653         return mainCropWindow->getZoom ();
654     } else {
655         return 1.0;
656     }
657 }
658 
659 // Called by imageAreaPanel before/after views
setZoom(double zoom)660 void ImageArea::setZoom (double zoom)
661 {
662 
663     if (mainCropWindow) {
664         mainCropWindow->setZoom (zoom);
665     }
666 
667     zoomPanel->refreshZoomLabel ();
668 }
669 
initialImageArrived()670 void ImageArea::initialImageArrived ()
671 {
672 
673     if (mainCropWindow) {
674         int w, h;
675         mainCropWindow->cropHandler.getFullImageSize(w, h);
676         if (options.prevdemo != PD_Sidecar || !options.rememberZoomAndPan || w != fullImageWidth || h != fullImageHeight) {
677             mainCropWindow->zoomFit();
678         } else if (mainCropWindow->cropHandler.cropParams.enabled) {
679             mainCropWindow->zoomFit();
680         }
681         fullImageWidth = w;
682         fullImageHeight = h;
683     }
684 }
685 
syncBeforeAfterViews()686 void ImageArea::syncBeforeAfterViews ()
687 {
688     parent->syncBeforeAfterViews ();
689 }
690 
setCropGUIListener(CropGUIListener * l)691 void ImageArea::setCropGUIListener (CropGUIListener* l)
692 {
693 
694     cropgl = l;
695 
696     for (auto cropWin : cropWins) {
697         cropWin->setCropGUIListener (cropgl);
698     }
699 
700     if (mainCropWindow) {
701         mainCropWindow->setCropGUIListener (cropgl);
702     }
703 }
704 
setPointerMotionListener(PointerMotionListener * pml)705 void ImageArea::setPointerMotionListener (PointerMotionListener* pml)
706 {
707 
708     pmlistener = pml;
709 
710     for (auto cropWin : cropWins) {
711         cropWin->setPointerMotionListener (pml);
712     }
713 
714     if (mainCropWindow) {
715         mainCropWindow->setPointerMotionListener (pml);
716     }
717 }
718 
setPointerMotionHListener(PointerMotionListener * pml)719 void ImageArea::setPointerMotionHListener (PointerMotionListener* pml)
720 {
721 
722     pmhlistener = pml;
723 
724     for (auto cropWin : cropWins) {
725         cropWin->setPointerMotionHListener (pml);
726     }
727 
728     if (mainCropWindow) {
729         mainCropWindow->setPointerMotionHListener (pml);
730     }
731 }
732 
getToolMode()733 ToolMode ImageArea::getToolMode ()
734 {
735 
736     if (listener && listener->getToolBar()) {
737         return listener->getToolBar()->getTool ();
738     } else {
739         return TMHand;
740     }
741 }
742 
showColorPickers()743 bool ImageArea::showColorPickers ()
744 {
745 
746     if (listener && listener->getToolBar()) {
747         return listener->getToolBar()->showColorPickers ();
748     } else {
749         return false;
750     }
751 }
752 
setToolHand()753 void ImageArea::setToolHand ()
754 {
755 
756     if (listener && listener->getToolBar()) {
757         listener->getToolBar()->setTool (TMHand);
758     }
759 }
760 
getSpotWBRectSize()761 int ImageArea::getSpotWBRectSize  ()
762 {
763 
764     if (listener) {
765         return listener->getSpotWBRectSize ();
766     } else {
767         return 1;
768     }
769 }
770 
get_request_mode_vfunc() const771 Gtk::SizeRequestMode ImageArea::get_request_mode_vfunc () const
772 {
773     return Gtk::SIZE_REQUEST_CONSTANT_SIZE;
774 }
775 
get_preferred_height_vfunc(int & minimum_height,int & natural_height) const776 void ImageArea::get_preferred_height_vfunc (int &minimum_height, int &natural_height) const
777 {
778     minimum_height= 50 * RTScalable::getScale();
779     natural_height = 300 * RTScalable::getScale();
780 }
781 
get_preferred_width_vfunc(int & minimum_width,int & natural_width) const782 void ImageArea::get_preferred_width_vfunc (int &minimum_width, int &natural_width) const
783 {
784     minimum_width = 100 * RTScalable::getScale();
785     natural_width = 400 * RTScalable::getScale();
786 }
787 
get_preferred_height_for_width_vfunc(int width,int & minimum_height,int & natural_height) const788 void ImageArea::get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const
789 {
790     get_preferred_height_vfunc(minimum_height, natural_height);
791 }
792 
get_preferred_width_for_height_vfunc(int height,int & minimum_width,int & natural_width) const793 void ImageArea::get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const
794 {
795     get_preferred_width_vfunc (minimum_width, natural_width);
796 }
797 
798 
setAreaDrawListenerProvider(AreaDrawListenerProvider * alp)799 void ImageArea::setAreaDrawListenerProvider(AreaDrawListenerProvider *alp)
800 {
801     alp_ = alp;
802 
803     if (mainCropWindow && alp_) {
804         alp_->setAreaDrawListener(mainCropWindow);
805     }
806 }
807