1 /* -*- C++ -*-
2  *
3  *  This file is part of RawTherapee.
4  *
5  *  Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
6  *
7  *  RawTherapee is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  RawTherapee is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with RawTherapee.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 #pragma once
21 #include <gtkmm.h>
22 //#include "../rtengine/imagefloat.h"
23 #include "editid.h"
24 #include "cursormanager.h"
25 #include "../rtengine/rt_math.h"
26 #include "../rtengine/coord.h"
27 #include "guiutils.h"
28 #include "options.h"
29 
30 #ifdef GUIVERSION
31 #include "rtsurface.h"
32 #endif
33 
34 class EditDataProvider;
35 class EditSubscriber;
36 
37 /** @file
38  *
39  * The Edit mechanism is designed to let tools (subscribers) communicate with the preview area (provider).
40  * Subscribers will be tools that need to create some graphics in the preview area, to let the user interact
41  * with it in a more user friendly way.
42  *
43  * Do not confuse with _local_ editing, which is another topic implemented in another class. The Edit feature
44  * is also not supported in batch editing from the File Browser.
45  *
46  * Edit tool can be of 2 types: pipette editing and object editing.
47  *
48  * ## Pipette edition
49  *
50  * By using this class, a pipette mechanism can be handled on the preview.
51  *
52  * Each pipette Edit tool must have a unique ID, that will identify them, and which let the ImProcCoordinator
53  * or other mechanism act as appropriated. They are all defined in rtgui/editid.h. A buffer type has to be given
54  * too, to know which kind of buffer to allocate (see EditSubscriber::BufferType).
55  *
56  * Only the first mouse button can be used to manipulate the pipette on the Preview, that's why the developer has
57  * to implement at least the following 4 methods:
58  *    - mouseOver
59  *    - button1Pressed
60  *    - drag1
61  *    - button1Released
62  *
63  * Actually, only curves does use this class, and everything is handled for curve implementor (as much as possible).
64  * See the curve's class documentation to see how to implement the curve's pipette feature.
65  *
66  * ### Event handling
67  *
68  * The mouseOver method is called on each mouse movement, excepted when dragging a point. This method can then access
69  * the pipetteVal array values, which contain the mean of the pixel read in the buffer, or -1 if the cursor is outside
70  * of the image. In this case, EditDataProvider::object is also set to 0 (and 1 if over the image).
71  *
72  * When the user will click on the left mouse button while pressing the CTRL key, button1Pressed will be called.
73  * Setting "dragging" to true (or false) is not required for the pipette type editing.
74  *
75  * The drag1 method will be called on all subsequent mouse move. The pipetteVal[3] array will already be filled with
76  * the mean of the read values under the cursor (actually a fixed square of 8px). If the BufferType is BT_SINGLEPLANE_FLOAT,
77  * only the first array value will be filled.
78  *
79  * Then the button1Released will be called to stop the dragging.
80  *
81  * ## Object edition
82  *
83  * By using this class, objects can be drawn and manipulated on the preview.
84  *
85  * The developer has to handle the buttonPress, buttonRelease, drag and mouseOver methods that he needs. There
86  * are buttonPress, buttonRelease and drag methods dedicated to each mouse button, for better flexibility
87  * (e.g.button2Press, button2Release, drag2 will handle event when mouse button 2 is used first). RT actually
88  * does not handle multiple mouse button event (e.g. button1 + button2), only one at a time. The first button pressed
89  * set the mechanism, all other combined button press are ignored.
90  *
91  * The developer also have to fill 2 display list with object of the Geometry subclass. Each geometric shape
92  * _can_ be used in one or the other, or both list at a time.
93  *
94  * The first list (visibleGeometry) is used to be displayed on the preview. The developer will have to set their state
95  * manually (see Geometry::State), but the display shape, line width and color can be handled automatically, or with
96  * specific values. To be displayed, the F_VISIBLE flag has to be set through the setActive or setVisible methods.
97  *
98  * The second list (mouseOverGeometry) is used in a backbuffer, the color used to draw the shape being the id of the
99  * mouseOverGeometry. As an example, you could create a line to be shown in the preview, but create 2 filled Circle object
100  * to be used as mouseOver detection, one on each end of the line. The association between both shape (visible and mouseOver)
101  * is handled by the developer. To be displayed on this backbuffer, the F_HOVERABLE flag has to be set through the
102  * setActive or setHoverable methods. For overlapping mouse over geometry, the priority is set by the order in the list :
103  * the last item is detected first (think of it like a stack piled up).
104  *
105  *
106  * ### Event handling
107  *
108  * RT will draw in the back buffer all mouseOverGeometry set by the developer once the Edit button is pressed, and handle
109  * the events automatically.
110  *
111  * RT will call the mouseOver method on each mouse movement where no mouse button is pressed.
112  *
113  * On mouse button press over a mouseOverGeometry (that has F_HOVERABLE set), it will call the button press method corresponding
114  * to the button (e.g. button1Pressed for mouse button 1), with the modifier key as parameter. Any other mouse button pressed at
115  * the same time will be ignored. It's up to the developer to decide whether this action is starting a 'drag' or 'pick' action,
116  * by setting the 'action' parameter to the appropriated value.
117  *
118  * If the user sets action to ES_ACTION_DRAGGING, RT will then send drag1 events (to stay with our button 1 pressed example) on each
119  * mouse movement. It's up to the developer of the tool to handle the dragging. The EditProvider class will help you in this by
120  * handling the actual position in various coordinate system and ways.
121  *
122  * When the user will release the mouse button, RT will call the button1Release event (in our example). The developer have
123  * then to set action to ES_ACTION_NONE.
124  *
125  * If the user sets action to ES_ACTION_PICKING, RT will keep in memory the mouseOver object that was selected when pressing the mouse
126  * (e.g. button 1), as well as the modifier keys.
127  *
128  * The element is said to be picked when the mouse button is released over the same mouse over object and with the same active
129  * modifier keys. In this case, the corresponding picked event (e.g. picked1 in our example) and the 'picked' flag will be true.
130  * If any of those condition is false, picked1 will still be be called to terminate the initiated picking action, but 'picked'
131  * will be false. This is necessary because the user may want to update the geometry if the picking is aborted. The developer have
132  * then to set action to ES_ACTION_NONE.
133  *
134  * Picking an on-screen element correspond to single-clicking on it. No double click is supported so far.
135  *
136  * Each of these methods have to returns a boolean value saying that the preview has to be refreshed or not (i.e. the displayed
137  * geometry).
138  *
139  * ## Other general internal implementation notes
140  *
141  * When a tool is being constructed, unique IDs are affected to the EditSubscribers of the Pipette type.
142  * Then the EditorPanel class will ask all ToolPanel to register the 'after' preview ImageArea object as data provider.
143  * The Subscribers have now to provide a toggle button to click on to start the Edit listening. When toggling on, the Subscriber
144  * register itself to the DataProvider, then an event is thrown through the standard ToolPanelListener::panelChanged
145  * method to update the preview with new graphics to be displayed. If a previous Edit button was active, it will be deactivated
146  * (the Edit buttons are mutually exclusive). For the Pipette type, a buffer will be created and has to be populated
147  * by the developer in rtengine's pipeline. The unique pipette ID will be used to know where to fill the buffer, as each pipette
148  * will need different data, corresponding to the state of the image right before the tool that needs pipette values. E.g for
149  * the HSV tool, the Hue and Saturation and Value curves are applied on the current state of the image. That's why the pipette
150  * of the H, S and V curve will share the same data of this "current state", otherwise the read value would be wrong.
151  *
152  * When the Edit process stops, the Subscriber is removed from the DataProvider, so buffers can be freed up.
153  * A new ToolPanelListener::panelChanged event is also thrown to update the preview again, without the tool's
154  * graphical objects. The Edit button is also toggled off (by the user or programmatically).
155  *
156  * It means that each Edit buttons toggled on will start an update of the preview which might or might not create
157  * a new History entry, depending on the ProcEvent used.
158  *
159  */
160 
161 
162 
163 class ObjectMOBuffer
164 {
165 private:
166 
167     // Used to draw the objects where the color correspond to the object's ID, in order to find the correct object when hovering
168     Cairo::RefPtr<Cairo::ImageSurface> objectMap;
169     ObjectMode objectMode;
170 
171 protected:
172 
173     // To avoid duplicated information, we points to a EditDataProvider that contains the current EditSubscriber
174     // instead of pointing to the EditSubscriber directly
175     EditDataProvider* dataProvider;
176 
177     void createBuffer(int width, int height);
178     void resize(int newWidth, int newHeight);
179     void flush();
180     EditSubscriber *getEditSubscriber ();
181 
182 public:
183     explicit ObjectMOBuffer (EditDataProvider *dataProvider);
184     ~ObjectMOBuffer();
185 
186     EditDataProvider* getDataProvider ();
187     void setObjectMode (ObjectMode newType);
188     ObjectMode getObjectMode ();
189 
190     Cairo::RefPtr<Cairo::ImageSurface>& getObjectMap ();
191 
192     // return true if the buffer has been allocated
193     bool bufferCreated();
194 
195     int getObjectID(const rtengine::Coord& location);
196 };
197 
198 
199 /** @brief Coordinate system where the widgets will be drawn
200  *
201  * The EditCoordSystem is used to define a screen and an image coordinate system.
202  */
203 class EditCoordSystem
204 {
205 public:
~EditCoordSystem()206     virtual ~EditCoordSystem() {}
207 
208     /// Convert the widget's DrawingArea (i.e. preview area) coords to the edit buffer coords / int values
209     virtual void screenCoordToCropBuffer (int phyx, int phyy, int& cropx, int& cropy) = 0;
210     /// Convert the widget's DrawingArea (i.e. preview area) coords to the edit buffer coords / double values
211     virtual void screenCoordToCropBuffer (double phyx, double phyy, double& cropx, double& cropy) = 0;
212 
213     /// Convert the widget's DrawingArea (i.e. preview area) coords to the full image coords / int values
214     virtual void screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy) = 0;
215     /// Convert the widget's DrawingArea (i.e. preview area) coords to the full image coords / double values
216     virtual void screenCoordToImage (double phyx, double phyy, double& imgx, double& imgy) = 0;
217 
218     /// Convert the image coords to the widget's DrawingArea (i.e. preview area) coords / int values
219     virtual void imageCoordToScreen (int imgx, int imgy, int& phyx, int& phyy) = 0;
220     /// Convert the image coords to the widget's DrawingArea (i.e. preview area) coords / double values
221     virtual void imageCoordToScreen (double imgx, double imgy, double& phyx, double& phyy) = 0;
222 
223     /// Convert the image coords to the crop's canvas coords (full image + padding) / int values
224     virtual void imageCoordToCropCanvas (int imgx, int imgy, int& phyx, int& phyy) = 0;
225     /// Convert the image coords to the crop's canvas coords (full image + padding) / double values
226     virtual void imageCoordToCropCanvas (double imgx, double imgy, double& phyx, double& phyy) = 0;
227 
228     /// Convert the image coords to the edit buffer coords  (includes borders) / int values
229     virtual void imageCoordToCropBuffer (int imgx, int imgy, int& phyx, int& phyy) = 0;
230     /// Convert the image coords to the edit buffer coords  (includes borders) / double values
231     virtual void imageCoordToCropBuffer (double imgx, double imgy, double& phyx, double& phyy) = 0;
232 
233     /// Convert the image coords to the displayed image coords  (no borders here) / int values
234     virtual void imageCoordToCropImage (int imgx, int imgy, int& phyx, int& phyy) = 0;
235     /// Convert the image coords to the displayed image coords  (no borders here) / double values
236     virtual void imageCoordToCropImage (double imgx, double imgy, double& phyx, double& phyy) = 0;
237 
238     /// Convert a size value from the preview's scale to the image's scale
239     virtual int scaleValueToImage (int value) = 0;
240 
241     /// Convert a size value from the preview's scale to the image's scale
242     virtual float scaleValueToImage (float value) = 0;
243 
244     /// Convert a size value from the preview's scale to the image's scale
245     virtual double scaleValueToImage (double value) = 0;
246 
247     /// Convert a size value from the image's scale to the preview's scale
248     virtual int scaleValueToCanvas (int value) = 0;
249 
250     /// Convert a size value from the image's scale to the preview's scale
251     virtual float scaleValueToCanvas (float value) = 0;
252 
253     /// Convert a size value from the image's scale to the preview's scale
254     virtual double scaleValueToCanvas (double value) = 0;
255 };
256 
257 class RGBColor
258 {
259     double r;
260     double g;
261     double b;
262 
263 public:
264     RGBColor ();
265     explicit RGBColor (double r, double g, double b);
266     explicit RGBColor (char r, char g, char b);
267 
268     void setColor (double r, double g, double b);
269     void setColor (char r, char g, char b);
270 
271     double getR ();
272     double getG ();
273     double getB ();
274 };
275 
276 class RGBAColor : public RGBColor
277 {
278     double a;
279 
280 public:
281     RGBAColor ();
282     explicit RGBAColor (double r, double g, double b, double a);
283     explicit RGBAColor (char r, char g, char b, char a);
284 
285     void setColor (double r, double g, double b, double a);
286     void setColor (char r, char g, char b, char a);
287 
288     double getA ();
289 };
290 
291 /// @brief Displayable and MouseOver geometry base class
292 class Geometry
293 {
294 public:
295     /// @brief Graphical state of the element
296     enum State {
297         NORMAL,     /// Default state
298         ACTIVE,     /// Focused state
299         PRELIGHT,   /// Hovered state
300         DRAGGED,    /// When being dragged
301         INSENSITIVE /// Displayed but insensitive
302     };
303 
304     /// @brief Coordinate space and origin of the point
305     enum Datum {
306         IMAGE,          /// Image coordinate system with image's top left corner as origin
307         CLICKED_POINT,  /// Screen coordinate system with clicked point as origin
308         CURSOR          /// Screen coordinate system with actual cursor position as origin
309     };
310     enum Flags {
311         F_VISIBLE     = 1 << 0, /// true if the geometry have to be drawn on the visible layer
312         F_HOVERABLE   = 1 << 1, /// true if the geometry have to be drawn on the "mouse over" layer
313         F_AUTO_COLOR  = 1 << 2, /// true if the color depend on the state value, not the color field above
314         F_DASHED      = 1 << 3, /// true if the geometry have to be drawn as a dash line
315         F_FRAME       = 1 << 4, /// true if the geometry represent a base shape used to compute a usable shape (e.g. : control cage)
316     };
317 
318     /// @brief Key point of the image's rectangle that is used to locate the icon copy to the target point:
319     enum DrivenPoint {
320         DP_CENTERCENTER,
321         DP_TOPLEFT,
322         DP_TOPCENTER,
323         DP_TOPRIGHT,
324         DP_CENTERRIGHT,
325         DP_BOTTOMRIGHT,
326         DP_BOTTOMCENTER,
327         DP_BOTTOMLEFT,
328         DP_CENTERLEFT
329     };
330 
331 protected:
332     static const std::vector<double> dash;
333     RGBColor innerLineColor;
334     RGBColor outerLineColor;
335     short flags;
336 
337 public:
338     float innerLineWidth;  // ...outerLineWidth = innerLineWidth+2
339     Datum datum;
340     State state;  // set by the Subscriber
341 
342     Geometry ();
~Geometry()343     virtual ~Geometry() {}
344 
345     void setInnerLineColor (double r, double g, double b);
346     void setInnerLineColor (char r, char g, char b);
347     RGBColor getInnerLineColor ();
348     void setOuterLineColor (double r, double g, double b);
349     void setOuterLineColor (char r, char g, char b);
350     RGBColor getOuterLineColor ();
351     double getOuterLineWidth ();
352     double getMouseOverLineWidth ();
353     void setAutoColor (bool aColor);
354     bool isVisible ();
355     void setVisible (bool visible);
356     bool isFrame ();
357     void setFrame (bool frame);
358     bool isHoverable ();
359     void setHoverable (bool visible);
360     bool isDashed ();
361     void setDashed (bool dashed);
362 
363 
364     // setActive will enable/disable the visible and hoverable flags in one shot!
365     void setActive (bool active);
366 
367     virtual void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
368     virtual void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
369     virtual void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
370 };
371 
372 #ifdef GUIVERSION
373 
374 class Circle : public Geometry
375 {
376 public:
377     rtengine::Coord center;
378     int radius;
379     bool filled;
380     bool radiusInImageSpace; /// If true, the radius depend on the image scale; if false, it is a fixed 'screen' size
381 
382     Circle ();
383     Circle (rtengine::Coord& center, int radius, bool filled = false, bool radiusInImageSpace = false);
384     Circle (int centerX, int centerY, int radius, bool filled = false, bool radiusInImageSpace = false);
385 
386     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
387     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
388     void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
389 };
390 
391 class Line : public Geometry
392 {
393 public:
394     rtengine::Coord begin;
395     rtengine::Coord end;
396 
397     Line ();
398     Line (rtengine::Coord& begin, rtengine::Coord& end);
399     Line (int beginX, int beginY, int endX, int endY);
400 
401     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
402     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
403     void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
404 };
405 
406 class PolyLine : public Geometry
407 {
408 public:
409     std::vector<rtengine::CoordD> points;
410     bool filled;
411     bool closed;
412 
413     PolyLine ();
414 
415     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
416     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
417     void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
418 };
419 
420 class Rectangle : public Geometry
421 {
422 public:
423     rtengine::Coord topLeft;
424     rtengine::Coord bottomRight;
425     bool filled;
426 
427     Rectangle ();
428 
429     void setXYWH(int left, int top, int width, int height);
430     void setXYXY(int left, int top, int right, int bottom);
431     void setXYWH(rtengine::Coord topLeft, rtengine::Coord widthHeight);
432     void setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight);
433     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
434     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
435     void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
436 };
437 
438 class OPIcon : public Geometry    // OP stands for "On Preview"
439 {
440 
441 private:
442     Cairo::RefPtr<RTSurface> normalImg;
443     Cairo::RefPtr<RTSurface> prelightImg;
444     Cairo::RefPtr<RTSurface> activeImg;
445     Cairo::RefPtr<RTSurface> draggedImg;
446     Cairo::RefPtr<RTSurface> insensitiveImg;
447 
448     static void updateImages();
449     void changeImage(Glib::ustring &newImage);
450     void drawImage (Cairo::RefPtr<RTSurface> &img, Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
451     void drawMOImage (Cairo::RefPtr<RTSurface> &img, Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
452     void drivenPointToRectangle(const rtengine::Coord &pos, rtengine::Coord &topLeft, rtengine::Coord &bottomRight, int W, int H);
453 
454 public:
455     DrivenPoint drivenPoint;
456     rtengine::Coord position;
457 
458     OPIcon (const Cairo::RefPtr<RTSurface> &normal,
459             const Cairo::RefPtr<RTSurface> &active,
460             const Cairo::RefPtr<RTSurface> &prelight = {},
461             const Cairo::RefPtr<RTSurface> &dragged = {},
462             const Cairo::RefPtr<RTSurface> &insensitive = {},
463             DrivenPoint drivenPoint = DP_CENTERCENTER);
464     OPIcon (Glib::ustring normalImage, Glib::ustring activeImage, Glib::ustring  prelightImage = "", Glib::ustring  draggedImage = "", Glib::ustring insensitiveImage = "", DrivenPoint drivenPoint = DP_CENTERCENTER);
465     const Cairo::RefPtr<RTSurface> getNormalImg();
466     const Cairo::RefPtr<RTSurface> getPrelightImg();
467     const Cairo::RefPtr<RTSurface> getActiveImg();
468     const Cairo::RefPtr<RTSurface> getDraggedImg();
469     const Cairo::RefPtr<RTSurface> getInsensitiveImg();
470     void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
471     void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
472     void drawToMOChannel   (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override;
473 };
474 
475 class OPAdjuster : public Geometry    // OP stands for "On Preview"
476 {
477 
478 };
479 
480 #endif
481 
482 /// @brief Method for client tools needing Edit information
483 class EditSubscriber
484 {
485 
486 public:
487 
488 private:
489     EditUniqueID ID; /// this will be used in improcfun to locate the data that has to be stored in the buffer; it must be unique in RT
490     EditType editingType;
491     BufferType bufferType;
492     EditDataProvider *provider;
493 
494 protected:
495     std::vector<Geometry*> visibleGeometry;    /// displayed geometry
496     std::vector<Geometry*> mouseOverGeometry;  /// mouseOver geometry, drawn in a hidden buffer
497     enum {
498         ES_ACTION_NONE,      ///
499         ES_ACTION_DRAGGING,  /// set action to this value in the buttonPressed event to start dragging and ask for drag event
500         ES_ACTION_PICKING    /// set action to this value in the buttonPressed event whenever the user is picking something through a single click. In this case, the pickX events will be called INSTEAD of buttonXReleased !
501     } action;                /// object mode only, ignored in Pipette mode
502 
503 public:
504     explicit EditSubscriber (EditType editType);
~EditSubscriber()505     virtual ~EditSubscriber () {}
506 
507     void               setEditProvider(EditDataProvider *provider);
508     EditDataProvider*  getEditProvider () const;
509     void               setEditID(EditUniqueID ID, BufferType buffType);
510     bool               isCurrentSubscriber();
511     virtual void       subscribe();
512     virtual void       unsubscribe();
513     virtual void       switchOffEditMode ();    /// Occurs when the user want to stop the editing mode
514     EditUniqueID       getEditID();
515     EditType           getEditingType();
516     BufferType         getPipetteBufferType();
517     bool               isDragging();            /// Returns true if something is being dragged and drag events has to be sent (object mode only)
518     bool               isPicking();             /// Returns true if something is being picked
519 
520     /** @brief Get the cursor to be displayed when above handles
521     @param objectID object currently "hovered" */
522     virtual CursorShape getCursor (int objectID);
523 
524     /** @brief Get the cursor to be displayed when above handles
525     @param objectID object currently "hovered"
526     @param xPos X cursor position in image space
527     @param yPos Y cursor position in image space */
528     virtual CursorShape getCursor (int objectID, int xPos, int yPos);
529 
530     /** @brief Triggered when the mouse is moving over an object
531     This method is also triggered when the cursor is moving over the image in ET_PIPETTE mode
532     @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
533     @return true if the preview has to be redrawn, false otherwise */
534     virtual bool mouseOver(int modifierKey);
535 
536     /** @brief Triggered when mouse button 1 is pressed, together with the CTRL modifier key if the subscriber is of type ET_PIPETTE
537     Once the key is pressed, RT will enter in drag1 mode on subsequent mouse movements
538     @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
539     @return true if the preview has to be redrawn, false otherwise */
540     virtual bool button1Pressed(int modifierKey);
button1Pressed(int modifierKey,double pressure)541     virtual bool button1Pressed(int modifierKey, double pressure) { return button1Pressed(modifierKey); }
542 
543     /** @brief Triggered when mouse button 1 is released
544     @return true if the preview has to be redrawn, false otherwise */
545     virtual bool button1Released ();
546 
547     /** @brief Triggered when mouse button 2 is pressed (middle button)
548     Once the key is pressed, RT will enter in drag2 mode on subsequent mouse movements
549     @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
550     @return true if the preview has to be redrawn, false otherwise */
551     virtual bool button2Pressed (int modifierKey);
button2Pressed(int modifierKey,double pressure)552     virtual bool button2Pressed(int modifierKey, double pressure) { return button2Pressed(modifierKey); }
553 
554     /** @brief Triggered when mouse button 2 is released (middle button)
555     @return true if the preview has to be redrawn, false otherwise */
556     virtual bool button2Released ();
557 
558     /** @brief Triggered when mouse button 3 is pressed (right button)
559     Once the key is pressed, RT will enter in drag3 mode on subsequent mouse movements
560     @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
561     @return true if the preview has to be redrawn, false otherwise */
562     virtual bool button3Pressed (int modifierKey);
button3Pressed(int modifierKey,double pressure)563     virtual bool button3Pressed(int modifierKey, double pressure) { return button3Pressed(modifierKey); }
564 
565     /** @brief Triggered when mouse button 3 is released (right button)
566     @return true if the preview has to be redrawn, false otherwise */
567     virtual bool button3Released ();
568 
569     /** @brief Triggered when the user is moving while holding down mouse button 1
570     @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
571     @return true if the preview has to be redrawn, false otherwise */
572     virtual bool drag1(int modifierKey);
drag1(int modifierKey,double pressure)573     virtual bool drag1(int modifierKey, double pressure) { return drag1(modifierKey); }
574 
575     /** @brief Triggered when the user is moving while holding down mouse button 2
576     @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
577     @return true if the preview has to be redrawn, false otherwise */
578     virtual bool drag2(int modifierKey);
drag2(int modifierKey,double pressure)579     virtual bool drag2(int modifierKey, double pressure) { return drag2(modifierKey); }
580 
581     /** @brief Triggered when the user is moving while holding down mouse button 3
582     @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
583     @return true if the preview has to be redrawn, false otherwise */
584     virtual bool drag3(int modifierKey);
drag3(int modifierKey,double pressure)585     virtual bool drag3(int modifierKey, double pressure) { return drag3(modifierKey); }
586 
587     /** @brief Triggered when the user is releasing mouse button 1 while in action==ES_ACTION_PICKING mode
588     No modifier key is provided, since having a different modifier key than on button press will set picked to false.
589     @param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys.
590                   If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected.
591     @return true if the preview has to be redrawn, false otherwise */
592     virtual bool pick1 (bool picked);
593 
594     /** @brief Triggered when the user is releasing mouse button 2 while in action==ES_ACTION_PICKING mode
595     @param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys.
596                   If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected.
597     @return true if the preview has to be redrawn, false otherwise */
598     virtual bool pick2 (bool picked);
599 
600     /** @brief Triggered when the user is releasing mouse button 3 while in action==ES_ACTION_PICKING mode
601     @param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys.
602                   If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected.
603     @return true if the preview has to be redrawn, false otherwise */
604     virtual bool pick3 (bool picked);
605 
606     /**
607      * triggered for scroll wheel events. Only for ET_OBJECTS types
608      *
609      * @param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
610      * @param direction Direction of the scroll (see Gtk documentation for more information)
611      * @param deltaX values sent by Gtk on scroll events
612      * @param deltaY values sent by Gtk on scroll events
613      * @param propagateEvent value to state whether the event has been catched (false) or should be propagated (true)
614      * @return false if the scroll event has to be propagated to the crop
615      * window, true if the event has been processed
616      */
617     virtual bool scroll(int modifierKey, GdkScrollDirection direction, double deltaX, double deltaY, bool &propagateEvent);
618 
619     /** @brief Get the geometry to be shown to the user */
620     const std::vector<Geometry*>& getVisibleGeometry ();
621 
622     /** @brief Get the geometry to be drawn in the "mouse over" channel, hidden from the user */
623     const std::vector<Geometry*>& getMouseOverGeometry ();
624 };
625 
626 /** @brief Class to handle the furniture of data to the subscribers.
627  *
628  * It is admitted that only one Subscriber can ask data at a time. If the Subscriber is of type ET_PIPETTE, it will have to
629  * trigger the usual event so that the image will be reprocessed to compute the buffer of the current subscriber.
630  */
631 class EditDataProvider
632 {
633 
634 private:
635     EditSubscriber *currSubscriber;
636 
637 public:
638     int object;            /// ET_OBJECTS mode: Object detected under the cursor, 0 otherwise; ET_PIPETTE mode: 1 if above the image, 0 otherwise
639     float pipetteVal[3];   /// Current pipette values; if bufferType==BT_SINGLEPLANE_FLOAT, #2 & #3 will be set to 0
640 
641     rtengine::Coord posScreen;       /// Location of the mouse button press, in preview image space
642     rtengine::Coord posImage;        /// Location of the mouse button press, in the full image space
643     rtengine::Coord deltaScreen;     /// Delta relative to posScreen
644     rtengine::Coord deltaImage;      /// Delta relative to posImage
645     rtengine::Coord deltaPrevScreen; /// Delta relative to the previous mouse location, in preview image space
646     rtengine::Coord deltaPrevImage;  /// Delta relative to the previous mouse location, in the full image space
647 
648     EditDataProvider();
649     virtual ~EditDataProvider();
650 
651     virtual void        subscribe(EditSubscriber *subscriber);
652     virtual void        unsubscribe();         /// Occurs when the subscriber has been switched off first
653     virtual void        switchOffEditMode ();  /// Occurs when the user want to stop the editing mode
654     int getObject() const;
655     void setObject(int newObject);
656 
657     virtual CursorShape getCursor(int objectID, int xPos, int yPos);
658     // virtual CursorShape getCursor(int objectID);
659     int                 getPipetteRectSize ();
660     EditSubscriber*     getCurrSubscriber();
661     virtual void        getImageSize (int &w, int&h) = 0;
662 };
663 
getDataProvider()664 inline EditDataProvider* ObjectMOBuffer::getDataProvider () {
665     return dataProvider;
666 }
667 
getObjectMode()668 inline ObjectMode ObjectMOBuffer::getObjectMode () {
669     return objectMode;
670 }
671 
getObjectMap()672 inline Cairo::RefPtr<Cairo::ImageSurface>& ObjectMOBuffer::getObjectMap () {
673     return objectMap;
674 }
675 
setColor(double r,double g,double b)676 inline void RGBColor::setColor (double r, double g, double b) {
677     this->r = r;
678     this->g = g;
679     this->b = b;
680 }
681 
setColor(char r,char g,char b)682 inline void RGBColor::setColor (char r, char g, char b) {
683     this->r = double (r) / 255.;
684     this->g = double (g) / 255.;
685     this->b = double (b) / 255.;
686 }
687 
getR()688 inline double RGBColor::getR () {
689     return r;
690 }
691 
getG()692 inline double RGBColor::getG () {
693     return g;
694 }
695 
getB()696 inline double RGBColor::getB () {
697     return b;
698 }
699 
setColor(double r,double g,double b,double a)700 inline void RGBAColor::setColor (double r, double g, double b, double a) {
701     RGBColor::setColor (r, g, b);
702     this->a = a;
703 }
704 
setColor(char r,char g,char b,char a)705 inline void RGBAColor::setColor (char r, char g, char b, char a) {
706     RGBColor::setColor (r, g, b);
707     this->a = double (a) / 255.;
708 }
709 
getA()710 inline double RGBAColor::getA () {
711     return a;
712 }
713 
setInnerLineColor(double r,double g,double b)714 inline void Geometry::setInnerLineColor (double r, double g, double b) {
715     innerLineColor.setColor (r, g, b);
716     flags &= ~F_AUTO_COLOR;
717 }
718 
setInnerLineColor(char r,char g,char b)719 inline void Geometry::setInnerLineColor (char r, char g, char b) {
720     innerLineColor.setColor (r, g, b);
721     flags &= ~F_AUTO_COLOR;
722 }
723 
setOuterLineColor(double r,double g,double b)724 inline void Geometry::setOuterLineColor (double r, double g, double b) {
725     outerLineColor.setColor (r, g, b);
726     flags &= ~F_AUTO_COLOR;
727 }
728 
getOuterLineWidth()729 inline double Geometry::getOuterLineWidth () {
730     return double (innerLineWidth) + 2.;
731 }
732 
setOuterLineColor(char r,char g,char b)733 inline void Geometry::setOuterLineColor (char r, char g, char b) {
734     outerLineColor.setColor (r, g, b);
735     flags &= ~F_AUTO_COLOR;
736 }
737 
getMouseOverLineWidth()738 inline double Geometry::getMouseOverLineWidth () {
739     return getOuterLineWidth () + 2.;
740 }
741 
setAutoColor(bool aColor)742 inline void Geometry::setAutoColor (bool aColor) {
743     if (aColor) {
744         flags |= F_AUTO_COLOR;
745     } else {
746         flags &= ~F_AUTO_COLOR;
747     }
748 }
749 
isVisible()750 inline bool Geometry::isVisible () {
751     return flags & F_VISIBLE;
752 }
753 
setVisible(bool visible)754 inline void Geometry::setVisible (bool visible) {
755     if (visible) {
756         flags |= F_VISIBLE;
757     } else {
758         flags &= ~F_VISIBLE;
759     }
760 }
761 
isFrame()762 inline bool Geometry::isFrame () {
763     return flags & F_FRAME;
764 }
765 
setFrame(bool frame)766 inline void Geometry::setFrame (bool frame) {
767     if (frame) {
768         flags |= F_FRAME;
769     } else {
770         flags &= ~F_FRAME;
771     }
772 }
773 
isHoverable()774 inline bool Geometry::isHoverable () {
775     return flags & F_HOVERABLE;
776 }
777 
setHoverable(bool hoverable)778 inline void Geometry::setHoverable (bool hoverable) {
779     if (hoverable) {
780         flags |= F_HOVERABLE;
781     } else {
782         flags &= ~F_HOVERABLE;
783     }
784 }
785 
setActive(bool active)786 inline void Geometry::setActive (bool active) {
787     if (active) {
788         flags |= (F_VISIBLE | F_HOVERABLE);
789     } else {
790         flags &= ~(F_VISIBLE | F_HOVERABLE);
791     }
792 }
793 
isDashed()794 inline bool Geometry::isDashed () {
795     return flags & F_DASHED;
796 }
797 
setDashed(bool dashed)798 inline void Geometry::setDashed (bool dashed) {
799     if (dashed) {
800         flags |= F_DASHED;
801     } else {
802         flags &= ~F_DASHED;
803     }
804 }
805 
getEditProvider()806 inline EditDataProvider* EditSubscriber::getEditProvider () const {
807     return provider;
808 }
809 
getCursor(int objectID)810 inline CursorShape EditSubscriber::getCursor (int objectID) {
811     return CSHandOpen;
812 }
813 
mouseOver(int modifierKey)814 inline bool EditSubscriber::mouseOver (int modifierKey) {
815     return false;
816 }
817 
button1Pressed(int modifierKey)818 inline bool EditSubscriber::button1Pressed (int modifierKey) {
819     return false;
820 }
821 
button1Released()822 inline bool EditSubscriber::button1Released () {
823     return false;
824 }
825 
button2Pressed(int modifierKey)826 inline bool EditSubscriber::button2Pressed (int modifierKey) {
827     return false;
828 }
829 
button2Released()830 inline bool EditSubscriber::button2Released () {
831     return false;
832 }
833 
button3Pressed(int modifierKey)834 inline bool EditSubscriber::button3Pressed (int modifierKey) {
835     return false;
836 }
837 
button3Released()838 inline bool EditSubscriber::button3Released () {
839     return false;
840 }
841 
drag1(int modifierKey)842 inline bool EditSubscriber::drag1 (int modifierKey) {
843     return false;
844 }
845 
drag2(int modifierKey)846 inline bool EditSubscriber::drag2 (int modifierKey) {
847     return false;
848 }
849 
drag3(int modifierKey)850 inline bool EditSubscriber::drag3 (int modifierKey) {
851     return false;
852 }
853 
pick1(bool picked)854 inline bool EditSubscriber::pick1 (bool picked) {
855     return false;
856 }
857 
pick2(bool picked)858 inline bool EditSubscriber::pick2 (bool picked) {
859     return false;
860 }
861 
pick3(bool picked)862 inline bool EditSubscriber::pick3 (bool picked) {
863     return false;
864 }
865 
scroll(int bstate,GdkScrollDirection direction,double deltaX,double deltaY,bool & propagateEvent)866 inline bool EditSubscriber::scroll(int bstate, GdkScrollDirection direction, double deltaX, double deltaY, bool &propagateEvent)
867 {
868     propagateEvent = true;
869     return false;
870 }
871 
getVisibleGeometry()872 inline const std::vector<Geometry*>& EditSubscriber::getVisibleGeometry () {
873     return visibleGeometry;
874 }
875 
getMouseOverGeometry()876 inline const std::vector<Geometry*>& EditSubscriber::getMouseOverGeometry () {
877     return mouseOverGeometry;
878 }
879 
getPipetteRectSize()880 inline int EditDataProvider::getPipetteRectSize () {
881     return 8; // TODO: make a GUI
882 }
883 
Geometry()884 inline Geometry::Geometry () :
885         innerLineColor (char (255), char (255), char (255)), outerLineColor (
886                 char (0), char (0), char (0)), flags (
887                 F_VISIBLE | F_HOVERABLE | F_AUTO_COLOR), innerLineWidth (1.5f), datum (
888                 IMAGE), state (NORMAL) {
889 }
890 
RGBAColor()891 inline RGBAColor::RGBAColor () :
892         RGBColor (0., 0., 0.), a (0.) {
893 }
894 
RGBColor()895 inline RGBColor::RGBColor () :
896         r (0.), g (0.), b (0.) {
897 }
898 
RGBColor(double r,double g,double b)899 inline RGBColor::RGBColor (double r, double g, double b) :
900         r (r), g (g), b (b) {
901 }
902 
RGBColor(char r,char g,char b)903 inline RGBColor::RGBColor (char r, char g, char b) :
904         r (double (r) / 255.), g (double (g) / 255.), b (double (b) / 255.) {
905 }
906 
RGBAColor(double r,double g,double b,double a)907 inline RGBAColor::RGBAColor (double r, double g, double b, double a) :
908         RGBColor (r, g, b), a (a) {
909 }
910 
RGBAColor(char r,char g,char b,char a)911 inline RGBAColor::RGBAColor (char r, char g, char b, char a) :
912         RGBColor (r, g, b), a (double (a) / 255.) {
913 }
914 
915 #ifdef GUIVERSION
916 
Circle()917 inline Circle::Circle () :
918         center (100, 100), radius (10), filled (false), radiusInImageSpace (
919                 false) {
920 }
921 
Rectangle()922 inline Rectangle::Rectangle () :
923         topLeft (0, 0), bottomRight (10, 10), filled (false) {
924 }
925 
PolyLine()926 inline PolyLine::PolyLine () :
927         filled (false),
928         closed (true) {
929 }
930 
Line()931 inline Line::Line () :
932         begin (10, 10), end (100, 100) {
933 }
934 
Circle(rtengine::Coord & center,int radius,bool filled,bool radiusInImageSpace)935 inline Circle::Circle (rtengine::Coord& center, int radius, bool filled,
936         bool radiusInImageSpace) :
937         center (center), radius (radius), filled (filled), radiusInImageSpace (
938                 radiusInImageSpace) {
939 }
940 
Circle(int centerX,int centerY,int radius,bool filled,bool radiusInImageSpace)941 inline Circle::Circle (int centerX, int centerY, int radius, bool filled,
942         bool radiusInImageSpace) :
943         center (centerX, centerY), radius (radius), filled (filled), radiusInImageSpace (
944                 radiusInImageSpace) {
945 }
946 
Line(rtengine::Coord & begin,rtengine::Coord & end)947 inline Line::Line (rtengine::Coord& begin, rtengine::Coord& end) :
948         begin (begin), end (end) {
949 }
950 
Line(int beginX,int beginY,int endX,int endY)951 inline Line::Line (int beginX, int beginY, int endX, int endY) :
952         begin (beginX, beginY), end (endX, endY) {
953 }
954 
955 #endif
956