858 lines
35 KiB
C++
858 lines
35 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* RawTherapee is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* RawTherapee is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef _EDIT_H_
|
|
#define _EDIT_H_
|
|
|
|
#include <gtkmm.h>
|
|
#include "../rtengine/imagefloat.h"
|
|
#include "editid.h"
|
|
#include "cursormanager.h"
|
|
#include "../rtengine/rt_math.h"
|
|
#include "../rtengine/coord.h"
|
|
#include "guiutils.h"
|
|
#include "options.h"
|
|
|
|
class EditDataProvider;
|
|
class EditSubscriber;
|
|
|
|
/** @file
|
|
*
|
|
* The Edit mechanism is designed to let tools (subscribers) communicate with the preview area (provider).
|
|
* Subscribers will be tools that need to create some graphics in the preview area, to let the user interact
|
|
* with it in a more user friendly way.
|
|
*
|
|
* Do not confuse with _local_ editing, which is another topic implemented in another class. The Edit feature
|
|
* is also not supported in batch editing from the File Browser.
|
|
*
|
|
* Edit tool can be of 2 types: pipette editing and object editing.
|
|
*
|
|
* ## Pipette edition
|
|
*
|
|
* By using this class, a pipette mechanism can be handled on the preview.
|
|
*
|
|
* Each pipette Edit tool must have a unique ID, that will identify them, and which let the ImProcCoordinator
|
|
* or other mechanism act as appropriated. They are all defined in rtgui/editid.h. A buffer type has to be given
|
|
* too, to know which kind of buffer to allocate (see EditSubscriber::BufferType).
|
|
*
|
|
* Only the first mouse button can be used to manipulate the pipette on the Preview, that's why the developer has
|
|
* to implement at least the following 4 methods:
|
|
* - mouseOver
|
|
* - button1Pressed
|
|
* - drag1
|
|
* - button1Released
|
|
*
|
|
* Actually, only curves does use this class, and everything is handled for curve implementor (as much as possible).
|
|
* See the curve's class documentation to see how to implement the curve's pipette feature.
|
|
*
|
|
* ### Event handling
|
|
*
|
|
* The mouseOver method is called on each mouse movement, excepted when dragging a point. This method can then access
|
|
* the pipetteVal array values, which contain the mean of the pixel read in the buffer, or -1 if the cursor is outside
|
|
* of the image. In this case, EditDataProvider::object is also set to 0 (and 1 if over the image).
|
|
*
|
|
* When the user will click on the left mouse button while pressing the CTRL key, button1Pressed will be called.
|
|
* Setting "dragging" to true (or false) is not required for the pipette type editing.
|
|
*
|
|
* The drag1 method will be called on all subsequent mouse move. The pipetteVal[3] array will already be filled with
|
|
* the mean of the read values under the cursor (actually a fixed square of 8px). If the BufferType is BT_SINGLEPLANE_FLOAT,
|
|
* only the first array value will be filled.
|
|
*
|
|
* Then the button1Released will be called to stop the dragging.
|
|
*
|
|
* ## Object edition
|
|
*
|
|
* By using this class, objects can be drawn and manipulated on the preview.
|
|
*
|
|
* The developer has to handle the buttonPress, buttonRelease, drag and mouseOver methods that he needs. There
|
|
* are buttonPress, buttonRelease and drag methods dedicated to each mouse button, for better flexibility
|
|
* (e.g.button2Press, button2Release, drag2 will handle event when mouse button 2 is used first). RT actually
|
|
* does not handle multiple mouse button event (e.g. button1 + button2), only one at a time. The first button pressed
|
|
* set the mechanism, all other combined button press are ignored.
|
|
*
|
|
* The developer also have to fill 2 display list with object of the Geometry subclass. Each geometric shape
|
|
* _can_ be used in one or the other, or both list at a time.
|
|
*
|
|
* The first list (visibleGeometry) is used to be displayed on the preview. The developer will have to set their state
|
|
* manually (see Geometry::State), but the display shape, line width and color can be handled automatically, or with
|
|
* specific values. To be displayed, the F_VISIBLE flag has to be set through the setActive or setVisible methods.
|
|
*
|
|
* The second list (mouseOverGeometry) is used in a backbuffer, the color used to draw the shape being the id of the
|
|
* mouseOverGeometry. As an example, you could create a line to be shown in the preview, but create 2 filled Circle object
|
|
* to be used as mouseOver detection, one on each end of the line. The association between both shape (visible and mouseOver)
|
|
* is handled by the developer. To be displayed on this backbuffer, the F_HOVERABLE flag has to be set through the
|
|
* setActive or setHoverable methods. For overlapping mouse over geometry, the priority is set by the order in the list :
|
|
* the last item is detected first (think of it like a stack piled up).
|
|
*
|
|
*
|
|
* ### Event handling
|
|
*
|
|
* RT will draw in the back buffer all mouseOverGeometry set by the developer once the Edit button is pressed, and handle
|
|
* the events automatically.
|
|
*
|
|
* RT will call the mouseOver method on each mouse movement where no mouse button is pressed.
|
|
*
|
|
* On mouse button press over a mouseOverGeometry (that has F_HOVERABLE set), it will call the button press method corresponding
|
|
* to the button (e.g. button1Pressed for mouse button 1), with the modifier key as parameter. Any other mouse button pressed at
|
|
* the same time will be ignored. It's up to the developer to decide whether this action is starting a 'drag' or 'pick' action,
|
|
* by setting the 'action' parameter to the appropriated value.
|
|
*
|
|
* 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
|
|
* mouse movement. It's up to the developer of the tool to handle the dragging. The EditProvider class will help you in this by
|
|
* handling the actual position in various coordinate system and ways.
|
|
*
|
|
* When the user will release the mouse button, RT will call the button1Release event (in our example). The developer have
|
|
* then to set action to ES_ACTION_NONE.
|
|
*
|
|
* If the user sets action to ES_ACTION_PICKING, RT will keep in memory the mouseOver object that was selected when pressing the mouse
|
|
* (e.g. button 1), as well as the modifier keys.
|
|
*
|
|
* The element is said to be picked when the mouse button is released over the same mouse over object and with the same active
|
|
* modifier keys. In this case, the corresponding picked event (e.g. picked1 in our example) and the 'picked' flag will be true.
|
|
* If any of those condition is false, picked1 will still be be called to terminate the initiated picking action, but 'picked'
|
|
* will be false. This is necessary because the user may want to update the geometry if the picking is aborted. The developer have
|
|
* then to set action to ES_ACTION_NONE.
|
|
*
|
|
* Picking an on-screen element correspond to single-clicking on it. No double click is supported so far.
|
|
*
|
|
* Each of these methods have to returns a boolean value saying that the preview has to be refreshed or not (i.e. the displayed
|
|
* geometry).
|
|
*
|
|
* ## Other general internal implementation notes
|
|
*
|
|
* When a tool is being constructed, unique IDs are affected to the EditSubscribers of the Pipette type.
|
|
* Then the EditorPanel class will ask all ToolPanel to register the 'after' preview ImageArea object as data provider.
|
|
* The Subscribers have now to provide a toggle button to click on to start the Edit listening. When toggling on, the Subscriber
|
|
* register itself to the DataProvider, then an event is thrown through the standard ToolPanelListener::panelChanged
|
|
* method to update the preview with new graphics to be displayed. If a previous Edit button was active, it will be deactivated
|
|
* (the Edit buttons are mutually exclusive). For the Pipette type, a buffer will be created and has to be populated
|
|
* by the developer in rtengine's pipeline. The unique pipette ID will be used to know where to fill the buffer, as each pipette
|
|
* will need different data, corresponding to the state of the image right before the tool that needs pipette values. E.g for
|
|
* the HSV tool, the Hue and Saturation and Value curves are applied on the current state of the image. That's why the pipette
|
|
* of the H, S and V curve will share the same data of this "current state", otherwise the read value would be wrong.
|
|
*
|
|
* When the Edit process stops, the Subscriber is removed from the DataProvider, so buffers can be freed up.
|
|
* A new ToolPanelListener::panelChanged event is also thrown to update the preview again, without the tool's
|
|
* graphical objects. The Edit button is also toggled off (by the user or programatically).
|
|
*
|
|
* It means that each Edit buttons toggled on will start an update of the preview which might or might not create
|
|
* a new History entry, depending on the ProcEvent used.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
class ObjectMOBuffer
|
|
{
|
|
private:
|
|
|
|
// Used to draw the objects where the color correspond to the object's ID, in order to find the correct object when hovering
|
|
Cairo::RefPtr<Cairo::ImageSurface> objectMap;
|
|
ObjectMode objectMode;
|
|
|
|
protected:
|
|
|
|
// To avoid duplicated information, we points to a EditDataProvider that contains the current EditSubscriber
|
|
// instead of pointing to the EditSubscriber directly
|
|
EditDataProvider* dataProvider;
|
|
|
|
void createBuffer(int width, int height);
|
|
void resize(int newWidth, int newHeight);
|
|
void flush();
|
|
EditSubscriber *getEditSubscriber ();
|
|
|
|
public:
|
|
explicit ObjectMOBuffer (EditDataProvider *dataProvider);
|
|
~ObjectMOBuffer();
|
|
|
|
EditDataProvider* getDataProvider ();
|
|
void setObjectMode (ObjectMode newType);
|
|
ObjectMode getObjectMode ();
|
|
|
|
Cairo::RefPtr<Cairo::ImageSurface>& getObjectMap ();
|
|
|
|
// return true if the buffer has been allocated
|
|
bool bufferCreated();
|
|
|
|
int getObjectID(const rtengine::Coord& location);
|
|
};
|
|
|
|
|
|
/** @brief Coordinate system where the widgets will be drawn
|
|
*
|
|
* The EditCoordSystem is used to define a screen and an image coordinate system.
|
|
*/
|
|
class EditCoordSystem
|
|
{
|
|
public:
|
|
virtual ~EditCoordSystem() {}
|
|
|
|
/// Convert the widget's DrawingArea (i.e. preview area) coords to the edit buffer coords
|
|
virtual void screenCoordToCropBuffer (int phyx, int phyy, int& cropx, int& cropy) = 0;
|
|
/// Convert the widget's DrawingArea (i.e. preview area) coords to the full image coords
|
|
virtual void screenCoordToImage (int phyx, int phyy, int& imgx, int& imgy) = 0;
|
|
/// Convert the image coords to the widget's DrawingArea (i.e. preview area) coords
|
|
virtual void imageCoordToScreen (int imgx, int imgy, int& phyx, int& phyy) = 0;
|
|
/// Convert the image coords to the crop's canvas coords (full image + padding)
|
|
virtual void imageCoordToCropCanvas (int imgx, int imgy, int& phyx, int& phyy) = 0;
|
|
/// Convert the image coords to the edit buffer coords (includes borders)
|
|
virtual void imageCoordToCropBuffer (int imgx, int imgy, int& phyx, int& phyy) = 0;
|
|
/// Convert the image coords to the displayed image coords (no borders here)
|
|
virtual void imageCoordToCropImage (int imgx, int imgy, int& phyx, int& phyy) = 0;
|
|
/// Convert a size value from the preview's scale to the image's scale
|
|
virtual int scaleValueToImage (int value) = 0;
|
|
/// Convert a size value from the preview's scale to the image's scale
|
|
virtual float scaleValueToImage (float value) = 0;
|
|
/// Convert a size value from the preview's scale to the image's scale
|
|
virtual double scaleValueToImage (double value) = 0;
|
|
/// Convert a size value from the image's scale to the preview's scale
|
|
virtual int scaleValueToCanvas (int value) = 0;
|
|
/// Convert a size value from the image's scale to the preview's scale
|
|
virtual float scaleValueToCanvas (float value) = 0;
|
|
/// Convert a size value from the image's scale to the preview's scale
|
|
virtual double scaleValueToCanvas (double value) = 0;
|
|
};
|
|
|
|
class RGBColor
|
|
{
|
|
double r;
|
|
double g;
|
|
double b;
|
|
|
|
public:
|
|
RGBColor ();
|
|
explicit RGBColor (double r, double g, double b);
|
|
explicit RGBColor (char r, char g, char b);
|
|
|
|
void setColor (double r, double g, double b);
|
|
void setColor (char r, char g, char b);
|
|
|
|
double getR ();
|
|
double getG ();
|
|
double getB ();
|
|
};
|
|
|
|
class RGBAColor : public RGBColor
|
|
{
|
|
double a;
|
|
|
|
public:
|
|
RGBAColor ();
|
|
explicit RGBAColor (double r, double g, double b, double a);
|
|
explicit RGBAColor (char r, char g, char b, char a);
|
|
|
|
void setColor (double r, double g, double b, double a);
|
|
void setColor (char r, char g, char b, char a);
|
|
|
|
double getA ();
|
|
};
|
|
|
|
/// @brief Displayable and MouseOver geometry base class
|
|
class Geometry
|
|
{
|
|
public:
|
|
/// @brief Graphical state of the element
|
|
enum State {
|
|
NORMAL, /// Default state
|
|
ACTIVE, /// Focused state
|
|
PRELIGHT, /// Hovered state
|
|
DRAGGED, /// When being dragged
|
|
INSENSITIVE /// Displayed but insensitive
|
|
};
|
|
|
|
/// @brief Coordinate space and origin of the point
|
|
enum Datum {
|
|
IMAGE, /// Image coordinate system with image's top left corner as origin
|
|
CLICKED_POINT, /// Screen coordinate system with clicked point as origin
|
|
CURSOR /// Screen coordinate system with actual cursor position as origin
|
|
};
|
|
enum Flags {
|
|
F_VISIBLE = 1 << 0, /// true if the geometry have to be drawn on the visible layer
|
|
F_HOVERABLE = 1 << 1, /// true if the geometry have to be drawn on the "mouse over" layer
|
|
F_AUTO_COLOR = 1 << 2, /// true if the color depend on the state value, not the color field above
|
|
};
|
|
|
|
/// @brief Key point of the image's rectangle that is used to locate the icon copy to the target point:
|
|
enum DrivenPoint {
|
|
DP_CENTERCENTER,
|
|
DP_TOPLEFT,
|
|
DP_TOPCENTER,
|
|
DP_TOPRIGHT,
|
|
DP_CENTERRIGHT,
|
|
DP_BOTTOMRIGHT,
|
|
DP_BOTTOMCENTER,
|
|
DP_BOTTOMLEFT,
|
|
DP_CENTERLEFT
|
|
};
|
|
|
|
protected:
|
|
RGBColor innerLineColor;
|
|
RGBColor outerLineColor;
|
|
short flags;
|
|
|
|
public:
|
|
float innerLineWidth; // ...outerLineWidth = innerLineWidth+2
|
|
Datum datum;
|
|
State state; // set by the Subscriber
|
|
|
|
Geometry ();
|
|
virtual ~Geometry() {}
|
|
|
|
void setInnerLineColor (double r, double g, double b);
|
|
void setInnerLineColor (char r, char g, char b);
|
|
RGBColor getInnerLineColor ();
|
|
void setOuterLineColor (double r, double g, double b);
|
|
void setOuterLineColor (char r, char g, char b);
|
|
RGBColor getOuterLineColor ();
|
|
double getOuterLineWidth ();
|
|
double getMouseOverLineWidth ();
|
|
void setAutoColor (bool aColor);
|
|
bool isVisible ();
|
|
void setVisible (bool visible);
|
|
bool isHoverable ();
|
|
void setHoverable (bool visible);
|
|
|
|
|
|
// setActive will enable/disable the visible and hoverable flags in one shot!
|
|
void setActive (bool active);
|
|
|
|
virtual void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
|
|
virtual void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
|
|
virtual void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0;
|
|
};
|
|
|
|
class Circle : public Geometry
|
|
{
|
|
public:
|
|
rtengine::Coord center;
|
|
int radius;
|
|
bool filled;
|
|
bool radiusInImageSpace; /// If true, the radius depend on the image scale; if false, it is a fixed 'screen' size
|
|
|
|
Circle ();
|
|
Circle (rtengine::Coord& center, int radius, bool filled = false, bool radiusInImageSpace = false);
|
|
Circle (int centerX, int centerY, int radius, bool filled = false, bool radiusInImageSpace = false);
|
|
|
|
void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
};
|
|
|
|
class Line : public Geometry
|
|
{
|
|
public:
|
|
rtengine::Coord begin;
|
|
rtengine::Coord end;
|
|
|
|
Line ();
|
|
Line (rtengine::Coord& begin, rtengine::Coord& end);
|
|
Line (int beginX, int beginY, int endX, int endY);
|
|
|
|
void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
};
|
|
|
|
class Polyline : public Geometry
|
|
{
|
|
public:
|
|
std::vector<rtengine::Coord> points;
|
|
bool filled;
|
|
|
|
Polyline ();
|
|
|
|
void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
};
|
|
|
|
class Rectangle : public Geometry
|
|
{
|
|
public:
|
|
rtengine::Coord topLeft;
|
|
rtengine::Coord bottomRight;
|
|
bool filled;
|
|
|
|
Rectangle ();
|
|
|
|
void setXYWH(int left, int top, int width, int height);
|
|
void setXYXY(int left, int top, int right, int bottom);
|
|
void setXYWH(rtengine::Coord topLeft, rtengine::Coord widthHeight);
|
|
void setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight);
|
|
void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
};
|
|
|
|
class OPIcon : public Geometry // OP stands for "On Preview"
|
|
{
|
|
|
|
private:
|
|
Cairo::RefPtr<Cairo::ImageSurface> normalImg;
|
|
Cairo::RefPtr<Cairo::ImageSurface> prelightImg;
|
|
Cairo::RefPtr<Cairo::ImageSurface> activeImg;
|
|
Cairo::RefPtr<Cairo::ImageSurface> draggedImg;
|
|
Cairo::RefPtr<Cairo::ImageSurface> insensitiveImg;
|
|
|
|
static void setPaths(Options &opt);
|
|
static void updateImages();
|
|
void changeImage(Glib::ustring &newImage);
|
|
static Glib::ustring findIconAbsolutePath(const Glib::ustring &iconFName);
|
|
void drawImage (const Cairo::RefPtr<Cairo::ImageSurface> &img, Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawMOImage (const Cairo::RefPtr<Cairo::ImageSurface> &img, Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drivenPointToRectangle(const rtengine::Coord &pos, rtengine::Coord &topLeft, rtengine::Coord &bottomRight, int W, int H);
|
|
|
|
public:
|
|
DrivenPoint drivenPoint;
|
|
rtengine::Coord position;
|
|
|
|
OPIcon (const Cairo::RefPtr<Cairo::ImageSurface> &normal,
|
|
const Cairo::RefPtr<Cairo::ImageSurface> &active,
|
|
const Cairo::RefPtr<Cairo::ImageSurface> &prelight = {},
|
|
const Cairo::RefPtr<Cairo::ImageSurface> &dragged = {},
|
|
const Cairo::RefPtr<Cairo::ImageSurface> &insensitive = {},
|
|
DrivenPoint drivenPoint = DP_CENTERCENTER);
|
|
OPIcon (Glib::ustring normalImage, Glib::ustring activeImage, Glib::ustring prelightImage = "", Glib::ustring draggedImage = "", Glib::ustring insensitiveImage = "", DrivenPoint drivenPoint = DP_CENTERCENTER);
|
|
const Cairo::RefPtr<Cairo::ImageSurface> getNormalImg();
|
|
const Cairo::RefPtr<Cairo::ImageSurface> getPrelightImg();
|
|
const Cairo::RefPtr<Cairo::ImageSurface> getActiveImg();
|
|
const Cairo::RefPtr<Cairo::ImageSurface> getDraggedImg();
|
|
const Cairo::RefPtr<Cairo::ImageSurface> getInsensitiveImg();
|
|
void drawOuterGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawInnerGeometry (Cairo::RefPtr<Cairo::Context> &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
void drawToMOChannel (Cairo::RefPtr<Cairo::Context> &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem);
|
|
};
|
|
|
|
class OPAdjuster : public Geometry // OP stands for "On Preview"
|
|
{
|
|
|
|
};
|
|
|
|
/// @brief Method for client tools needing Edit information
|
|
class EditSubscriber
|
|
{
|
|
|
|
public:
|
|
|
|
private:
|
|
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
|
|
EditType editingType;
|
|
BufferType bufferType;
|
|
EditDataProvider *provider;
|
|
|
|
protected:
|
|
std::vector<Geometry*> visibleGeometry; /// displayed geometry
|
|
std::vector<Geometry*> mouseOverGeometry; /// mouseOver geometry, drawn in a hidden buffer
|
|
enum {
|
|
ES_ACTION_NONE, ///
|
|
ES_ACTION_DRAGGING, /// set action to this value in the buttonPressed event to start dragging and ask for drag event
|
|
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 !
|
|
} action; /// object mode only, ignored in Pipette mode
|
|
|
|
public:
|
|
explicit EditSubscriber (EditType editType);
|
|
virtual ~EditSubscriber () {}
|
|
|
|
void setEditProvider(EditDataProvider *provider);
|
|
EditDataProvider* getEditProvider ();
|
|
void setEditID(EditUniqueID ID, BufferType buffType);
|
|
bool isCurrentSubscriber();
|
|
virtual void subscribe();
|
|
virtual void unsubscribe();
|
|
virtual void switchOffEditMode (); /// Occurs when the user want to stop the editing mode
|
|
EditUniqueID getEditID();
|
|
EditType getEditingType();
|
|
BufferType getPipetteBufferType();
|
|
bool isDragging(); /// Returns true if something is being dragged and drag events has to be sent (object mode only)
|
|
bool isPicking(); /// Returns true if something is being picked
|
|
|
|
/** @brief Get the cursor to be displayed when above handles
|
|
@param objectID object currently "hovered" */
|
|
virtual CursorShape getCursor (const int objectID);
|
|
|
|
/** @brief Triggered when the mouse is moving over an object
|
|
This method is also triggered when the cursor is moving over the image in ET_PIPETTE mode
|
|
@param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool mouseOver (const int modifierKey);
|
|
|
|
/** @brief Triggered when mouse button 1 is pressed, together with the CTRL modifier key if the subscriber is of type ET_PIPETTE
|
|
Once the key is pressed, RT will enter in drag1 mode on subsequent mouse movements
|
|
@param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool button1Pressed (const int modifierKey);
|
|
|
|
/** @brief Triggered when mouse button 1 is released
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool button1Released ();
|
|
|
|
/** @brief Triggered when mouse button 2 is pressed (middle button)
|
|
Once the key is pressed, RT will enter in drag2 mode on subsequent mouse movements
|
|
@param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool button2Pressed (const int modifierKey);
|
|
|
|
/** @brief Triggered when mouse button 2 is released (middle button)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool button2Released ();
|
|
|
|
/** @brief Triggered when mouse button 3 is pressed (right button)
|
|
Once the key is pressed, RT will enter in drag3 mode on subsequent mouse movements
|
|
@param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool button3Pressed (const int modifierKey);
|
|
|
|
/** @brief Triggered when mouse button 3 is released (right button)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool button3Released ();
|
|
|
|
/** @brief Triggered when the user is moving while holding down mouse button 1
|
|
@param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool drag1 (const int modifierKey);
|
|
|
|
/** @brief Triggered when the user is moving while holding down mouse button 2
|
|
@param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool drag2 (const int modifierKey);
|
|
|
|
/** @brief Triggered when the user is moving while holding down mouse button 3
|
|
@param modifierKey Gtk's event modifier key (GDK_CONTROL_MASK | GDK_SHIFT_MASK | ...)
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool drag3 (const int modifierKey);
|
|
|
|
/** @brief Triggered when the user is releasing mouse button 1 while in action==ES_ACTION_PICKING mode
|
|
No modifier key is provided, since having a different modifier key than on button press will set picked to false.
|
|
@param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys.
|
|
If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected.
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool pick1 (const bool picked);
|
|
|
|
/** @brief Triggered when the user is releasing mouse button 2 while in action==ES_ACTION_PICKING mode
|
|
@param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys.
|
|
If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected.
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool pick2 (const bool picked);
|
|
|
|
/** @brief Triggered when the user is releasing mouse button 3 while in action==ES_ACTION_PICKING mode
|
|
@param picked True if the cursor is still above the the same object than on button pressed and with the same modifier keys.
|
|
If false, the user moved the cursor away or the modifier key is different, so the element is considered as NOT selected.
|
|
@return true if the preview has to be redrawn, false otherwise */
|
|
virtual bool pick3 (const bool picked);
|
|
|
|
/** @brief Get the geometry to be shown to the user */
|
|
const std::vector<Geometry*>& getVisibleGeometry ();
|
|
|
|
/** @brief Get the geometry to be drawn in the "mouse over" channel, hidden from the user */
|
|
const std::vector<Geometry*>& getMouseOverGeometry ();
|
|
};
|
|
|
|
/** @brief Class to handle the furniture of data to the subscribers.
|
|
*
|
|
* 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
|
|
* trigger the usual event so that the image will be reprocessed to compute the buffer of the current subscriber.
|
|
*/
|
|
class EditDataProvider
|
|
{
|
|
|
|
private:
|
|
EditSubscriber *currSubscriber;
|
|
|
|
public:
|
|
int object; /// ET_OBJECTS mode: Object detected under the cursor, 0 otherwise; ET_PIPETTE mode: 1 if above the image, 0 otherwise
|
|
float pipetteVal[3]; /// Current pipette values; if bufferType==BT_SINGLEPLANE_FLOAT, #2 & #3 will be set to 0
|
|
|
|
rtengine::Coord posScreen; /// Location of the mouse button press, in preview image space
|
|
rtengine::Coord posImage; /// Location of the mouse button press, in the full image space
|
|
rtengine::Coord deltaScreen; /// Delta relative to posScreen
|
|
rtengine::Coord deltaImage; /// Delta relative to posImage
|
|
rtengine::Coord deltaPrevScreen; /// Delta relative to the previous mouse location, in preview image space
|
|
rtengine::Coord deltaPrevImage; /// Delta relative to the previous mouse location, in the full image space
|
|
|
|
EditDataProvider();
|
|
virtual ~EditDataProvider() {}
|
|
|
|
virtual void subscribe(EditSubscriber *subscriber);
|
|
virtual void unsubscribe(); /// Occurs when the subscriber has been switched off first
|
|
virtual void switchOffEditMode (); /// Occurs when the user want to stop the editing mode
|
|
virtual CursorShape getCursor(int objectID);
|
|
int getPipetteRectSize ();
|
|
EditSubscriber* getCurrSubscriber();
|
|
virtual void getImageSize (int &w, int&h) = 0;
|
|
};
|
|
|
|
inline EditDataProvider* ObjectMOBuffer::getDataProvider () {
|
|
return dataProvider;
|
|
}
|
|
|
|
inline ObjectMode ObjectMOBuffer::getObjectMode () {
|
|
return objectMode;
|
|
}
|
|
|
|
inline Cairo::RefPtr<Cairo::ImageSurface>& ObjectMOBuffer::getObjectMap () {
|
|
return objectMap;
|
|
}
|
|
|
|
inline void RGBColor::setColor (double r, double g, double b) {
|
|
this->r = r;
|
|
this->g = g;
|
|
this->b = b;
|
|
}
|
|
|
|
inline void RGBColor::setColor (char r, char g, char b) {
|
|
this->r = double (r) / 255.;
|
|
this->g = double (g) / 255.;
|
|
this->b = double (b) / 255.;
|
|
}
|
|
|
|
inline double RGBColor::getR () {
|
|
return r;
|
|
}
|
|
|
|
inline double RGBColor::getG () {
|
|
return g;
|
|
}
|
|
|
|
inline double RGBColor::getB () {
|
|
return b;
|
|
}
|
|
|
|
inline void RGBAColor::setColor (double r, double g, double b, double a) {
|
|
RGBColor::setColor (r, g, b);
|
|
this->a = a;
|
|
}
|
|
|
|
inline void RGBAColor::setColor (char r, char g, char b, char a) {
|
|
RGBColor::setColor (r, g, b);
|
|
this->a = double (a) / 255.;
|
|
}
|
|
|
|
inline double RGBAColor::getA () {
|
|
return a;
|
|
}
|
|
|
|
inline void Geometry::setInnerLineColor (double r, double g, double b) {
|
|
innerLineColor.setColor (r, g, b);
|
|
flags &= ~F_AUTO_COLOR;
|
|
}
|
|
|
|
inline void Geometry::setInnerLineColor (char r, char g, char b) {
|
|
innerLineColor.setColor (r, g, b);
|
|
flags &= ~F_AUTO_COLOR;
|
|
}
|
|
|
|
inline void Geometry::setOuterLineColor (double r, double g, double b) {
|
|
outerLineColor.setColor (r, g, b);
|
|
flags &= ~F_AUTO_COLOR;
|
|
}
|
|
|
|
inline double Geometry::getOuterLineWidth () {
|
|
return double (innerLineWidth) + 2.;
|
|
}
|
|
|
|
inline void Geometry::setOuterLineColor (char r, char g, char b) {
|
|
outerLineColor.setColor (r, g, b);
|
|
flags &= ~F_AUTO_COLOR;
|
|
}
|
|
|
|
inline double Geometry::getMouseOverLineWidth () {
|
|
return getOuterLineWidth () + 2.;
|
|
}
|
|
|
|
inline void Geometry::setAutoColor (bool aColor) {
|
|
if (aColor) {
|
|
flags |= F_AUTO_COLOR;
|
|
} else {
|
|
flags &= ~F_AUTO_COLOR;
|
|
}
|
|
}
|
|
|
|
inline bool Geometry::isVisible () {
|
|
return flags & F_VISIBLE;
|
|
}
|
|
|
|
inline void Geometry::setVisible (bool visible) {
|
|
if (visible) {
|
|
flags |= F_VISIBLE;
|
|
} else {
|
|
flags &= ~F_VISIBLE;
|
|
}
|
|
}
|
|
|
|
inline bool Geometry::isHoverable () {
|
|
return flags & F_HOVERABLE;
|
|
}
|
|
|
|
inline void Geometry::setHoverable (bool hoverable) {
|
|
if (hoverable) {
|
|
flags |= F_HOVERABLE;
|
|
} else {
|
|
flags &= ~F_HOVERABLE;
|
|
}
|
|
}
|
|
|
|
inline void Geometry::setActive (bool active) {
|
|
if (active) {
|
|
flags |= (F_VISIBLE | F_HOVERABLE);
|
|
} else {
|
|
flags &= ~(F_VISIBLE | F_HOVERABLE);
|
|
}
|
|
}
|
|
|
|
inline EditDataProvider* EditSubscriber::getEditProvider () {
|
|
return provider;
|
|
}
|
|
|
|
inline CursorShape EditSubscriber::getCursor (const int objectID) {
|
|
return CSOpenHand;
|
|
}
|
|
|
|
inline bool EditSubscriber::mouseOver (const int modifierKey) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::button1Pressed (const int modifierKey) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::button1Released () {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::button2Pressed (const int modifierKey) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::button2Released () {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::button3Pressed (const int modifierKey) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::button3Released () {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::drag1 (const int modifierKey) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::drag2 (const int modifierKey) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::drag3 (const int modifierKey) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::pick1 (const bool picked) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::pick2 (const bool picked) {
|
|
return false;
|
|
}
|
|
|
|
inline bool EditSubscriber::pick3 (const bool picked) {
|
|
return false;
|
|
}
|
|
|
|
inline const std::vector<Geometry*>& EditSubscriber::getVisibleGeometry () {
|
|
return visibleGeometry;
|
|
}
|
|
|
|
inline const std::vector<Geometry*>& EditSubscriber::getMouseOverGeometry () {
|
|
return mouseOverGeometry;
|
|
}
|
|
|
|
inline int EditDataProvider::getPipetteRectSize () {
|
|
return 8; // TODO: make a GUI
|
|
}
|
|
|
|
inline Geometry::Geometry () :
|
|
innerLineColor (char (255), char (255), char (255)), outerLineColor (
|
|
char (0), char (0), char (0)), flags (
|
|
F_VISIBLE | F_HOVERABLE | F_AUTO_COLOR), innerLineWidth (1.5f), datum (
|
|
IMAGE), state (NORMAL) {
|
|
}
|
|
|
|
inline Circle::Circle () :
|
|
center (100, 100), radius (10), filled (false), radiusInImageSpace (
|
|
false) {
|
|
}
|
|
|
|
inline Rectangle::Rectangle () :
|
|
topLeft (0, 0), bottomRight (10, 10), filled (false) {
|
|
}
|
|
|
|
inline Polyline::Polyline () :
|
|
filled (false) {
|
|
}
|
|
|
|
inline Line::Line () :
|
|
begin (10, 10), end (100, 100) {
|
|
}
|
|
|
|
inline RGBAColor::RGBAColor () :
|
|
RGBColor (0., 0., 0.), a (0.) {
|
|
}
|
|
|
|
inline RGBColor::RGBColor () :
|
|
r (0.), g (0.), b (0.) {
|
|
}
|
|
|
|
inline RGBColor::RGBColor (double r, double g, double b) :
|
|
r (r), g (g), b (b) {
|
|
}
|
|
|
|
inline RGBColor::RGBColor (char r, char g, char b) :
|
|
r (double (r) / 255.), g (double (g) / 255.), b (double (b) / 255.) {
|
|
}
|
|
|
|
inline RGBAColor::RGBAColor (double r, double g, double b, double a) :
|
|
RGBColor (r, g, b), a (a) {
|
|
}
|
|
|
|
inline RGBAColor::RGBAColor (char r, char g, char b, char a) :
|
|
RGBColor (r, g, b), a (double (a) / 255.) {
|
|
}
|
|
|
|
inline Circle::Circle (rtengine::Coord& center, int radius, bool filled,
|
|
bool radiusInImageSpace) :
|
|
center (center), radius (radius), filled (filled), radiusInImageSpace (
|
|
radiusInImageSpace) {
|
|
}
|
|
|
|
inline Circle::Circle (int centerX, int centerY, int radius, bool filled,
|
|
bool radiusInImageSpace) :
|
|
center (centerX, centerY), radius (radius), filled (filled), radiusInImageSpace (
|
|
radiusInImageSpace) {
|
|
}
|
|
|
|
inline Line::Line (rtengine::Coord& begin, rtengine::Coord& end) :
|
|
begin (begin), end (end) {
|
|
}
|
|
|
|
inline Line::Line (int beginX, int beginY, int endX, int endY) :
|
|
begin (beginX, beginY), end (endX, endY) {
|
|
}
|
|
|
|
#endif
|