From c351be41b83c4a83a6ec0d283562a9a163bdf082 Mon Sep 17 00:00:00 2001 From: Hombre Date: Thu, 20 Aug 2015 02:33:46 +0200 Subject: [PATCH] Add some new features to the On Preview Objects mechanism --- rtengine/CMakeLists.txt | 2 +- rtengine/coord.cc | 39 +++++ {rtgui => rtengine}/coord.h | 42 ++++- rtengine/editbuffer.h | 1 + rtengine/procparams.h | 3 +- rtgui/cropwindow.cc | 107 +++++++++---- rtgui/curveeditor.cc | 2 +- rtgui/curveeditor.h | 7 +- rtgui/edit.cc | 286 ++++++++++++++++++++------------- rtgui/edit.h | 309 ++++++++++++++++++++++++++++++------ rtgui/editenums.h | 2 +- rtgui/gradient.cc | 29 ++-- rtgui/gradient.h | 5 +- rtgui/inspector.cc | 8 +- rtgui/inspector.h | 4 +- 15 files changed, 618 insertions(+), 228 deletions(-) create mode 100644 rtengine/coord.cc rename {rtgui => rtengine}/coord.h (84%) diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 0cda358e0..70c593a9c 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -11,7 +11,7 @@ set (RTENGINESOURCEFILES safegtk.cc colortemp.cc curves.cc flatcurves.cc diagona dfmanager.cc ffmanager.cc rawimage.cc image8.cc image16.cc imagefloat.cc imagedata.cc imageio.cc improcfun.cc init.cc dcrop.cc loadinitial.cc procparams.cc rawimagesource.cc demosaic_algos.cc shmap.cc simpleprocess.cc refreshmap.cc fast_demo.cc amaze_demosaic_RT.cc CA_correct_RT.cc cfa_linedn_RT.cc green_equil_RT.cc hilite_recon.cc expo_before_b.cc - stdimagesource.cc myfile.cc iccjpeg.cc hlmultipliers.cc improccoordinator.cc editbuffer.cc + stdimagesource.cc myfile.cc iccjpeg.cc hlmultipliers.cc improccoordinator.cc editbuffer.cc coord.cc processingjob.cc rtthumbnail.cc utils.cc labimage.cc slicer.cc cieimage.cc iplab2rgb.cc ipsharpen.cc iptransform.cc ipresize.cc ipvibrance.cc imagedimensions.cc jpeg_memsrc.cc jdatasrc.cc iimage.cc diff --git a/rtengine/coord.cc b/rtengine/coord.cc new file mode 100644 index 000000000..c35d71e73 --- /dev/null +++ b/rtengine/coord.cc @@ -0,0 +1,39 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * 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 . + */ + +#include "coord.h" + +namespace rtengine +{ + +void Coord::setFromPolar(PolarCoord polar) +{ + while (polar.angle < 0.f) { + polar.angle += 360.f; + } + + while (polar.angle > 360.f) { + polar.angle -= 360.f; + } + + x = polar.radius * cos(polar.angle / 180.f * M_PI); + y = polar.radius * sin(polar.angle / 180.f * M_PI); +} + +} diff --git a/rtgui/coord.h b/rtengine/coord.h similarity index 84% rename from rtgui/coord.h rename to rtengine/coord.h index cf0d9be29..0b134ce21 100644 --- a/rtgui/coord.h +++ b/rtengine/coord.h @@ -16,12 +16,17 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ -#ifndef _COORD_H_ -#define _COORD_H_ +#ifndef __COORD__ +#define __COORD__ + +#include "rt_math.h" + +namespace rtengine +{ class PolarCoord; -// Do not confuse with rtengine::Coord2D, Coord is for the GUI +// Do not confuse with rtengine::Coord2D, this one is for the GUI class Coord { public: @@ -51,6 +56,16 @@ public: return retval; } + bool operator== (const Coord& other) const + { + return other.x == x && other.y == y; + } + + bool operator!= (const Coord& other) const + { + return other.x != x || other.y != y; + } + void operator+=(const Coord & rhs) { x += rhs.x; @@ -91,6 +106,14 @@ public: PolarCoord() : radius(1.), angle(0.) {} PolarCoord(double radius, double angle) : radius(radius), angle(angle) {} + PolarCoord(Coord start, Coord end) : radius(1.), angle(0.) + { + setFromCartesian(start, end); + } + PolarCoord(Coord delta) : radius(1.), angle(0.) + { + setFromCartesian(delta); + } void set (double radius, double angle) { @@ -134,6 +157,16 @@ public: } } + bool operator== (const PolarCoord& other) const + { + return other.radius == radius && other.angle == angle; + } + + bool operator!= (const PolarCoord& other) const + { + return other.radius != radius || other.angle != angle; + } + void operator+=(const PolarCoord & rhs) { Coord thisCoord, rhsCoord; @@ -182,4 +215,7 @@ public: }; + +} + #endif diff --git a/rtengine/editbuffer.h b/rtengine/editbuffer.h index ba1cc7e0c..1a55a39ed 100644 --- a/rtengine/editbuffer.h +++ b/rtengine/editbuffer.h @@ -22,6 +22,7 @@ #include "../rtgui/edit.h" #include "array2D.h" #include "iimage.h" +#include "coord.h" namespace rtengine { diff --git a/rtengine/procparams.h b/rtengine/procparams.h index 47dccb47f..ad8c963e8 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -24,6 +24,7 @@ #include #include #include "LUT.h" +#include "coord.h" class ParamsEdited; @@ -1221,7 +1222,7 @@ public: ResizeParams resize; ///< Resize parameters ColorManagementParams icm; ///< profiles/color spaces used during the image processing RAWParams raw; ///< RAW parameters before demosaicing - WaveletParams wavelet; ///< wavelet wavelet parameters + WaveletParams wavelet; ///< Wavelet parameters DirPyrEqualizerParams dirpyrequalizer; ///< directional pyramid wavelet parameters HSVEqualizerParams hsvequalizer; ///< hsv wavelet parameters FilmSimulationParams filmSimulation; ///< film simulation parameters diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index e787a11e1..1c5b62a33 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -267,6 +267,8 @@ void CropWindow::flawnOver (bool isFlawnOver) void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) { + bool needRedraw = true; // most common case ; not redrawing are exceptions + iarea->grabFocus (this); if (button == 1 && type == GDK_2BUTTON_PRESS && onArea (CropImage, x, y) && (state == SNormal || state == SCropImgMove)) { @@ -355,33 +357,55 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) } else if (iarea->getToolMode () == TMHand) { EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); - if (button == 1 && editSubscriber && cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && (editSubscriber->getEditingType() == ET_OBJECTS && iarea->object > -1) ) { - editSubscriber->button1Pressed(bstate); - state = SEditDrag; - press_x = x; - press_y = y; - action_x = 0; - action_y = 0; - } else if (onArea (CropObserved, x, y)) { - state = SObservedMove; - press_x = x; - press_y = y; - action_x = 0; - action_y = 0; - } else if (button == 1 && editSubscriber && cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && (editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK)) ) { - editSubscriber->button1Pressed(bstate); - state = SEditDrag; - press_x = x; - press_y = y; - action_x = 0; - action_y = 0; - } else if(zoomSteps[cropZoom].zoom > cropHandler.getFitZoom()) { // only allow move when image is only partial visible - state = SCropImgMove; + if (editSubscriber && cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && (editSubscriber->getEditingType() == ET_OBJECTS)) { + if (button == 1) { + needRedraw = editSubscriber->button1Pressed(bstate); + + if (editSubscriber->isDragging()) { + state = SEditDrag1; + } + } else if (button == 2) { + needRedraw = editSubscriber->button2Pressed(bstate); + + if (editSubscriber->isDragging()) { + state = SEditDrag2; + } + } else if (button == 3) { + needRedraw = editSubscriber->button3Pressed(bstate); + + if (editSubscriber->isDragging()) { + state = SEditDrag3; + } + } + press_x = x; press_y = y; action_x = 0; action_y = 0; } + + if (state != SEditDrag1 && state != SEditDrag2 && state != SEditDrag3) { + if (onArea (CropObserved, x, y)) { + state = SObservedMove; + press_x = x; + press_y = y; + action_x = 0; + action_y = 0; + } else if (button == 1 && editSubscriber && cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) && (editSubscriber->getEditingType() == ET_PIPETTE && (bstate & GDK_CONTROL_MASK)) ) { + editSubscriber->button1Pressed(bstate); + state = SEditDrag1; + press_x = x; + press_y = y; + action_x = 0; + action_y = 0; + } else if(zoomSteps[cropZoom].zoom > cropHandler.getFitZoom()) { // only allow move when image is only partial visible + state = SCropImgMove; + press_x = x; + press_y = y; + action_x = 0; + action_y = 0; + } + } } else if (onArea (CropObserved, x, y)) { state = SObservedMove; press_x = x; @@ -424,7 +448,10 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) } } - iarea->redraw (); + if (needRedraw) { + iarea->redraw (); + } + updateCursor (x, y); } @@ -472,8 +499,14 @@ void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) observedCropWin->remoteMoveReady (); state = SNormal; needRedraw = true; - } else if (state == SEditDrag) { - editSubscriber->button1Released(); + } else if (state == SEditDrag1 || state == SEditDrag2 || state == SEditDrag3) { + if (state == SEditDrag1) { + needRedraw = editSubscriber->button1Released(); + } else if (state == SEditDrag2) { + needRedraw = editSubscriber->button2Released(); + } else if (state == SEditDrag3) { + needRedraw = editSubscriber->button3Released(); + } if (editSubscriber) { rtengine::Crop* crop = static_cast(cropHandler.getCrop()); @@ -488,7 +521,7 @@ void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) Coord cropPos; screenCoordToCropBuffer(x, y, cropPos.x, cropPos.y); - if (editSubscriber->getEditingType() == ET_PIPETTE) { + if (state == SEditDrag1 && editSubscriber->getEditingType() == ET_PIPETTE) { iarea->object = onArea (CropImage, x, y) && !onArea (CropObserved, x, y) ? 1 : 0; //iarea->object = cropgl && cropgl->inImageArea(iarea->posImage.x, iarea->posImage.y) ? 1 : 0; @@ -506,9 +539,7 @@ void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) } } - if (editSubscriber->mouseOver(bstate)) { - iarea->redraw (); - } + needRedraw |= editSubscriber->mouseOver(bstate); } else { iarea->object = 0; } @@ -518,7 +549,6 @@ void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) iarea->deltaPrevImage.set(0, 0); iarea->deltaPrevScreen.set(0, 0); state = SNormal; - needRedraw = true; } if (cropgl && (state == SCropSelecting || state == SResizeH1 || state == SResizeH2 || state == SResizeW1 || state == SResizeW2 || state == SResizeTL || state == SResizeTR || state == SResizeBL || state == SResizeBR || state == SCropMove)) { @@ -703,7 +733,7 @@ void CropWindow::pointerMoved (int bstate, int x, int y) if (editSubscriber->mouseOver(bstate)) { iarea->redraw (); } - } else if (state == SEditDrag) { + } else if (state == SEditDrag1 || state == SEditDrag2 || state == SEditDrag3) { Coord currPos; action_x = x; action_y = y; @@ -721,8 +751,18 @@ void CropWindow::pointerMoved (int bstate, int x, int y) iarea->deltaPrevScreen = currPos - oldPosScreen; //printf(" action_ & xy (%d x %d) -> (%d x %d) = (%d x %d) + (%d x %d) / deltaPrev(%d x %d)\n", action_x, action_y, currPos.x, currPos.y, iarea->posScreen.x, iarea->posScreen.y, iarea->deltaScreen.x, iarea->deltaScreen.y, iarea->deltaPrevScreen.x, iarea->deltaPrevScreen.y); - if (editSubscriber->drag(bstate)) { - iarea->redraw (); + if (state == SEditDrag1) { + if (editSubscriber->drag1(bstate)) { + iarea->redraw (); + } + } else if (state == SEditDrag2) { + if (editSubscriber->drag2(bstate)) { + iarea->redraw (); + } + } else if (state == SEditDrag3) { + if (editSubscriber->drag3(bstate)) { + iarea->redraw (); + } } } } @@ -1465,7 +1505,6 @@ void CropWindow::expose (Cairo::RefPtr cr) if (editSubscriber && crop->bufferCreated()) { - // clip the region if (this != iarea->mainCropWindow) { cr->set_line_width (0.); cr->rectangle (x + imgX, y + imgY, imgW, imgH); diff --git a/rtgui/curveeditor.cc b/rtgui/curveeditor.cc index 840ea25e9..3cfe335b2 100644 --- a/rtgui/curveeditor.cc +++ b/rtgui/curveeditor.cc @@ -422,7 +422,7 @@ bool CurveEditor::button1Released() return true; } -bool CurveEditor::drag(int modifierKey) +bool CurveEditor::drag1(int modifierKey) { EditDataProvider* provider = getEditProvider(); subGroup->pipetteDrag(provider, modifierKey); diff --git a/rtgui/curveeditor.h b/rtgui/curveeditor.h index 2708c4df2..10e3d9668 100644 --- a/rtgui/curveeditor.h +++ b/rtgui/curveeditor.h @@ -35,9 +35,8 @@ class CurveEditorSubGroup; */ -/* - * This class is an interface between RT and the curve editor group ; it handles the methods - * related to a specific curve. It is created by CurveEditorGroup::addCurve +/** @brief This class is an interface between RT and the curve editor group + * It handles the methods related to a specific curve. It is created by CurveEditorGroup::addCurve */ class CurveEditor : public EditSubscriber { @@ -131,7 +130,7 @@ public: bool mouseOver(int modifierKey); bool button1Pressed(int modifierKey); bool button1Released(); - bool drag(int modifierKey); + bool drag1(int modifierKey); CursorShape getCursor(int objectID); diff --git a/rtgui/edit.cc b/rtgui/edit.cc index dc054cdc1..3efdea7f4 100644 --- a/rtgui/edit.cc +++ b/rtgui/edit.cc @@ -19,32 +19,21 @@ #include "edit.h" #include "../rtengine/editbuffer.h" - -void Coord::setFromPolar(PolarCoord polar) -{ - while (polar.angle < 0.f) { - polar.angle += 360.f; - } - - while (polar.angle > 360.f) { - polar.angle -= 360.f; - } - - x = polar.radius * cos(polar.angle / 180.f * M_PI); - y = polar.radius * sin(polar.angle / 180.f * M_PI); -} +#include "rtimage.h" RGBColor Geometry::getInnerLineColor () { RGBColor color; - if (flags & AUTO_COLOR) { + if (flags & F_AUTO_COLOR) { if (state == NORMAL) { - color.setColor (1., 1., 1.); // White + color.setColor (1., 1., 1.); // White + } else if (state == ACTIVE) { + color.setColor (1., 1., 0.); // Yellow } else if (state == PRELIGHT) { - color.setColor (1., 1., 0.); // Orange - } else if (state == DRAGGED) { - color.setColor (1., 0., 0.); // Red + color.setColor (1., 100. / 255., 0.); // Orange + } else if (state == DRAGGED) { + color.setColor (1., 0., 0.); // Red } } else { color = innerLineColor; @@ -57,7 +46,7 @@ RGBColor Geometry::getOuterLineColor () { RGBColor color; - if (flags & AUTO_COLOR) { + if (flags & F_AUTO_COLOR) { /* if (state == NORMAL) { color.setColor (0., 0., 0.); } // Black else if (state == PRELIGHT) { color.setColor (0., 0., 0.); } // Black @@ -73,10 +62,10 @@ RGBColor Geometry::getOuterLineColor () void Circle::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if (flags & ACTIVE) { + if ((flags & F_VISIBLE) && state != INSENSITIVE) { RGBColor color; - if (flags & AUTO_COLOR) { + if (flags & F_AUTO_COLOR) { color = getOuterLineColor(); } else { color = outerLineColor; @@ -85,7 +74,7 @@ void Circle::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::Edit cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width( getOuterLineWidth() ); - Coord center_ = center; + rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToScreen(double(radius)) : double(radius); if (datum == IMAGE) { @@ -93,7 +82,7 @@ void Circle::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::Edit } else if (datum == CLICKED_POINT) { center_ += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*M_PI); @@ -103,19 +92,22 @@ void Circle::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::Edit void Circle::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if (flags & ACTIVE) { - RGBColor color; + if (flags & F_VISIBLE) { + if (state != INSENSITIVE) { + RGBColor color; - if (flags & AUTO_COLOR) { - color = getInnerLineColor(); - } else { - color = innerLineColor; + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgb(color.getR(), color.getG(), color.getB()); } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width( innerLineWidth ); - Coord center_ = center; + rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToScreen(double(radius)) : double(radius); if (datum == IMAGE) { @@ -123,10 +115,10 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::Edit } else if (datum == CLICKED_POINT) { center_ += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } - if (filled) { + if (filled && state != INSENSITIVE) { cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*M_PI); if (innerLineWidth > 0.) { @@ -137,16 +129,29 @@ void Circle::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::Edit } } else if (innerLineWidth > 0.) { cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*M_PI); - cr->stroke(); + + if (state == INSENSITIVE) { + std::valarray ds(1); + ds[0] = 4; + cr->set_source_rgba(1.0, 1.0, 1.0, 0.618); + cr->stroke_preserve(); + cr->set_source_rgba(0.0, 0.0, 0.0, 0.618); + cr->set_dash(ds, 0); + cr->stroke(); + ds.resize(0); + cr->set_dash(ds, 0); + } else { + cr->stroke(); + } } } } void Circle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if (flags & ACTIVE) { + if (flags & F_HOVERABLE) { cr->set_line_width( getMouseOverLineWidth() ); - Coord center_ = center; + rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToScreen(double(radius)) : double(radius); if (datum == IMAGE) { @@ -154,7 +159,7 @@ void Circle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtrgetDataProvider()->posScreen; } else if (datum == CURSOR) { - center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + center_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } // drawing the lower byte's value @@ -195,10 +200,10 @@ void Circle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if (flags & ACTIVE) { + if ((flags & F_VISIBLE) && state != INSENSITIVE) { RGBColor color; - if (flags & AUTO_COLOR) { + if (flags & F_AUTO_COLOR) { color = getOuterLineColor(); } else { color = outerLineColor; @@ -207,8 +212,8 @@ void Line::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBu cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width( getOuterLineWidth() ); - Coord begin_ = begin; - Coord end_ = end; + rtengine::Coord begin_ = begin; + rtengine::Coord end_ = end; if (datum == IMAGE) { coordSystem.imageCoordToScreen(begin.x, begin.y, begin_.x, begin_.y); @@ -217,8 +222,8 @@ void Line::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBu begin_ += editBuffer->getDataProvider()->posScreen; end_ += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; - end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; + end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } cr->move_to(begin_.x + 0.5, begin_.y + 0.5); @@ -229,20 +234,23 @@ void Line::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBu void Line::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if ((flags & ACTIVE) && innerLineWidth > 0.) { - RGBColor color; + if ((flags & F_VISIBLE) && innerLineWidth > 0.) { + if (state != INSENSITIVE) { + RGBColor color; - if (flags & AUTO_COLOR) { - color = getInnerLineColor(); - } else { - color = innerLineColor; + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width(innerLineWidth); - Coord begin_ = begin; - Coord end_ = end; + rtengine::Coord begin_ = begin; + rtengine::Coord end_ = end; if (datum == IMAGE) { coordSystem.imageCoordToScreen(begin.x, begin.y, begin_.x, begin_.y); @@ -251,22 +259,37 @@ void Line::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBu begin_ += editBuffer->getDataProvider()->posScreen; end_ += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; - end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; + end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } cr->move_to(begin_.x + 0.5, begin_.y + 0.5); cr->line_to(end_.x + 0.5, end_.y + 0.5); - cr->stroke(); + + if (state == INSENSITIVE) { + std::valarray ds(1); + ds[0] = 4; + cr->set_source_rgba(1.0, 1.0, 1.0, 0.618); + cr->stroke_preserve(); + cr->set_source_rgba(0.0, 0.0, 0.0, 0.618); + cr->set_dash(ds, 0); + cr->stroke(); + ds.resize(0); + cr->set_dash(ds, 0); + } else { + cr->stroke(); + } } } -void Line::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) +void Line::drawToMOChannel(Cairo::RefPtr &cr, + Cairo::RefPtr &cr2, unsigned short id, + rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if (flags & ACTIVE) { + if (flags & F_HOVERABLE) { cr->set_line_width( getMouseOverLineWidth() ); - Coord begin_ = begin; - Coord end_ = end; + rtengine::Coord begin_ = begin; + rtengine::Coord end_ = end; if (datum == IMAGE) { coordSystem.imageCoordToCropBuffer(begin.x, begin.y, begin_.x, begin_.y); @@ -275,8 +298,8 @@ void Line::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtrgetDataProvider()->posScreen; end_ += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; - end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + begin_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; + end_ += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } // drawing the lower byte's value @@ -299,10 +322,10 @@ void Line::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if ((flags & ACTIVE) && points.size() > 1) { + if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1) { RGBColor color; - if (flags & AUTO_COLOR) { + if (flags & F_AUTO_COLOR) { color = getOuterLineColor(); } else { color = outerLineColor; @@ -311,7 +334,7 @@ void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::Ed cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width( getOuterLineWidth() ); - Coord currPos; + rtengine::Coord currPos; for (unsigned int i = 0; i < points.size(); ++i) { currPos = points.at(i); @@ -321,7 +344,7 @@ void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::Ed } else if (datum == CLICKED_POINT) { currPos += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } if (!i) { @@ -342,20 +365,23 @@ void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::Ed void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if ((flags & ACTIVE) && points.size() > 1) { - RGBColor color; + if ((flags & F_VISIBLE) && points.size() > 1) { + if (state != INSENSITIVE) { + RGBColor color; - if (flags & AUTO_COLOR) { - color = getInnerLineColor(); - } else { - color = innerLineColor; + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); + cr->set_line_width( innerLineWidth ); - if (filled) { - Coord currPos; + if (filled && state != INSENSITIVE) { + rtengine::Coord currPos; for (unsigned int i = 0; i < points.size(); ++i) { currPos = points.at(i); @@ -365,7 +391,7 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::Ed } else if (datum == CLICKED_POINT) { currPos += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } if (!i) { @@ -382,17 +408,17 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::Ed cr->fill(); } } else if (innerLineWidth > 0.) { - Coord currPos; + rtengine::Coord currPos; for (unsigned int i = 0; i < points.size(); ++i) { currPos = points.at(i); - if (datum == IMAGE) { + if (datum == IMAGE) { coordSystem.imageCoordToScreen(points.at(i).x, points.at(i).y, currPos.x, currPos.y); } else if (datum == CLICKED_POINT) { currPos += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } if (!i) { @@ -402,22 +428,34 @@ void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::Ed } } - cr->stroke(); + if (state == INSENSITIVE) { + std::valarray ds(1); + ds[0] = 4; + cr->set_source_rgba(1.0, 1.0, 1.0, 0.618); + cr->stroke_preserve(); + cr->set_source_rgba(0.0, 0.0, 0.0, 0.618); + cr->set_dash(ds, 0); + cr->stroke(); + ds.resize(0); + cr->set_dash(ds, 0); + } else { + cr->stroke(); + } } } } void Polyline::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if ((flags & ACTIVE) && points.size() > 1) { - Coord currPos; + if ((flags & F_HOVERABLE) && points.size() > 1) { + rtengine::Coord currPos; // drawing the lower byte's value unsigned short a = (id + 1) & 0xFF; cr->set_source_rgba (0., 0., 0., double(a) / 255.); for (unsigned int i = 0; i < points.size(); ++i) { - cr->set_line_width( getOuterLineWidth() ); + cr->set_line_width( getMouseOverLineWidth() ); currPos = points.at(i); if (datum == IMAGE) { @@ -425,7 +463,7 @@ void Polyline::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr } else if (datum == CLICKED_POINT) { currPos += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } if (!i) { @@ -452,7 +490,7 @@ void Polyline::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr cr2->set_source_rgba (0., 0., 0., double(a) / 255.); for (unsigned int i = 0; i < points.size(); ++i) { - cr2->set_line_width( getOuterLineWidth() ); + cr2->set_line_width( getMouseOverLineWidth() ); currPos = points.at(i); if (datum == IMAGE) { @@ -460,7 +498,7 @@ void Polyline::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr } else if (datum == CLICKED_POINT) { currPos += editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + currPos += editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } if (!i) { @@ -496,24 +534,24 @@ void Rectangle::setXYXY(int left, int top, int right, int bottom) bottomRight.set(right, bottom); } -void Rectangle::setXYWH(Coord topLeft, Coord widthHeight) +void Rectangle::setXYWH(rtengine::Coord topLeft, rtengine::Coord widthHeight) { this->topLeft = topLeft; this->bottomRight = topLeft + widthHeight; } -void Rectangle::setXYXY(Coord topLeft, Coord bottomRight) +void Rectangle::setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight) { this->topLeft = topLeft; this->bottomRight = bottomRight; } -void Rectangle::drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) +void Rectangle::drawOuterGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if ((flags & ACTIVE)) { + if ((flags & F_VISIBLE) && state != INSENSITIVE) { RGBColor color; - if (flags & AUTO_COLOR) { + if (flags & F_AUTO_COLOR) { color = getOuterLineColor(); } else { color = outerLineColor; @@ -522,14 +560,14 @@ void Rectangle::drawOuterGeometry (Cairo::RefPtr &cr, rtengine:: cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width( getOuterLineWidth() ); - Coord tl, br; + rtengine::Coord tl, br; if (datum == IMAGE) { coordSystem.imageCoordToScreen(topLeft.x, topLeft.y, tl.x, tl.y); } else if (datum == CLICKED_POINT) { tl = topLeft + editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - tl = topLeft + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + tl = topLeft + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } if (datum == IMAGE) { @@ -537,7 +575,7 @@ void Rectangle::drawOuterGeometry (Cairo::RefPtr &cr, rtengine:: } else if (datum == CLICKED_POINT) { br = bottomRight + editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - br = bottomRight + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + br = bottomRight + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); @@ -551,28 +589,31 @@ void Rectangle::drawOuterGeometry (Cairo::RefPtr &cr, rtengine:: } } -void Rectangle::drawInnerGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) +void Rectangle::drawInnerGeometry(Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if (flags & ACTIVE) { - RGBColor color; + if (flags & F_VISIBLE) { + if (state != INSENSITIVE) { + RGBColor color; - if (flags & AUTO_COLOR) { - color = getInnerLineColor(); - } else { - color = innerLineColor; + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); } - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); cr->set_line_width( innerLineWidth ); - Coord tl, br; + rtengine::Coord tl, br; if (datum == IMAGE) { coordSystem.imageCoordToScreen(topLeft.x, topLeft.y, tl.x, tl.y); } else if (datum == CLICKED_POINT) { tl = topLeft + editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - tl = topLeft + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + tl = topLeft + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } if (datum == IMAGE) { @@ -580,10 +621,10 @@ void Rectangle::drawInnerGeometry (Cairo::RefPtr &cr, rtengine:: } else if (datum == CLICKED_POINT) { br = bottomRight + editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - br = bottomRight + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + br = bottomRight + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } - if (filled) { + if (filled && state != INSENSITIVE) { cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); if (innerLineWidth > 0.) { @@ -594,24 +635,37 @@ void Rectangle::drawInnerGeometry (Cairo::RefPtr &cr, rtengine:: } } else if (innerLineWidth > 0.) { cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); - cr->stroke(); + + if (state == INSENSITIVE) { + std::valarray ds(1); + ds[0] = 4; + cr->set_source_rgba(1.0, 1.0, 1.0, 0.618); + cr->stroke_preserve(); + cr->set_source_rgba(0.0, 0.0, 0.0, 0.618); + cr->set_dash(ds, 0); + cr->stroke(); + ds.resize(0); + cr->set_dash(ds, 0); + } else { + cr->stroke(); + } } } } -void Rectangle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) +void Rectangle::drawToMOChannel(Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem) { - if (flags & ACTIVE) { + if (flags & F_HOVERABLE) { cr->set_line_width( getMouseOverLineWidth() ); - Coord tl, br; + rtengine::Coord tl, br; if (datum == IMAGE) { coordSystem.imageCoordToCropBuffer(topLeft.x, topLeft.y, tl.x, tl.y); } else if (datum == CLICKED_POINT) { tl = topLeft + editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - tl = topLeft + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + tl = topLeft + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } if (datum == IMAGE) { @@ -619,7 +673,7 @@ void Rectangle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPt } else if (datum == CLICKED_POINT) { br = bottomRight + editBuffer->getDataProvider()->posScreen; } else if (datum == CURSOR) { - br = bottomRight + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaPrevScreen; + br = bottomRight + editBuffer->getDataProvider()->posScreen + editBuffer->getDataProvider()->deltaScreen; } // drawing the lower byte's value @@ -658,7 +712,7 @@ void Rectangle::drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPt } } -EditSubscriber::EditSubscriber (EditType editType) : ID(EUID_None), editingType(editType), bufferType(BT_SINGLEPLANE_FLOAT), provider(NULL) {} +EditSubscriber::EditSubscriber (EditType editType) : ID(EUID_None), editingType(editType), bufferType(BT_SINGLEPLANE_FLOAT), provider(NULL), dragging(false) {} void EditSubscriber::setEditProvider(EditDataProvider *provider) { @@ -717,6 +771,10 @@ BufferType EditSubscriber::getEditBufferType() return bufferType; } +bool EditSubscriber::isDragging() +{ + return dragging; +} //-------------------------------------------------------------------------------------------------- diff --git a/rtgui/edit.h b/rtgui/edit.h index 134b373ea..c8b8090fb 100644 --- a/rtgui/edit.h +++ b/rtgui/edit.h @@ -24,7 +24,9 @@ #include "editid.h" #include "cursormanager.h" #include "../rtengine/rt_math.h" -#include "coord.h" +#include "../rtengine/coord.h" +#include "guiutils.h" +#include "options.h" class EditDataProvider; @@ -39,30 +41,117 @@ class EditBuffer; * 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 a _local_ editing, which is another topic implemented in another class. The Edit feature + * 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. * - * Each 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. + * Edit tool can be of 2 types: pipette editing and object editing. * - * ## How does it works? + * ## Pipette edition * - * When a tool is being constructed, unique IDs are affected to the EditSubscribers. 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 + * 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 of 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, the 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, object can be drawn and manipulated on the preview. + * + * The developer has to handle the buttonPress, buttonRelease, drag and mouseOver method 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 geometrical 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 use a circle line to represent the line to the user, but use another + * Circle object, filled, to be used as mouseOver detection. 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. + * + * + * ### 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, 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 it leads to a drag movement or not, + * by setting the "dragging" boolean to true. + * + * In this case, RT will then sent drag1 event (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 the "dragging" flag to false. + * + * 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, and eventually buffers to create and populate. + * 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 buffer can be freed up. + * When the mouse button 1 is pressed while pressing the CTRL key, the button1Pressed method will be called. + * + * 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 programmatically). * * 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 event. + * a new History entry, depending on the ProcEvent used. * */ - /** @brief Coordinate system where the widgets will be drawn * * The EditCoordSystem is used to define a screen and an image coordinate system. @@ -133,22 +222,69 @@ public: } }; +class RGBAColor : public RGBColor +{ + double a; + +public: + RGBAColor () : RGBColor(0., 0., 0.), a(0.) {} + explicit RGBAColor (double r, double g, double b, double a) : RGBColor(r, g, b), a(a) {} + explicit RGBAColor (char r, char g, char b, char a) : RGBColor(r, g, b), a(double(a) / 255.) {} + + void setColor(double r, double g, double b, double a) + { + RGBColor::setColor(r, g, b); + this->a = a; + } + + void setColor(char r, char g, char b, char a) + { + RGBColor::setColor(r, g, b); + this->a = double(a) / 255.; + } + + double getA() + { + return a; + } +}; + +/// @brief Displayable and MouseOver geometry base class class Geometry { public: + /// @brief Graphical state of the element enum State { - NORMAL, - PRELIGHT, - DRAGGED + 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, - CLICKED_POINT, - CURSOR + 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 { - ACTIVE = 1 << 0, // true if the geometry is active and have to be drawn - AUTO_COLOR = 1 << 1, // true if the color depend on the state value, not the color field above + 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: @@ -161,29 +297,29 @@ public: Datum datum; State state; // set by the Subscriber - Geometry () : innerLineColor(char(255), char(255), char(255)), outerLineColor(char(0), char(0), char(0)), flags(ACTIVE | AUTO_COLOR), innerLineWidth(1.f), datum(IMAGE), state(NORMAL) {} + 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) {} virtual ~Geometry() {} void setInnerLineColor (double r, double g, double b) { innerLineColor.setColor(r, g, b); - flags &= ~AUTO_COLOR; + flags &= ~F_AUTO_COLOR; } void setInnerLineColor (char r, char g, char b) { innerLineColor.setColor(r, g, b); - flags &= ~AUTO_COLOR; + flags &= ~F_AUTO_COLOR; } RGBColor getInnerLineColor (); void setOuterLineColor (double r, double g, double b) { outerLineColor.setColor(r, g, b); - flags &= ~AUTO_COLOR; + flags &= ~F_AUTO_COLOR; } void setOuterLineColor (char r, char g, char b) { outerLineColor.setColor(r, g, b); - flags &= ~AUTO_COLOR; + flags &= ~F_AUTO_COLOR; } RGBColor getOuterLineColor (); double getOuterLineWidth () @@ -197,17 +333,43 @@ public: void setAutoColor (bool aColor) { if (aColor) { - flags |= AUTO_COLOR; + flags |= F_AUTO_COLOR; } else { - flags &= ~AUTO_COLOR; + flags &= ~F_AUTO_COLOR; } } + bool isVisible () + { + return flags & F_VISIBLE; + } + void setVisible (bool visible) + { + if (visible) { + flags |= F_VISIBLE; + } else { + flags &= ~F_VISIBLE; + } + } + bool isHoverable () + { + return flags & F_HOVERABLE; + } + void setHoverable (bool visible) + { + if (visible) { + flags |= F_HOVERABLE; + } else { + flags &= ~F_HOVERABLE; + } + } + + // setActive will enable/disable the visible and hoverable flags in one shot! void setActive (bool active) { if (active) { - flags |= ACTIVE; + flags |= (F_VISIBLE | F_HOVERABLE); } else { - flags &= ~ACTIVE; + flags &= ~(F_VISIBLE | F_HOVERABLE); } } @@ -219,13 +381,13 @@ public: class Circle : public Geometry { public: - Coord center; + 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 () : center(100, 100), radius(10), filled(false), radiusInImageSpace(false) {} - Circle (Coord ¢er, int radius, bool filled = false, bool radiusInImageSpace = false) : center(center), radius(radius), filled(filled), radiusInImageSpace(radiusInImageSpace) {} + Circle (rtengine::Coord ¢er, int radius, bool filled = false, bool radiusInImageSpace = false) : center(center), radius(radius), filled(filled), radiusInImageSpace(radiusInImageSpace) {} Circle (int centerX, int centerY, int radius, bool filled = false, bool radiusInImageSpace = false) : center(centerX, centerY), radius(radius), filled(filled), radiusInImageSpace(radiusInImageSpace) {} void drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); @@ -236,11 +398,11 @@ public: class Line : public Geometry { public: - Coord begin; - Coord end; + rtengine::Coord begin; + rtengine::Coord end; Line () : begin(10, 10), end(100, 100) {} - Line (Coord &begin, Coord &end) : begin(begin), end(end) {} + Line (rtengine::Coord &begin, rtengine::Coord &end) : begin(begin), end(end) {} Line (int beginX, int beginY, int endX, int endY) : begin(beginX, beginY), end(endX, endY) {} void drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); @@ -251,7 +413,7 @@ public: class Polyline : public Geometry { public: - std::vector points; + std::vector points; bool filled; Polyline() : filled(false) {} @@ -264,16 +426,16 @@ public: class Rectangle : public Geometry { public: - Coord topLeft; - Coord bottomRight; + rtengine::Coord topLeft; + rtengine::Coord bottomRight; bool filled; Rectangle() : topLeft(0, 0), bottomRight(10, 10), filled(false) {} void setXYWH(int left, int top, int width, int height); void setXYXY(int left, int top, int right, int bottom); - void setXYWH(Coord topLeft, Coord widthHeight); - void setXYXY(Coord topLeft, Coord bottomRight); + void setXYWH(rtengine::Coord topLeft, rtengine::Coord widthHeight); + void setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight); void drawOuterGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); void drawInnerGeometry (Cairo::RefPtr &cr, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); void drawToMOChannel (Cairo::RefPtr &cr, Cairo::RefPtr &cr2, unsigned short id, rtengine::EditBuffer *editBuffer, EditCoordSystem &coordSystem); @@ -292,8 +454,9 @@ private: EditDataProvider *provider; protected: - std::vector visibleGeometry; - std::vector mouseOverGeometry; + std::vector visibleGeometry; /// displayed geometry + std::vector mouseOverGeometry; /// mouseOver geometry, drawn in a hidden buffer + bool dragging; /// in object mode, set this to true in buttonPressed events to start dragging and ask for drag event (ignored in pipette mode) public: EditSubscriber (EditType editType); @@ -312,6 +475,7 @@ public: EditUniqueID getEditID(); EditType getEditingType(); BufferType getEditBufferType(); + bool isDragging(); /// Returns true if something is being dragged and drag events has to be sent (object mode only) /** @brief Get the cursor to be displayed when above handles @param objectID object currently "hovered" */ @@ -331,6 +495,7 @@ public: } /** @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(int modifierKey) @@ -345,10 +510,58 @@ public: return false; } + /** @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(int modifierKey) + { + return false; + } + + /** @brief Triggered when mouse button 2 is released (middle button) + @return true if the preview has to be redrawn, false otherwise */ + virtual bool button2Released() + { + return false; + } + + /** @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(int modifierKey) + { + return false; + } + + /** @brief Triggered when mouse button 3 is released (right button) + @return true if the preview has to be redrawn, false otherwise */ + virtual bool button3Released() + { + return false; + } + /** @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 drag(int modifierKey) + virtual bool drag1(int modifierKey) + { + return false; + } + + /** @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(int modifierKey) + { + return false; + } + + /** @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(int modifierKey) { return false; } @@ -381,12 +594,12 @@ 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 - Coord posScreen; /// Location of the mouse button press, in preview image space - Coord posImage; /// Location of the mouse button press, in the full image space - Coord deltaScreen; /// Delta relative to posScreen - Coord deltaImage; /// Delta relative to posImage - Coord deltaPrevScreen; /// Delta relative to the previous mouse location, in preview image space - Coord deltaPrevImage; /// Delta relative to the previous mouse location, in the full image space + 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() {} diff --git a/rtgui/editenums.h b/rtgui/editenums.h index 32b114643..e51918426 100644 --- a/rtgui/editenums.h +++ b/rtgui/editenums.h @@ -20,7 +20,7 @@ #define _EDITENUMS_ enum ImgEditState {SNormal, SCropMove, SHandMove, SResizeW1, SResizeW2, SResizeH1, SResizeH2, SResizeTL, SResizeTR, SResizeBL, SResizeBR, - SCropSelecting, SRotateSelecting, SCropWinMove, SCropFrameMove, SCropImgMove, SCropWinResize, SObservedMove, SEditDrag + SCropSelecting, SRotateSelecting, SCropWinMove, SCropFrameMove, SCropImgMove, SCropWinResize, SObservedMove, SEditDrag1, SEditDrag2, SEditDrag3 }; enum CursorArea {CropWinButtons, CropToolBar, CropImage, CropBorder, CropTop, CropTopLeft, CropTopRight, CropBottom, CropBottomLeft, CropBottomRight, CropLeft, CropRight, CropInside, CropResize, CropObserved diff --git a/rtgui/gradient.cc b/rtgui/gradient.cc index 6d61dd840..49cc3ee59 100644 --- a/rtgui/gradient.cc +++ b/rtgui/gradient.cc @@ -146,7 +146,7 @@ void Gradient::updateGeometry(int centerX_, int centerY_, double feather_, doubl dataProvider->getImageSize(imW, imH); double decay = feather_ * sqrt(double(imW) * double(imW) + double(imH) * double(imH)) / 200.; - Coord origin(imW / 2 + centerX_ * imW / 200.f, imH / 2 + centerY_ * imH / 200.f); + rtengine::Coord origin(imW / 2 + centerX_ * imW / 200.f, imH / 2 + centerY_ * imH / 200.f); Line *currLine; Circle *currCircle; @@ -397,10 +397,11 @@ bool Gradient::mouseOver(int modifierKey) bool Gradient::button1Pressed(int modifierKey) { - if (!(modifierKey & (GDK_CONTROL_MASK | GDK_CONTROL_MASK))) { + EditDataProvider *provider = getEditProvider(); + + if (!(modifierKey & GDK_CONTROL_MASK)) { // button press is valid (no modifier key) PolarCoord pCoord; - EditDataProvider *provider = getEditProvider(); int imW, imH; provider->getImageSize(imW, imH); double halfSizeW = imW / 2.; @@ -408,8 +409,8 @@ bool Gradient::button1Pressed(int modifierKey) draggedCenter.set(int(halfSizeW + halfSizeW * (centerX->getValue() / 100.)), int(halfSizeH + halfSizeH * (centerY->getValue() / 100.))); // trick to get the correct angle (clockwise/counter-clockwise) - Coord p1 = draggedCenter; - Coord p2 = provider->posImage; + rtengine::Coord p1 = draggedCenter; + rtengine::Coord p2 = provider->posImage; int p = p1.y; p1.y = p2.y; p2.y = p; @@ -422,9 +423,9 @@ bool Gradient::button1Pressed(int modifierKey) if (lastObject == 2 || lastObject == 3) { // Dragging a line to change the angle PolarCoord draggedPoint; - Coord currPos; + rtengine::Coord currPos; currPos = provider->posImage; - Coord centerPos = draggedCenter; + rtengine::Coord centerPos = draggedCenter; double diagonal = sqrt(double(imW) * double(imW) + double(imH) * double(imH)); @@ -444,6 +445,7 @@ bool Gradient::button1Pressed(int modifierKey) draggedFeatherOffset -= (feather->getValue() / 200. * diagonal); } + EditSubscriber::dragging = true; return false; } else { // this will let this class ignore further drag events @@ -464,10 +466,11 @@ bool Gradient::button1Pressed(int modifierKey) bool Gradient::button1Released() { draggedPointOldAngle = -1000.; + EditSubscriber::dragging = false; return true; } -bool Gradient::drag(int modifierKey) +bool Gradient::drag1(int modifierKey) { // compute the polar coordinate of the mouse position EditDataProvider *provider = getEditProvider(); @@ -480,9 +483,9 @@ bool Gradient::drag(int modifierKey) // Dragging a line to change the angle PolarCoord draggedPoint; - Coord currPos; + rtengine::Coord currPos; currPos = provider->posImage + provider->deltaImage; - Coord centerPos = draggedCenter; + rtengine::Coord centerPos = draggedCenter; // trick to get the correct angle (clockwise/counter-clockwise) int p = centerPos.y; @@ -523,9 +526,9 @@ bool Gradient::drag(int modifierKey) } else if (lastObject == 2 || lastObject == 3) { // Dragging the upper or lower feather bar PolarCoord draggedPoint; - Coord currPos; + rtengine::Coord currPos; currPos = provider->posImage + provider->deltaImage; - Coord centerPos = draggedCenter; + rtengine::Coord centerPos = draggedCenter; double diagonal = sqrt(double(imW) * double(imW) + double(imH) * double(imH)); @@ -561,7 +564,7 @@ bool Gradient::drag(int modifierKey) } } else if (lastObject == 4) { // Dragging the circle to change the center - Coord currPos; + rtengine::Coord currPos; draggedCenter += provider->deltaPrevImage; currPos = draggedCenter; currPos.clip(imW, imH); diff --git a/rtgui/gradient.h b/rtgui/gradient.h index e30dd7728..4117c3859 100644 --- a/rtgui/gradient.h +++ b/rtgui/gradient.h @@ -8,6 +8,7 @@ #include "adjuster.h" #include "toolpanel.h" #include "edit.h" +#include "guiutils.h" class Gradient : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public EditSubscriber { @@ -26,7 +27,7 @@ protected: double draggedPointOldAngle; double draggedPointAdjusterAngle; double draggedFeatherOffset; - Coord draggedCenter; + rtengine::Coord draggedCenter; sigc::connection editConn; void editToggled (); @@ -55,7 +56,7 @@ public: bool mouseOver(int modifierKey); bool button1Pressed(int modifierKey); bool button1Released(); - bool drag(int modifierKey); + bool drag1(int modifierKey); void switchOffEditMode (); }; diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc index 53f32dbbc..291339414 100644 --- a/rtgui/inspector.cc +++ b/rtgui/inspector.cc @@ -110,10 +110,10 @@ bool Inspector::on_expose_event (GdkEventExpose* event) // this will eventually create/update the off-screen pixmap // compute the displayed area - Coord availableSize; - Coord topLeft; - Coord displayedSize; - Coord dest(0, 0); + rtengine::Coord availableSize; + rtengine::Coord topLeft; + rtengine::Coord displayedSize; + rtengine::Coord dest(0, 0); win->get_size(availableSize.x, availableSize.y); int imW = currImage->imgBuffer.getWidth(); int imH = currImage->imgBuffer.getHeight(); diff --git a/rtgui/inspector.h b/rtgui/inspector.h index 192e86d40..ce2da9aeb 100644 --- a/rtgui/inspector.h +++ b/rtgui/inspector.h @@ -21,7 +21,7 @@ #include #include "guiutils.h" -#include "coord.h" +#include "../rtengine/coord.h" class InspectorBuffer { @@ -42,7 +42,7 @@ class Inspector : public Gtk::DrawingArea { private: - Coord center; + rtengine::Coord center; std::vector images; InspectorBuffer* currImage; double zoom;