From 108b69ae226fcb9fa43398ba148bb36e2bf7f7d9 Mon Sep 17 00:00:00 2001 From: Hombre Date: Fri, 22 Mar 2019 00:09:37 +0100 Subject: [PATCH] "edit.h/.cc" splitted to limit rebuilding dependencies --- rtengine/dcrop.cc | 1 + rtengine/improcfun.cc | 1 + rtengine/pipettebuffer.cc | 2 + rtengine/pipettebuffer.h | 4 +- rtgui/CMakeLists.txt | 6 +- rtgui/bayerpreprocess.cc | 1 + rtgui/bayerrawexposure.cc | 1 + rtgui/blackwhite.cc | 1 - rtgui/blackwhite.h | 2 + rtgui/colortoning.cc | 20 +- rtgui/colortoning.h | 3 +- rtgui/crophandler.h | 4 +- rtgui/cropwindow.cc | 3 + rtgui/cropwindow.h | 3 +- rtgui/curveeditor.h | 2 +- rtgui/dirpyrdenoise.cc | 6 +- rtgui/dirpyrdenoise.h | 2 + rtgui/editbuffer.cc | 129 ++ rtgui/editbuffer.h | 83 ++ rtgui/editcallbacks.cc | 134 ++ rtgui/editcallbacks.h | 256 ++++ rtgui/editcoordsys.h | 61 + rtgui/{edit.cc => editwidgets.cc} | 2088 +++++++++++++---------------- rtgui/{edit.h => editwidgets.h} | 1405 ++++++++----------- rtgui/exifpanel.cc | 1 + rtgui/gradient.cc | 1 + rtgui/gradient.h | 2 +- rtgui/imagearea.h | 2 +- rtgui/labcurve.cc | 3 +- rtgui/labcurve.h | 2 + rtgui/labgrid.cc | 2 + rtgui/lensprofile.cc | 1 + rtgui/lockablecolorpicker.h | 1 - rtgui/mydiagonalcurve.cc | 1 + rtgui/myflatcurve.cc | 1 + rtgui/preprocess.cc | 1 + rtgui/rawcacorrection.cc | 1 + rtgui/rawexposure.cc | 1 + rtgui/sharpenedge.cc | 1 + rtgui/tonecurve.cc | 2 +- rtgui/tonecurve.h | 2 + rtgui/toolpanel.h | 2 +- rtgui/wavelet.cc | 3 +- rtgui/wavelet.h | 2 + rtgui/xtransrawexposure.cc | 1 + 45 files changed, 2206 insertions(+), 2045 deletions(-) create mode 100644 rtgui/editbuffer.cc create mode 100644 rtgui/editbuffer.h create mode 100644 rtgui/editcallbacks.cc create mode 100644 rtgui/editcallbacks.h create mode 100644 rtgui/editcoordsys.h rename rtgui/{edit.cc => editwidgets.cc} (85%) rename rtgui/{edit.h => editwidgets.h} (61%) diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 0872049d8..a17f6de4b 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -23,6 +23,7 @@ #include "procparams.h" #include "refreshmap.h" #include "rt_math.h" +#include "../rtgui/editcallbacks.h" namespace { diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 4e2523137..c0158e712 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -44,6 +44,7 @@ #include "procparams.h" #include "../rtgui/ppversion.h" #include "../rtgui/guiutils.h" +#include "../rtgui/editcallbacks.h" #undef CLIPD #define CLIPD(a) ((a)>0.0f?((a)<1.0f?(a):1.0f):0.0f) diff --git a/rtengine/pipettebuffer.cc b/rtengine/pipettebuffer.cc index 8981b6480..ac47deed9 100644 --- a/rtengine/pipettebuffer.cc +++ b/rtengine/pipettebuffer.cc @@ -18,6 +18,8 @@ */ #include "pipettebuffer.h" +#include "../rtgui/editcallbacks.h" +#include "imagefloat.h" namespace rtengine { diff --git a/rtengine/pipettebuffer.h b/rtengine/pipettebuffer.h index 139438e1d..21762e026 100644 --- a/rtengine/pipettebuffer.h +++ b/rtengine/pipettebuffer.h @@ -19,7 +19,7 @@ #ifndef _PIPETTEBUFFER_H_ #define _PIPETTEBUFFER_H_ -#include "../rtgui/edit.h" +#include "../rtgui/editbuffer.h" #include "array2D.h" #include "iimage.h" #include "coord.h" @@ -27,6 +27,8 @@ namespace rtengine { +class Imagefloat; + /// @brief Structure that contains information about and pointers to the Edit buffer class PipetteBuffer { diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 238ad8344..831f100e7 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -1,7 +1,7 @@ # Common source files for both CLI and non-CLI execautables set(CLISOURCEFILES alignedmalloc.cc - edit.cc + editcallbacks.cc main-cli.cc multilangmgr.cc options.cc @@ -49,8 +49,10 @@ set(NONCLISOURCEFILES dirpyrequalizer.cc distortion.cc dynamicprofilepanel.cc - edit.cc + editbuffer.cc + editcallbacks.cc editorpanel.cc + editwidgets.cc editwindow.cc epd.cc exiffiltersettings.cc diff --git a/rtgui/bayerpreprocess.cc b/rtgui/bayerpreprocess.cc index 89fd6fcb6..4910b2798 100644 --- a/rtgui/bayerpreprocess.cc +++ b/rtgui/bayerpreprocess.cc @@ -21,6 +21,7 @@ #include "bayerpreprocess.h" #include "eventmapper.h" #include "guiutils.h" +#include "options.h" #include "../rtengine/procparams.h" diff --git a/rtgui/bayerrawexposure.cc b/rtgui/bayerrawexposure.cc index d49486511..86cd9794b 100644 --- a/rtgui/bayerrawexposure.cc +++ b/rtgui/bayerrawexposure.cc @@ -19,6 +19,7 @@ #include "bayerrawexposure.h" #include "guiutils.h" +#include "options.h" #include "../rtengine/procparams.h" diff --git a/rtgui/blackwhite.cc b/rtgui/blackwhite.cc index 48d37ebab..3d5deb8f2 100644 --- a/rtgui/blackwhite.cc +++ b/rtgui/blackwhite.cc @@ -21,7 +21,6 @@ #include "blackwhite.h" -#include "edit.h" #include "guiutils.h" #include "rtimage.h" diff --git a/rtgui/blackwhite.h b/rtgui/blackwhite.h index 45f1e5b7b..dd45a7729 100644 --- a/rtgui/blackwhite.h +++ b/rtgui/blackwhite.h @@ -28,6 +28,8 @@ #include "mycurve.h" #include "colorprovider.h" +class EditDataProvider; + class BlackWhite final : public ToolParamBlock, public AdjusterListener, diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc index 025acb55f..7ca9a7305 100644 --- a/rtgui/colortoning.cc +++ b/rtgui/colortoning.cc @@ -567,7 +567,7 @@ void ColorToning::read (const ProcParams* pp, const ParamsEdited* pedited) labRegionData = pp->colorToning.labregions; if (labRegionData.empty()) { - labRegionData.emplace_back(rtengine::ColorToningParams::LabCorrectionRegion()); + labRegionData.emplace_back(rtengine::procparams::ColorToningParams::LabCorrectionRegion()); } if (pp->colorToning.labregionsShowMask >= 0) { labRegionSelected = pp->colorToning.labregionsShowMask; @@ -1441,7 +1441,7 @@ void ColorToning::labRegionGet(int idx) void ColorToning::labRegionAddPressed() { labRegionSelected = labRegionData.size(); - labRegionData.push_back(rtengine::ColorToningParams::LabCorrectionRegion()); + labRegionData.push_back(rtengine::procparams::ColorToningParams::LabCorrectionRegion()); labRegionPopulateList(); labRegionShow(labRegionSelected); @@ -1525,7 +1525,7 @@ void ColorToning::labRegionPopulateList() { ConnectionBlocker b(labRegionSelectionConn); labRegionList->clear_items(); - rtengine::ColorToningParams::LabCorrectionRegion dflt; + rtengine::procparams::ColorToningParams::LabCorrectionRegion dflt; for (size_t i = 0; i < labRegionData.size(); ++i) { auto &r = labRegionData[i]; @@ -1533,11 +1533,11 @@ void ColorToning::labRegionPopulateList() labRegionList->set_text(j, 1, Glib::ustring::compose("a=%1 b=%2 S=%3\ns=%4 o=%5 p=%6", round_ab(r.a), round_ab(r.b), r.saturation, r.slope, r.offset, r.power)); const char *ch = ""; switch (r.channel) { - case rtengine::ColorToningParams::LabCorrectionRegion::CHAN_R: + case rtengine::procparams::ColorToningParams::LabCorrectionRegion::CHAN_R: ch = "\n[Red]"; break; - case rtengine::ColorToningParams::LabCorrectionRegion::CHAN_G: + case rtengine::procparams::ColorToningParams::LabCorrectionRegion::CHAN_G: ch = "\n[Green]"; break; - case rtengine::ColorToningParams::LabCorrectionRegion::CHAN_B: + case rtengine::procparams::ColorToningParams::LabCorrectionRegion::CHAN_B: ch = "\n[Blue]"; break; default: ch = ""; @@ -1560,7 +1560,7 @@ void ColorToning::labRegionShow(int idx, bool list_only) if (disable) { disableListener(); } - rtengine::ColorToningParams::LabCorrectionRegion dflt; + rtengine::procparams::ColorToningParams::LabCorrectionRegion dflt; auto &r = labRegionData[idx]; if (!list_only) { labRegionAB->setParams(0, 0, r.a, r.b, false); @@ -1577,11 +1577,11 @@ void ColorToning::labRegionShow(int idx, bool list_only) labRegionList->set_text(idx, 1, Glib::ustring::compose("a=%1 b=%2 S=%3\ns=%4 o=%5 p=%6", round_ab(r.a), round_ab(r.b), r.saturation, r.slope, r.offset, r.power)); const char *ch = ""; switch (r.channel) { - case rtengine::ColorToningParams::LabCorrectionRegion::CHAN_R: + case rtengine::procparams::ColorToningParams::LabCorrectionRegion::CHAN_R: ch = "\n[Red]"; break; - case rtengine::ColorToningParams::LabCorrectionRegion::CHAN_G: + case rtengine::procparams::ColorToningParams::LabCorrectionRegion::CHAN_G: ch = "\n[Green]"; break; - case rtengine::ColorToningParams::LabCorrectionRegion::CHAN_B: + case rtengine::procparams::ColorToningParams::LabCorrectionRegion::CHAN_B: ch = "\n[Blue]"; break; default: ch = ""; diff --git a/rtgui/colortoning.h b/rtgui/colortoning.h index 29a71b03b..abb44d09c 100644 --- a/rtgui/colortoning.h +++ b/rtgui/colortoning.h @@ -13,6 +13,7 @@ #include "thresholdadjuster.h" #include "colorprovider.h" #include "labgrid.h" +#include "../rtengine/procparams.h" class ColorToning final : public ToolParamBlock, @@ -160,7 +161,7 @@ private: FlatCurveEditor *labRegionLightnessMask; Adjuster *labRegionMaskBlur; Gtk::CheckButton *labRegionShowMask; - std::vector labRegionData; + std::vector labRegionData; int labRegionSelected; sigc::connection labRegionSelectionConn; diff --git a/rtgui/crophandler.h b/rtgui/crophandler.h index e0b48d348..e90b96c25 100644 --- a/rtgui/crophandler.h +++ b/rtgui/crophandler.h @@ -26,11 +26,13 @@ #include #include "../rtengine/rtengine.h" +#include "editbuffer.h" -#include "edit.h" #include "lockablecolorpicker.h" #include "threadutils.h" +class EditSubscriber; + class CropDisplayHandler { diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc index a8ead7558..11e247ffb 100644 --- a/rtgui/cropwindow.cc +++ b/rtgui/cropwindow.cc @@ -27,6 +27,9 @@ #include "options.h" #include "rtimage.h" #include "threadutils.h" +#include "editcallbacks.h" +#include "editbuffer.h" +#include "editwidgets.h" #include "../rtengine/dcrop.h" #include "../rtengine/mytime.h" diff --git a/rtgui/cropwindow.h b/rtgui/cropwindow.h index 395b1b621..287cbf1ef 100644 --- a/rtgui/cropwindow.h +++ b/rtgui/cropwindow.h @@ -29,7 +29,8 @@ #include "cropguilistener.h" #include "pointermotionlistener.h" #include "cursormanager.h" -#include "edit.h" +#include "editbuffer.h" +#include "editcoordsys.h" class CropWindow; diff --git a/rtgui/curveeditor.h b/rtgui/curveeditor.h index 4cf49a377..1faa96f15 100644 --- a/rtgui/curveeditor.h +++ b/rtgui/curveeditor.h @@ -22,7 +22,7 @@ #include "popuptogglebutton.h" #include "../rtengine/LUT.h" #include "coloredbar.h" -#include "edit.h" +#include "editcallbacks.h" #include "mydiagonalcurve.h" #include "myflatcurve.h" diff --git a/rtgui/dirpyrdenoise.cc b/rtgui/dirpyrdenoise.cc index 7be07420e..c60c06243 100644 --- a/rtgui/dirpyrdenoise.cc +++ b/rtgui/dirpyrdenoise.cc @@ -21,10 +21,10 @@ #include "dirpyrdenoise.h" -#include "edit.h" #include "guiutils.h" #include "../rtengine/procparams.h" +#include "editbuffer.h" using namespace rtengine; using namespace rtengine::procparams; @@ -63,7 +63,7 @@ DirPyrDenoise::DirPyrDenoise () : FoldableToolPanel(this, "dirpyrdenoise", M("TP NoiscurveEditorG = new CurveEditorGroup (options.lastDenoiseCurvesDir, M("TP_DIRPYRDENOISE_LUMINANCE_CURVE")); //curveEditorG = new CurveEditorGroup (options.lastLabCurvesDir); NoiscurveEditorG->setCurveListener (this); - defaultCurve = rtengine::DirPyrDenoiseParams().lcurve; + defaultCurve = rtengine::procparams::DirPyrDenoiseParams().lcurve; lshape = static_cast(NoiscurveEditorG->addCurve(CT_Flat, "", nullptr, false, false)); lshape->setIdentityValue(0.); lshape->setResetCurve(FlatCurveType(defaultCurve.at(0)), defaultCurve); @@ -139,7 +139,7 @@ DirPyrDenoise::DirPyrDenoise () : FoldableToolPanel(this, "dirpyrdenoise", M("TP CCcurveEditorG = new CurveEditorGroup (options.lastDenoiseCurvesDir, M("TP_DIRPYRDENOISE_CHROMINANCE_CURVE")); CCcurveEditorG->setCurveListener (this); - defaultCurve = rtengine::DirPyrDenoiseParams().cccurve; + defaultCurve = rtengine::procparams::DirPyrDenoiseParams().cccurve; ccshape = static_cast(CCcurveEditorG->addCurve(CT_Flat, "", nullptr, false, false)); ccshape->setIdentityValue(0.); ccshape->setResetCurve(FlatCurveType(defaultCurve.at(0)), defaultCurve); diff --git a/rtgui/dirpyrdenoise.h b/rtgui/dirpyrdenoise.h index 2ee1863ab..de7dee800 100644 --- a/rtgui/dirpyrdenoise.h +++ b/rtgui/dirpyrdenoise.h @@ -28,6 +28,8 @@ #include "guiutils.h" #include "options.h" +class EditDataProvider; + class DirPyrDenoise final : public ToolParamBlock, public AdjusterListener, diff --git a/rtgui/editbuffer.cc b/rtgui/editbuffer.cc new file mode 100644 index 000000000..882f3c084 --- /dev/null +++ b/rtgui/editbuffer.cc @@ -0,0 +1,129 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Jean-Christophe FRISCH + * + * 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 "editbuffer.h" +#include "editcallbacks.h" + +ObjectMOBuffer::ObjectMOBuffer(EditDataProvider *dataProvider) : objectMap(nullptr), objectMode(OM_255), dataProvider(dataProvider) {} + +ObjectMOBuffer::~ObjectMOBuffer() +{ + flush(); +} + + +/* Upgrade or downgrade the objectModeType */ +void ObjectMOBuffer::setObjectMode(ObjectMode newType) +{ + if (!objectMap) { + objectMode = newType; + return; + } + + int w = objectMap->get_width (); + int h = objectMap->get_height (); + if (w && h) { + switch (newType) { + case (OM_255): + if (objectMode==OM_65535) { + objectMap->unreference(); + objectMap = Cairo::ImageSurface::create(Cairo::FORMAT_A8, w, h); + } + break; + + case (OM_65535): + if (objectMode==OM_255) { + objectMap->unreference(); + objectMap = Cairo::ImageSurface::create(Cairo::FORMAT_RGB16_565, w, h); + } + break; + } + } + objectMode = newType; +} + +void ObjectMOBuffer::flush() +{ + if (objectMap ) { + objectMap.clear(); + } +} + +EditSubscriber *ObjectMOBuffer::getEditSubscriber () { + if (dataProvider) { + return dataProvider->getCurrSubscriber(); + } else { + return nullptr; + } +} + + +// Resize buffers if they already exist +void ObjectMOBuffer::resize(int newWidth, int newHeight) +{ + if (!dataProvider) { + return; + } + + if (const auto currSubscriber = dataProvider->getCurrSubscriber ()) { + if (currSubscriber->getEditingType() == ET_OBJECTS) { + if (objectMap && (objectMap->get_width() != newWidth || objectMap->get_height() != newHeight)) { + objectMap.clear(); + } + + if (!objectMap && newWidth>0 && newHeight>0) { + objectMap = Cairo::ImageSurface::create(objectMode==OM_255?Cairo::FORMAT_A8:Cairo::FORMAT_RGB16_565, newWidth, newHeight); + } + + } else { + flush(); + } + } else { + flush(); + } +} + +int ObjectMOBuffer::getObjectID(const rtengine::Coord& location) +{ + int id = 0; + + if (!objectMap || location.x < 0 || location.y < 0 || location.x >= objectMap->get_width() || location.y >= objectMap->get_height()) { + return -1; + } + + if (objectMode == OM_255) { + id = (unsigned char)(*( objectMap->get_data() + location.y * objectMap->get_stride() + location.x )); + } else { + id = (unsigned short)(*( objectMap->get_data() + location.y * objectMap->get_stride() + location.x )); + } + + return id - 1; +} + +bool ObjectMOBuffer::bufferCreated() +{ + EditSubscriber* subscriber; + + if (dataProvider && (subscriber = dataProvider->getCurrSubscriber())) { + return subscriber->getEditingType() == ET_OBJECTS ? bool(objectMap) : false; + } + + return false; +} + diff --git a/rtgui/editbuffer.h b/rtgui/editbuffer.h new file mode 100644 index 000000000..ffb80c48d --- /dev/null +++ b/rtgui/editbuffer.h @@ -0,0 +1,83 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Jean-Christophe FRISCH + * + * 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 . + */ +#pragma once + +#include "editid.h" +#include "../rtengine/coord.h" +#include + +#ifdef GUIVERSION +#include "rtsurface.h" +#endif + +class EditDataProvider; +class EditSubscriber; + +/** @file + * See editwidgets.h for documentation + */ + +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 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& getObjectMap (); + + // return true if the buffer has been allocated + bool bufferCreated(); + + int getObjectID(const rtengine::Coord& location); +}; + +inline EditDataProvider* ObjectMOBuffer::getDataProvider () { + return dataProvider; +} + +inline ObjectMode ObjectMOBuffer::getObjectMode () { + return objectMode; +} + +inline Cairo::RefPtr& ObjectMOBuffer::getObjectMap () { + return objectMap; +} + + diff --git a/rtgui/editcallbacks.cc b/rtgui/editcallbacks.cc new file mode 100644 index 000000000..3b43e4658 --- /dev/null +++ b/rtgui/editcallbacks.cc @@ -0,0 +1,134 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Jean-Christophe FRISCH + * + * 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 "editcallbacks.h" + +EditSubscriber::EditSubscriber (EditType editType) : ID(EUID_None), editingType(editType), bufferType(BT_SINGLEPLANE_FLOAT), provider(nullptr), action(ES_ACTION_NONE) {} + +void EditSubscriber::setEditProvider(EditDataProvider *provider) +{ + this->provider = provider; +} + +void EditSubscriber::setEditID(EditUniqueID ID, BufferType buffType) +{ + this->ID = ID; + bufferType = buffType; +} + +bool EditSubscriber::isCurrentSubscriber() +{ + //if (provider && provider->getCurrSubscriber()) + // return provider->getCurrSubscriber()->getEditID() == ID; + + if (provider) { + return provider->getCurrSubscriber() == this; + } + + return false; +} + +void EditSubscriber::subscribe() +{ + if (provider) { + provider->subscribe(this); + } +} + +void EditSubscriber::unsubscribe() +{ + if (provider) { + provider->unsubscribe(); + } +} + +void EditSubscriber::switchOffEditMode() +{ + unsubscribe(); +} + +EditUniqueID EditSubscriber::getEditID() +{ + return ID; +} + +EditType EditSubscriber::getEditingType() +{ + return editingType; +} + +BufferType EditSubscriber::getPipetteBufferType() +{ + return bufferType; +} + +bool EditSubscriber::isDragging() +{ + return action == ES_ACTION_DRAGGING; +} + +bool EditSubscriber::isPicking() +{ + return action == ES_ACTION_PICKING; +} + +//-------------------------------------------------------------------------------------------------- + + +EditDataProvider::EditDataProvider() : currSubscriber(nullptr), object(0), posScreen(-1, -1), posImage(-1, -1), + deltaScreen(0, 0), deltaImage(0, 0), deltaPrevScreen(0, 0), deltaPrevImage(0, 0) +{ + pipetteVal[0] = pipetteVal[1] = pipetteVal[2] = 0.f; +} + +void EditDataProvider::subscribe(EditSubscriber *subscriber) +{ + if (currSubscriber) { + currSubscriber->switchOffEditMode(); + } + + currSubscriber = subscriber; +} + +void EditDataProvider::unsubscribe() +{ + currSubscriber = nullptr; +} + +void EditDataProvider::switchOffEditMode() +{ + if (currSubscriber) { + currSubscriber->switchOffEditMode (); + } +} + +CursorShape EditDataProvider::getCursor(int objectID) +{ + if (currSubscriber) { + currSubscriber->getCursor(objectID); + } + + return CSHandOpen; +} + +EditSubscriber* EditDataProvider::getCurrSubscriber() +{ + return currSubscriber; +} + diff --git a/rtgui/editcallbacks.h b/rtgui/editcallbacks.h new file mode 100644 index 000000000..1c1242fe0 --- /dev/null +++ b/rtgui/editcallbacks.h @@ -0,0 +1,256 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Jean-Christophe FRISCH + * + * 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 . + */ +#pragma once + +#include "editid.h" +#include "cursormanager.h" +#include "../rtengine/coord.h" + +class Geometry; +class EditDataProvider; + +/** @file + * See editwidgets.h for documentation + */ + +/// @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 visibleGeometry; /// displayed geometry + std::vector 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& getVisibleGeometry (); + + /** @brief Get the geometry to be drawn in the "mouse over" channel, hidden from the user */ + const std::vector& 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* EditSubscriber::getEditProvider () { + return provider; +} + +inline CursorShape EditSubscriber::getCursor (const int objectID) { + return CSHandOpen; +} + +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& EditSubscriber::getVisibleGeometry () { + return visibleGeometry; +} + +inline const std::vector& EditSubscriber::getMouseOverGeometry () { + return mouseOverGeometry; +} + +inline int EditDataProvider::getPipetteRectSize () { + return 8; // TODO: make a GUI +} + diff --git a/rtgui/editcoordsys.h b/rtgui/editcoordsys.h new file mode 100644 index 000000000..1af0f5e37 --- /dev/null +++ b/rtgui/editcoordsys.h @@ -0,0 +1,61 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Jean-Christophe FRISCH + * + * 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 . + */ + +/** @file + * See editwidgets.h for documentation + */ + + /** @brief Coordinate system where the widgets will be drawn + * + * The EditCoordSystem is used to define a screen and an image coordinate system. + */ +#pragma once + + + 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; + }; + diff --git a/rtgui/edit.cc b/rtgui/editwidgets.cc similarity index 85% rename from rtgui/edit.cc rename to rtgui/editwidgets.cc index c59c2dc04..667b8cb37 100644 --- a/rtgui/edit.cc +++ b/rtgui/editwidgets.cc @@ -1,1153 +1,935 @@ -/* - * 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 "edit.h" - -ObjectMOBuffer::ObjectMOBuffer(EditDataProvider *dataProvider) : objectMap(nullptr), objectMode(OM_255), dataProvider(dataProvider) {} - -ObjectMOBuffer::~ObjectMOBuffer() -{ - flush(); -} - - -/* Upgrade or downgrade the objectModeType */ -void ObjectMOBuffer::setObjectMode(ObjectMode newType) -{ - if (!objectMap) { - objectMode = newType; - return; - } - - int w = objectMap->get_width (); - int h = objectMap->get_height (); - if (w && h) { - switch (newType) { - case (OM_255): - if (objectMode==OM_65535) { - objectMap->unreference(); - objectMap = Cairo::ImageSurface::create(Cairo::FORMAT_A8, w, h); - } - break; - - case (OM_65535): - if (objectMode==OM_255) { - objectMap->unreference(); - objectMap = Cairo::ImageSurface::create(Cairo::FORMAT_RGB16_565, w, h); - } - break; - } - } - objectMode = newType; -} - -void ObjectMOBuffer::flush() -{ - if (objectMap ) { - objectMap.clear(); - } -} - -EditSubscriber *ObjectMOBuffer::getEditSubscriber () { - if (dataProvider) { - return dataProvider->getCurrSubscriber(); - } else { - return nullptr; - } -} - - -// Resize buffers if they already exist -void ObjectMOBuffer::resize(int newWidth, int newHeight) -{ - if (!dataProvider) { - return; - } - - if (const auto currSubscriber = dataProvider->getCurrSubscriber ()) { - if (currSubscriber->getEditingType() == ET_OBJECTS) { - if (objectMap && (objectMap->get_width() != newWidth || objectMap->get_height() != newHeight)) { - objectMap.clear(); - } - - if (!objectMap && newWidth>0 && newHeight>0) { - objectMap = Cairo::ImageSurface::create(objectMode==OM_255?Cairo::FORMAT_A8:Cairo::FORMAT_RGB16_565, newWidth, newHeight); - } - - } else { - flush(); - } - } else { - flush(); - } -} - -int ObjectMOBuffer::getObjectID(const rtengine::Coord& location) -{ - int id = 0; - - if (!objectMap || location.x < 0 || location.y < 0 || location.x >= objectMap->get_width() || location.y >= objectMap->get_height()) { - return -1; - } - - if (objectMode == OM_255) { - id = (unsigned char)(*( objectMap->get_data() + location.y * objectMap->get_stride() + location.x )); - } else { - id = (unsigned short)(*( objectMap->get_data() + location.y * objectMap->get_stride() + location.x )); - } - - return id - 1; -} - -bool ObjectMOBuffer::bufferCreated() -{ - EditSubscriber* subscriber; - - if (dataProvider && (subscriber = dataProvider->getCurrSubscriber())) { - return subscriber->getEditingType() == ET_OBJECTS ? bool(objectMap) : false; - } - - return false; -} - -RGBColor Geometry::getInnerLineColor () -{ - RGBColor color; - - if (flags & F_AUTO_COLOR) { - if (state == NORMAL) { - color.setColor (1., 1., 1.); // White - } else if (state == ACTIVE) { - color.setColor (1., 1., 0.); // Yellow - } else if (state == PRELIGHT) { - color.setColor (1., 100. / 255., 0.); // Orange - } else if (state == DRAGGED) { - color.setColor (1., 0., 0.); // Red - } - } else { - color = innerLineColor; - } - - return color; -} - -RGBColor Geometry::getOuterLineColor () -{ - RGBColor 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 - else if (state == DRAGGED) { color.setColor (1., 0., 0.); } // Black - */ - color.setColor (0., 0., 0.); // Black - } else { - color = outerLineColor; - } - - return color; -} - -#ifdef GUIVERSION - -void Circle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if ((flags & F_VISIBLE) && state != INSENSITIVE) { - RGBColor color; - - if (flags & F_AUTO_COLOR) { - color = getOuterLineColor(); - } else { - color = outerLineColor; - } - - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); - - rtengine::Coord center_ = center; - double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (center.x, center.y, center_.x, center_.y); - } else if (datum == CLICKED_POINT) { - center_ += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); - cr->stroke(); - } -} - -void Circle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if (flags & F_VISIBLE) { - if (state != INSENSITIVE) { - RGBColor color; - - if (flags & F_AUTO_COLOR) { - color = getInnerLineColor(); - } else { - color = innerLineColor; - } - - cr->set_source_rgb(color.getR(), color.getG(), color.getB()); - } - - cr->set_line_width( innerLineWidth ); - - rtengine::Coord center_ = center; - double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (center.x, center.y, center_.x, center_.y); - } else if (datum == CLICKED_POINT) { - center_ += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - if (filled && state != INSENSITIVE) { - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); - - if (innerLineWidth > 0.) { - cr->fill_preserve(); - cr->stroke(); - } else { - cr->fill(); - } - } else if (innerLineWidth > 0.) { - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); - - 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, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if (flags & F_HOVERABLE) { - cr->set_line_width( getMouseOverLineWidth() ); - rtengine::Coord center_ = center; - double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); - - if (datum == IMAGE) { - coordSystem.imageCoordToCropCanvas (center.x, center.y, center_.x, center_.y); - } else if (datum == CLICKED_POINT) { - center_ += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - // setting the color to the objet's ID - if (objectBuffer->getObjectMode() == OM_255) { - cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); - } else { - cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); - } - cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0, 2.*rtengine::RT_PI); - - if (filled) { - if (innerLineWidth > 0.) { - cr->fill_preserve(); - cr->stroke(); - } else { - cr->fill(); - } - } else { - cr->stroke(); - } - } -} - -void Line::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if ((flags & F_VISIBLE) && state != INSENSITIVE) { - RGBColor color; - - if (flags & F_AUTO_COLOR) { - color = getOuterLineColor(); - } else { - color = outerLineColor; - } - - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); - - rtengine::Coord begin_ = begin; - rtengine::Coord end_ = end; - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (begin.x, begin.y, begin_.x, begin_.y); - coordSystem.imageCoordToScreen (end.x, end.y, end_.x, end_.y); - } else if (datum == CLICKED_POINT) { - begin_ += objectBuffer->getDataProvider()->posScreen; - end_ += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - begin_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->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(); - } -} - -void Line::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if ((flags & F_VISIBLE) && innerLineWidth > 0.) { - if (state != INSENSITIVE) { - RGBColor color; - - if (flags & F_AUTO_COLOR) { - color = getInnerLineColor(); - } else { - color = innerLineColor; - } - - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - } - - cr->set_line_width(innerLineWidth); - - rtengine::Coord begin_ = begin; - rtengine::Coord end_ = end; - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (begin.x, begin.y, begin_.x, begin_.y); - coordSystem.imageCoordToScreen (end.x, end.y, end_.x, end_.y); - } else if (datum == CLICKED_POINT) { - begin_ += objectBuffer->getDataProvider()->posScreen; - end_ += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - begin_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - cr->move_to(begin_.x + 0.5, begin_.y + 0.5); - cr->line_to(end_.x + 0.5, end_.y + 0.5); - - 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, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if (flags & F_HOVERABLE) { - cr->set_line_width( getMouseOverLineWidth() ); - rtengine::Coord begin_ = begin; - rtengine::Coord end_ = end; - - if (datum == IMAGE) { - coordSystem.imageCoordToCropCanvas (begin.x, begin.y, begin_.x, begin_.y); - coordSystem.imageCoordToCropCanvas (end.x, end.y, end_.x, end_.y); - } else if (datum == CLICKED_POINT) { - begin_ += objectBuffer->getDataProvider()->posScreen; - end_ += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - begin_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - // setting the color to the objet's ID - if (objectBuffer->getObjectMode() == OM_255) { - cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); - } else { - cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); - } - cr->move_to(begin_.x + 0.5, begin_.y + 0.5); - cr->line_to(end_.x + 0.5, end_.y + 0.5); - cr->stroke(); - } -} - -void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1) { - RGBColor color; - - if (flags & F_AUTO_COLOR) { - color = getOuterLineColor(); - } else { - color = outerLineColor; - } - - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); - - rtengine::Coord currPos; - - for (unsigned int i = 0; i < points.size(); ++i) { - currPos = points.at(i); - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (points.at(i).x, points.at(i).y, currPos.x, currPos.y); - } else if (datum == CLICKED_POINT) { - currPos += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - currPos += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - if (!i) { - cr->move_to(currPos.x + 0.5, currPos.y + 0.5); - } else { - cr->line_to(currPos.x + 0.5, currPos.y + 0.5); - } - } - - if (filled) { - cr->fill_preserve(); - cr->stroke(); - } else { - cr->stroke(); - } - } -} - -void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if ((flags & F_VISIBLE) && points.size() > 1) { - if (state != INSENSITIVE) { - RGBColor color; - - if (flags & F_AUTO_COLOR) { - color = getInnerLineColor(); - } else { - color = innerLineColor; - } - - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - } - - cr->set_line_width( innerLineWidth ); - - if (filled && state != INSENSITIVE) { - rtengine::Coord currPos; - - for (unsigned int i = 0; i < points.size(); ++i) { - currPos = points.at(i); - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (points.at(i).x, points.at(i).y, currPos.x, currPos.y); - } else if (datum == CLICKED_POINT) { - currPos += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - currPos += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - if (!i) { - cr->move_to(currPos.x + 0.5, currPos.y + 0.5); - } else { - cr->line_to(currPos.x + 0.5, currPos.y + 0.5); - } - } - - if (innerLineWidth > 0.) { - cr->fill_preserve(); - cr->stroke(); - } else { - cr->fill(); - } - } else if (innerLineWidth > 0.) { - rtengine::Coord currPos; - - for (unsigned int i = 0; i < points.size(); ++i) { - currPos = points.at(i); - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (points.at(i).x, points.at(i).y, currPos.x, currPos.y); - } else if (datum == CLICKED_POINT) { - currPos += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - currPos += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - if (!i) { - cr->move_to(currPos.x + 0.5, currPos.y + 0.5); - } else { - cr->line_to(currPos.x + 0.5, currPos.y + 0.5); - } - } - - 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, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if ((flags & F_HOVERABLE) && points.size() > 1) { - rtengine::Coord currPos; - - // setting the color to the objet's ID - if (objectBuffer->getObjectMode() == OM_255) { - cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); - } else { - cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); - } - - for (unsigned int i = 0; i < points.size(); ++i) { - cr->set_line_width( getMouseOverLineWidth() ); - currPos = points.at(i); - - if (datum == IMAGE) { - coordSystem.imageCoordToCropCanvas (points.at(i).x, points.at(i).y, currPos.x, currPos.y); - } else if (datum == CLICKED_POINT) { - currPos += objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - currPos += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - if (!i) { - cr->move_to(currPos.x + 0.5, currPos.y + 0.5); - } else { - cr->line_to(currPos.x + 0.5, currPos.y + 0.5); - } - } - - if (filled) { - if (innerLineWidth > 0.) { - cr->fill_preserve(); - cr->stroke(); - } else { - cr->fill(); - } - } else { - cr->stroke(); - } - } -} - -void Rectangle::setXYWH(int left, int top, int width, int height) -{ - topLeft.set(left, top); - bottomRight.set(left + width, top + height); -} - -void Rectangle::setXYXY(int left, int top, int right, int bottom) -{ - topLeft.set(left, top); - bottomRight.set(right, bottom); -} - -void Rectangle::setXYWH(rtengine::Coord topLeft, rtengine::Coord widthHeight) -{ - this->topLeft = topLeft; - this->bottomRight = topLeft + widthHeight; -} - -void Rectangle::setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight) -{ - this->topLeft = topLeft; - this->bottomRight = bottomRight; -} - -void Rectangle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if ((flags & F_VISIBLE) && state != INSENSITIVE) { - RGBColor color; - - if (flags & F_AUTO_COLOR) { - color = getOuterLineColor(); - } else { - color = outerLineColor; - } - - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - cr->set_line_width( getOuterLineWidth() ); - - rtengine::Coord tl, br; - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (topLeft.x, topLeft.y, tl.x, tl.y); - } else if (datum == CLICKED_POINT) { - tl = topLeft + objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - tl = topLeft + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (bottomRight.x, bottomRight.y, br.x, br.y); - } else if (datum == CLICKED_POINT) { - br = bottomRight + objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); - - if (filled) { - cr->fill_preserve(); - cr->stroke(); - } else { - cr->stroke(); - } - } -} - -void Rectangle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if (flags & F_VISIBLE) { - if (state != INSENSITIVE) { - RGBColor color; - - if (flags & F_AUTO_COLOR) { - color = getInnerLineColor(); - } else { - color = innerLineColor; - } - - cr->set_source_rgb (color.getR(), color.getG(), color.getB()); - } - - cr->set_line_width( innerLineWidth ); - - rtengine::Coord tl, br; - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (topLeft.x, topLeft.y, tl.x, tl.y); - } else if (datum == CLICKED_POINT) { - tl = topLeft + objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - tl = topLeft + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen (bottomRight.x, bottomRight.y, br.x, br.y); - } else if (datum == CLICKED_POINT) { - br = bottomRight + objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - 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.) { - cr->fill_preserve(); - cr->stroke(); - } else { - cr->fill(); - } - } else if (innerLineWidth > 0.) { - cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); - - 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, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if (flags & F_HOVERABLE) { - cr->set_line_width( getMouseOverLineWidth() ); - - rtengine::Coord tl, br; - - if (datum == IMAGE) { - coordSystem.imageCoordToCropCanvas (topLeft.x, topLeft.y, tl.x, tl.y); - } else if (datum == CLICKED_POINT) { - tl = topLeft + objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - tl = topLeft + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - if (datum == IMAGE) { - coordSystem.imageCoordToCropCanvas (bottomRight.x, bottomRight.y, br.x, br.y); - } else if (datum == CLICKED_POINT) { - br = bottomRight + objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) { - br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; - } - - // setting the color to the objet's ID - if (objectBuffer->getObjectMode() == OM_255) { - cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); - } else { - cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); - } - cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); - - if (filled) { - if (innerLineWidth > 0.) { - cr->fill_preserve(); - cr->stroke(); - } else { - cr->fill(); - } - } else { - cr->stroke(); - } - } -} - -void OPIcon::drivenPointToRectangle(const rtengine::Coord &pos, - rtengine::Coord &topLeft, rtengine::Coord &bottomRight, int W, int H) -{ - switch (drivenPoint) { - case (DP_CENTERCENTER): - topLeft.x = pos.x - W / 2; - topLeft.y = pos.y - H / 2; - break; - - case (DP_TOPLEFT): - topLeft.x = pos.x; - topLeft.y = pos.y; - break; - - case (DP_TOPCENTER): - topLeft.x = pos.x - W / 2; - topLeft.y = pos.y; - break; - - case (DP_TOPRIGHT): - topLeft.x = pos.x - W; - topLeft.y = pos.y; - break; - - case (DP_CENTERRIGHT): - topLeft.x = pos.x - W; - topLeft.y = pos.y - H / 2; - break; - - case (DP_BOTTOMRIGHT): - topLeft.x = pos.x - W; - topLeft.y = pos.y - H; - break; - - case (DP_BOTTOMCENTER): - topLeft.x = pos.x - W / 2; - topLeft.y = pos.y - H; - break; - - case (DP_BOTTOMLEFT): - topLeft.x = pos.x; - topLeft.y = pos.y - H; - break; - - case (DP_CENTERLEFT): - topLeft.x = pos.x; - topLeft.y = pos.y - H / 2; - break; - } - - bottomRight.x = topLeft.x + W - 1; - bottomRight.y = topLeft.y + H - 1; -} - -OPIcon::OPIcon(const Cairo::RefPtr &normal, - const Cairo::RefPtr &active, - const Cairo::RefPtr &prelight, - const Cairo::RefPtr &dragged, - const Cairo::RefPtr &insensitive, - DrivenPoint drivenPoint) : - drivenPoint(drivenPoint) -{ - if (normal) { - normalImg = normal; - } - - if (prelight) { - prelightImg = prelight; - } - - if (active) { - activeImg = active; - } - - if (dragged) { - draggedImg = dragged; - } - - if (insensitive) { - insensitiveImg = insensitive; - } -} - -OPIcon::OPIcon(Glib::ustring normalImage, Glib::ustring activeImage, Glib::ustring prelightImage, - Glib::ustring draggedImage, Glib::ustring insensitiveImage, DrivenPoint drivenPoint) : drivenPoint(drivenPoint) -{ - if (!normalImage.empty()) { - normalImg->setImage(normalImage); - } - - if (!prelightImage.empty()) { - prelightImg->setImage(prelightImage); - } - - if (!activeImage.empty()) { - activeImg->setImage(activeImage); - } - - if (!draggedImage.empty()) { - draggedImg->setImage(draggedImage); - } - - if (!insensitiveImage.empty()) { - insensitiveImg->setImage(insensitiveImage); - } -} - -const Cairo::RefPtr OPIcon::getNormalImg() -{ - return normalImg; -} -const Cairo::RefPtr OPIcon::getPrelightImg() -{ - return prelightImg; -} -const Cairo::RefPtr OPIcon::getActiveImg() -{ - return activeImg; -} -const Cairo::RefPtr OPIcon::getDraggedImg() -{ - return draggedImg; -} -const Cairo::RefPtr OPIcon::getInsensitiveImg() -{ - return insensitiveImg; -} - -void OPIcon::drawImage(Cairo::RefPtr &img, - Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, - EditCoordSystem &coordSystem) -{ - int imgW = img->getWidth(); - int imgH = img->getHeight(); - - rtengine::Coord pos; - - if (datum == IMAGE) { - coordSystem.imageCoordToScreen(position.x, position.y, pos.x, pos.y); - } else if (datum == CLICKED_POINT) { - pos = position + objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) - pos = position + objectBuffer->getDataProvider()->posScreen - + objectBuffer->getDataProvider()->deltaScreen; - - rtengine::Coord tl, br; // Coordinate of the rectangle in the CropBuffer coordinate system - drivenPointToRectangle(pos, tl, br, imgW, imgH); - - cr->set_source(img->get(), tl.x, tl.y); - cr->set_line_width(0.); - cr->rectangle(tl.x, tl.y, imgW, imgH); - cr->fill(); -} - -void OPIcon::drawMOImage(Cairo::RefPtr &img, Cairo::RefPtr &cr, - unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - // test of F_HOVERABLE has already been done - - int imgW = img->getWidth(); - int imgH = img->getHeight(); - - rtengine::Coord pos; - - if (datum == IMAGE) - coordSystem.imageCoordToCropCanvas (position.x, position.y, pos.x, pos.y); - else if (datum == CLICKED_POINT) { - pos = position + objectBuffer->getDataProvider()->posScreen; - } else if (datum == CURSOR) - pos = position + objectBuffer->getDataProvider()->posScreen - + objectBuffer->getDataProvider()->deltaScreen; - - rtengine::Coord tl, br; // Coordinate of the rectangle in the CropBuffer coordinate system - drivenPointToRectangle(pos, tl, br, imgW, imgH); - - // drawing the lower byte's value - if (objectBuffer->getObjectMode() == OM_255) { - cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); - } else { - cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); - } - cr->set_line_width(0.); - cr->rectangle(tl.x, tl.y, imgW, imgH); - cr->fill(); -} - -void OPIcon::drawOuterGeometry(Cairo::RefPtr &cr, - ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) {} - -void OPIcon::drawInnerGeometry(Cairo::RefPtr &cr, - ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if (flags & F_VISIBLE) { - // Here we will handle fall-back solutions - - State tmpState = state; // can be updated through the successive test - - if (tmpState == INSENSITIVE) { - if (!insensitiveImg) { - tmpState = NORMAL; - } else { - OPIcon::drawImage(insensitiveImg, cr, objectBuffer, coordSystem); - return; - } - } - - if (tmpState == DRAGGED) { - if (!draggedImg) { - tmpState = ACTIVE; - } else { - OPIcon::drawImage(draggedImg, cr, objectBuffer, coordSystem); - return; - } - } - - if (tmpState == ACTIVE) { - if (!activeImg) { - tmpState = PRELIGHT; - } else { - OPIcon::drawImage(activeImg, cr, objectBuffer, coordSystem); - return; - } - } - - if (tmpState == PRELIGHT) { - if (!prelightImg) { - tmpState = NORMAL; - } else { - OPIcon::drawImage(prelightImg, cr, objectBuffer, coordSystem); - return; - } - } - - if (tmpState == NORMAL && normalImg) { - OPIcon::drawImage(normalImg, cr, objectBuffer, coordSystem); - } - } -} - -void OPIcon::drawToMOChannel(Cairo::RefPtr &cr, unsigned short id, - ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) -{ - if (flags & F_HOVERABLE) { - // Here we will handle fallback solutions - State tmpState = state; - - if (tmpState == INSENSITIVE) { - if (!insensitiveImg) { - tmpState = NORMAL; - } else { - OPIcon::drawMOImage(insensitiveImg, cr, id, objectBuffer, coordSystem); - return; - } - } - - if (tmpState == DRAGGED) { - if (!draggedImg) { - tmpState = ACTIVE; - } else { - OPIcon::drawMOImage(draggedImg, cr, id, objectBuffer, coordSystem); - return; - } - } - - if (tmpState == ACTIVE) { - if (!activeImg) { - tmpState = PRELIGHT; - } else { - OPIcon::drawMOImage(activeImg, cr, id, objectBuffer, coordSystem); - return; - } - } - - if (tmpState == PRELIGHT) { - if (!prelightImg) { - tmpState = NORMAL; - } else { - OPIcon::drawMOImage(prelightImg, cr, id, objectBuffer, coordSystem); - return; - } - } - - if (tmpState == NORMAL && normalImg) { - OPIcon::drawMOImage(normalImg, cr, id, objectBuffer, coordSystem); - } - } -} - -#endif - -EditSubscriber::EditSubscriber (EditType editType) : ID(EUID_None), editingType(editType), bufferType(BT_SINGLEPLANE_FLOAT), provider(nullptr), action(ES_ACTION_NONE) {} - -void EditSubscriber::setEditProvider(EditDataProvider *provider) -{ - this->provider = provider; -} - -void EditSubscriber::setEditID(EditUniqueID ID, BufferType buffType) -{ - this->ID = ID; - bufferType = buffType; -} - -bool EditSubscriber::isCurrentSubscriber() -{ - //if (provider && provider->getCurrSubscriber()) - // return provider->getCurrSubscriber()->getEditID() == ID; - - if (provider) { - return provider->getCurrSubscriber() == this; - } - - return false; -} - -void EditSubscriber::subscribe() -{ - if (provider) { - provider->subscribe(this); - } -} - -void EditSubscriber::unsubscribe() -{ - if (provider) { - provider->unsubscribe(); - } -} - -void EditSubscriber::switchOffEditMode() -{ - unsubscribe(); -} - -EditUniqueID EditSubscriber::getEditID() -{ - return ID; -} - -EditType EditSubscriber::getEditingType() -{ - return editingType; -} - -BufferType EditSubscriber::getPipetteBufferType() -{ - return bufferType; -} - -bool EditSubscriber::isDragging() -{ - return action == ES_ACTION_DRAGGING; -} - -bool EditSubscriber::isPicking() -{ - return action == ES_ACTION_PICKING; -} - -//-------------------------------------------------------------------------------------------------- - - -EditDataProvider::EditDataProvider() : currSubscriber(nullptr), object(0), posScreen(-1, -1), posImage(-1, -1), - deltaScreen(0, 0), deltaImage(0, 0), deltaPrevScreen(0, 0), deltaPrevImage(0, 0) -{ - pipetteVal[0] = pipetteVal[1] = pipetteVal[2] = 0.f; -} - -void EditDataProvider::subscribe(EditSubscriber *subscriber) -{ - if (currSubscriber) { - currSubscriber->switchOffEditMode(); - } - - currSubscriber = subscriber; -} - -void EditDataProvider::unsubscribe() -{ - currSubscriber = nullptr; -} - -void EditDataProvider::switchOffEditMode() -{ - if (currSubscriber) { - currSubscriber->switchOffEditMode (); - } -} - -CursorShape EditDataProvider::getCursor(int objectID) -{ - if (currSubscriber) { - currSubscriber->getCursor(objectID); - } - - return CSHandOpen; -} - -EditSubscriber* EditDataProvider::getCurrSubscriber() -{ - return currSubscriber; -} - +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Jean-Christophe FRISCH + * + * 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 "editwidgets.h" +#include "editbuffer.h" +#include "editcallbacks.h" +#include "../rtengine/rt_math.h" + +RGBColor Geometry::getInnerLineColor () +{ + RGBColor color; + + if (flags & F_AUTO_COLOR) { + if (state == NORMAL) { + color.setColor (1., 1., 1.); // White + } else if (state == ACTIVE) { + color.setColor (1., 1., 0.); // Yellow + } else if (state == PRELIGHT) { + color.setColor (1., 100. / 255., 0.); // Orange + } else if (state == DRAGGED) { + color.setColor (1., 0., 0.); // Red + } + } else { + color = innerLineColor; + } + + return color; +} + +RGBColor Geometry::getOuterLineColor () +{ + RGBColor 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 + else if (state == DRAGGED) { color.setColor (1., 0., 0.); } // Black + */ + color.setColor (0., 0., 0.); // Black + } else { + color = outerLineColor; + } + + return color; +} + +#ifdef GUIVERSION + +void Circle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if ((flags & F_VISIBLE) && state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getOuterLineColor(); + } else { + color = outerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( getOuterLineWidth() ); + + rtengine::Coord center_ = center; + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (center.x, center.y, center_.x, center_.y); + } else if (datum == CLICKED_POINT) { + center_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); + cr->stroke(); + } +} + +void Circle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_VISIBLE) { + if (state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgb(color.getR(), color.getG(), color.getB()); + } + + cr->set_line_width( innerLineWidth ); + + rtengine::Coord center_ = center; + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (center.x, center.y, center_.x, center_.y); + } else if (datum == CLICKED_POINT) { + center_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (filled && state != INSENSITIVE) { + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); + + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->fill(); + } + } else if (innerLineWidth > 0.) { + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0., 2.*rtengine::RT_PI); + + 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, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_HOVERABLE) { + cr->set_line_width( getMouseOverLineWidth() ); + rtengine::Coord center_ = center; + double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas(double(radius)) : double(radius); + + if (datum == IMAGE) { + coordSystem.imageCoordToCropCanvas (center.x, center.y, center_.x, center_.y); + } else if (datum == CLICKED_POINT) { + center_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + // setting the color to the objet's ID + if (objectBuffer->getObjectMode() == OM_255) { + cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); + } else { + cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); + } + cr->arc(center_.x + 0.5, center_.y + 0.5, radius_, 0, 2.*rtengine::RT_PI); + + if (filled) { + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->fill(); + } + } else { + cr->stroke(); + } + } +} + +void Line::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if ((flags & F_VISIBLE) && state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getOuterLineColor(); + } else { + color = outerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( getOuterLineWidth() ); + + rtengine::Coord begin_ = begin; + rtengine::Coord end_ = end; + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (begin.x, begin.y, begin_.x, begin_.y); + coordSystem.imageCoordToScreen (end.x, end.y, end_.x, end_.y); + } else if (datum == CLICKED_POINT) { + begin_ += objectBuffer->getDataProvider()->posScreen; + end_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + begin_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->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(); + } +} + +void Line::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if ((flags & F_VISIBLE) && innerLineWidth > 0.) { + if (state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + } + + cr->set_line_width(innerLineWidth); + + rtengine::Coord begin_ = begin; + rtengine::Coord end_ = end; + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (begin.x, begin.y, begin_.x, begin_.y); + coordSystem.imageCoordToScreen (end.x, end.y, end_.x, end_.y); + } else if (datum == CLICKED_POINT) { + begin_ += objectBuffer->getDataProvider()->posScreen; + end_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + begin_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + cr->move_to(begin_.x + 0.5, begin_.y + 0.5); + cr->line_to(end_.x + 0.5, end_.y + 0.5); + + 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, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_HOVERABLE) { + cr->set_line_width( getMouseOverLineWidth() ); + rtengine::Coord begin_ = begin; + rtengine::Coord end_ = end; + + if (datum == IMAGE) { + coordSystem.imageCoordToCropCanvas (begin.x, begin.y, begin_.x, begin_.y); + coordSystem.imageCoordToCropCanvas (end.x, end.y, end_.x, end_.y); + } else if (datum == CLICKED_POINT) { + begin_ += objectBuffer->getDataProvider()->posScreen; + end_ += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + begin_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + end_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + // setting the color to the objet's ID + if (objectBuffer->getObjectMode() == OM_255) { + cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); + } else { + cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); + } + cr->move_to(begin_.x + 0.5, begin_.y + 0.5); + cr->line_to(end_.x + 0.5, end_.y + 0.5); + cr->stroke(); + } +} + +void Polyline::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if ((flags & F_VISIBLE) && state != INSENSITIVE && points.size() > 1) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getOuterLineColor(); + } else { + color = outerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( getOuterLineWidth() ); + + rtengine::Coord currPos; + + for (unsigned int i = 0; i < points.size(); ++i) { + currPos = points.at(i); + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (points.at(i).x, points.at(i).y, currPos.x, currPos.y); + } else if (datum == CLICKED_POINT) { + currPos += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + currPos += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (!i) { + cr->move_to(currPos.x + 0.5, currPos.y + 0.5); + } else { + cr->line_to(currPos.x + 0.5, currPos.y + 0.5); + } + } + + if (filled) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->stroke(); + } + } +} + +void Polyline::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if ((flags & F_VISIBLE) && points.size() > 1) { + if (state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + } + + cr->set_line_width( innerLineWidth ); + + if (filled && state != INSENSITIVE) { + rtengine::Coord currPos; + + for (unsigned int i = 0; i < points.size(); ++i) { + currPos = points.at(i); + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (points.at(i).x, points.at(i).y, currPos.x, currPos.y); + } else if (datum == CLICKED_POINT) { + currPos += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + currPos += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (!i) { + cr->move_to(currPos.x + 0.5, currPos.y + 0.5); + } else { + cr->line_to(currPos.x + 0.5, currPos.y + 0.5); + } + } + + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->fill(); + } + } else if (innerLineWidth > 0.) { + rtengine::Coord currPos; + + for (unsigned int i = 0; i < points.size(); ++i) { + currPos = points.at(i); + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (points.at(i).x, points.at(i).y, currPos.x, currPos.y); + } else if (datum == CLICKED_POINT) { + currPos += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + currPos += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (!i) { + cr->move_to(currPos.x + 0.5, currPos.y + 0.5); + } else { + cr->line_to(currPos.x + 0.5, currPos.y + 0.5); + } + } + + 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, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if ((flags & F_HOVERABLE) && points.size() > 1) { + rtengine::Coord currPos; + + // setting the color to the objet's ID + if (objectBuffer->getObjectMode() == OM_255) { + cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); + } else { + cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); + } + + for (unsigned int i = 0; i < points.size(); ++i) { + cr->set_line_width( getMouseOverLineWidth() ); + currPos = points.at(i); + + if (datum == IMAGE) { + coordSystem.imageCoordToCropCanvas (points.at(i).x, points.at(i).y, currPos.x, currPos.y); + } else if (datum == CLICKED_POINT) { + currPos += objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + currPos += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (!i) { + cr->move_to(currPos.x + 0.5, currPos.y + 0.5); + } else { + cr->line_to(currPos.x + 0.5, currPos.y + 0.5); + } + } + + if (filled) { + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->fill(); + } + } else { + cr->stroke(); + } + } +} + +void Rectangle::setXYWH(int left, int top, int width, int height) +{ + topLeft.set(left, top); + bottomRight.set(left + width, top + height); +} + +void Rectangle::setXYXY(int left, int top, int right, int bottom) +{ + topLeft.set(left, top); + bottomRight.set(right, bottom); +} + +void Rectangle::setXYWH(rtengine::Coord topLeft, rtengine::Coord widthHeight) +{ + this->topLeft = topLeft; + this->bottomRight = topLeft + widthHeight; +} + +void Rectangle::setXYXY(rtengine::Coord topLeft, rtengine::Coord bottomRight) +{ + this->topLeft = topLeft; + this->bottomRight = bottomRight; +} + +void Rectangle::drawOuterGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if ((flags & F_VISIBLE) && state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getOuterLineColor(); + } else { + color = outerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + cr->set_line_width( getOuterLineWidth() ); + + rtengine::Coord tl, br; + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (topLeft.x, topLeft.y, tl.x, tl.y); + } else if (datum == CLICKED_POINT) { + tl = topLeft + objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + tl = topLeft + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (bottomRight.x, bottomRight.y, br.x, br.y); + } else if (datum == CLICKED_POINT) { + br = bottomRight + objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); + + if (filled) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->stroke(); + } + } +} + +void Rectangle::drawInnerGeometry(Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_VISIBLE) { + if (state != INSENSITIVE) { + RGBColor color; + + if (flags & F_AUTO_COLOR) { + color = getInnerLineColor(); + } else { + color = innerLineColor; + } + + cr->set_source_rgb (color.getR(), color.getG(), color.getB()); + } + + cr->set_line_width( innerLineWidth ); + + rtengine::Coord tl, br; + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (topLeft.x, topLeft.y, tl.x, tl.y); + } else if (datum == CLICKED_POINT) { + tl = topLeft + objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + tl = topLeft + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen (bottomRight.x, bottomRight.y, br.x, br.y); + } else if (datum == CLICKED_POINT) { + br = bottomRight + objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + 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.) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->fill(); + } + } else if (innerLineWidth > 0.) { + cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); + + 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, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_HOVERABLE) { + cr->set_line_width( getMouseOverLineWidth() ); + + rtengine::Coord tl, br; + + if (datum == IMAGE) { + coordSystem.imageCoordToCropCanvas (topLeft.x, topLeft.y, tl.x, tl.y); + } else if (datum == CLICKED_POINT) { + tl = topLeft + objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + tl = topLeft + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + if (datum == IMAGE) { + coordSystem.imageCoordToCropCanvas (bottomRight.x, bottomRight.y, br.x, br.y); + } else if (datum == CLICKED_POINT) { + br = bottomRight + objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) { + br = bottomRight + objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; + } + + // setting the color to the objet's ID + if (objectBuffer->getObjectMode() == OM_255) { + cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); + } else { + cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); + } + cr->rectangle(tl.x + 0.5, tl.y + 0.5, br.x - tl.x, br.y - tl.y); + + if (filled) { + if (innerLineWidth > 0.) { + cr->fill_preserve(); + cr->stroke(); + } else { + cr->fill(); + } + } else { + cr->stroke(); + } + } +} + +void OPIcon::drivenPointToRectangle(const rtengine::Coord &pos, + rtengine::Coord &topLeft, rtengine::Coord &bottomRight, int W, int H) +{ + switch (drivenPoint) { + case (DP_CENTERCENTER): + topLeft.x = pos.x - W / 2; + topLeft.y = pos.y - H / 2; + break; + + case (DP_TOPLEFT): + topLeft.x = pos.x; + topLeft.y = pos.y; + break; + + case (DP_TOPCENTER): + topLeft.x = pos.x - W / 2; + topLeft.y = pos.y; + break; + + case (DP_TOPRIGHT): + topLeft.x = pos.x - W; + topLeft.y = pos.y; + break; + + case (DP_CENTERRIGHT): + topLeft.x = pos.x - W; + topLeft.y = pos.y - H / 2; + break; + + case (DP_BOTTOMRIGHT): + topLeft.x = pos.x - W; + topLeft.y = pos.y - H; + break; + + case (DP_BOTTOMCENTER): + topLeft.x = pos.x - W / 2; + topLeft.y = pos.y - H; + break; + + case (DP_BOTTOMLEFT): + topLeft.x = pos.x; + topLeft.y = pos.y - H; + break; + + case (DP_CENTERLEFT): + topLeft.x = pos.x; + topLeft.y = pos.y - H / 2; + break; + } + + bottomRight.x = topLeft.x + W - 1; + bottomRight.y = topLeft.y + H - 1; +} + +OPIcon::OPIcon(const Cairo::RefPtr &normal, + const Cairo::RefPtr &active, + const Cairo::RefPtr &prelight, + const Cairo::RefPtr &dragged, + const Cairo::RefPtr &insensitive, + DrivenPoint drivenPoint) : + drivenPoint(drivenPoint) +{ + if (normal) { + normalImg = normal; + } + + if (prelight) { + prelightImg = prelight; + } + + if (active) { + activeImg = active; + } + + if (dragged) { + draggedImg = dragged; + } + + if (insensitive) { + insensitiveImg = insensitive; + } +} + +OPIcon::OPIcon(Glib::ustring normalImage, Glib::ustring activeImage, Glib::ustring prelightImage, + Glib::ustring draggedImage, Glib::ustring insensitiveImage, DrivenPoint drivenPoint) : drivenPoint(drivenPoint) +{ + if (!normalImage.empty()) { + normalImg->setImage(normalImage); + } + + if (!prelightImage.empty()) { + prelightImg->setImage(prelightImage); + } + + if (!activeImage.empty()) { + activeImg->setImage(activeImage); + } + + if (!draggedImage.empty()) { + draggedImg->setImage(draggedImage); + } + + if (!insensitiveImage.empty()) { + insensitiveImg->setImage(insensitiveImage); + } +} + +const Cairo::RefPtr OPIcon::getNormalImg() +{ + return normalImg; +} +const Cairo::RefPtr OPIcon::getPrelightImg() +{ + return prelightImg; +} +const Cairo::RefPtr OPIcon::getActiveImg() +{ + return activeImg; +} +const Cairo::RefPtr OPIcon::getDraggedImg() +{ + return draggedImg; +} +const Cairo::RefPtr OPIcon::getInsensitiveImg() +{ + return insensitiveImg; +} + +void OPIcon::drawImage(Cairo::RefPtr &img, + Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, + EditCoordSystem &coordSystem) +{ + int imgW = img->getWidth(); + int imgH = img->getHeight(); + + rtengine::Coord pos; + + if (datum == IMAGE) { + coordSystem.imageCoordToScreen(position.x, position.y, pos.x, pos.y); + } else if (datum == CLICKED_POINT) { + pos = position + objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) + pos = position + objectBuffer->getDataProvider()->posScreen + + objectBuffer->getDataProvider()->deltaScreen; + + rtengine::Coord tl, br; // Coordinate of the rectangle in the CropBuffer coordinate system + drivenPointToRectangle(pos, tl, br, imgW, imgH); + + cr->set_source(img->get(), tl.x, tl.y); + cr->set_line_width(0.); + cr->rectangle(tl.x, tl.y, imgW, imgH); + cr->fill(); +} + +void OPIcon::drawMOImage(Cairo::RefPtr &img, Cairo::RefPtr &cr, + unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + // test of F_HOVERABLE has already been done + + int imgW = img->getWidth(); + int imgH = img->getHeight(); + + rtengine::Coord pos; + + if (datum == IMAGE) + coordSystem.imageCoordToCropCanvas (position.x, position.y, pos.x, pos.y); + else if (datum == CLICKED_POINT) { + pos = position + objectBuffer->getDataProvider()->posScreen; + } else if (datum == CURSOR) + pos = position + objectBuffer->getDataProvider()->posScreen + + objectBuffer->getDataProvider()->deltaScreen; + + rtengine::Coord tl, br; // Coordinate of the rectangle in the CropBuffer coordinate system + drivenPointToRectangle(pos, tl, br, imgW, imgH); + + // drawing the lower byte's value + if (objectBuffer->getObjectMode() == OM_255) { + cr->set_source_rgba (0., 0., 0., ((id + 1) & 0xFF) / 255.); + } else { + cr->set_source_rgba (0., 0., 0., (id + 1) / 65535.); + } + cr->set_line_width(0.); + cr->rectangle(tl.x, tl.y, imgW, imgH); + cr->fill(); +} + +void OPIcon::drawOuterGeometry(Cairo::RefPtr &cr, + ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) {} + +void OPIcon::drawInnerGeometry(Cairo::RefPtr &cr, + ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_VISIBLE) { + // Here we will handle fall-back solutions + + State tmpState = state; // can be updated through the successive test + + if (tmpState == INSENSITIVE) { + if (!insensitiveImg) { + tmpState = NORMAL; + } else { + OPIcon::drawImage(insensitiveImg, cr, objectBuffer, coordSystem); + return; + } + } + + if (tmpState == DRAGGED) { + if (!draggedImg) { + tmpState = ACTIVE; + } else { + OPIcon::drawImage(draggedImg, cr, objectBuffer, coordSystem); + return; + } + } + + if (tmpState == ACTIVE) { + if (!activeImg) { + tmpState = PRELIGHT; + } else { + OPIcon::drawImage(activeImg, cr, objectBuffer, coordSystem); + return; + } + } + + if (tmpState == PRELIGHT) { + if (!prelightImg) { + tmpState = NORMAL; + } else { + OPIcon::drawImage(prelightImg, cr, objectBuffer, coordSystem); + return; + } + } + + if (tmpState == NORMAL && normalImg) { + OPIcon::drawImage(normalImg, cr, objectBuffer, coordSystem); + } + } +} + +void OPIcon::drawToMOChannel(Cairo::RefPtr &cr, unsigned short id, + ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) +{ + if (flags & F_HOVERABLE) { + // Here we will handle fallback solutions + State tmpState = state; + + if (tmpState == INSENSITIVE) { + if (!insensitiveImg) { + tmpState = NORMAL; + } else { + OPIcon::drawMOImage(insensitiveImg, cr, id, objectBuffer, coordSystem); + return; + } + } + + if (tmpState == DRAGGED) { + if (!draggedImg) { + tmpState = ACTIVE; + } else { + OPIcon::drawMOImage(draggedImg, cr, id, objectBuffer, coordSystem); + return; + } + } + + if (tmpState == ACTIVE) { + if (!activeImg) { + tmpState = PRELIGHT; + } else { + OPIcon::drawMOImage(activeImg, cr, id, objectBuffer, coordSystem); + return; + } + } + + if (tmpState == PRELIGHT) { + if (!prelightImg) { + tmpState = NORMAL; + } else { + OPIcon::drawMOImage(prelightImg, cr, id, objectBuffer, coordSystem); + return; + } + } + + if (tmpState == NORMAL && normalImg) { + OPIcon::drawMOImage(normalImg, cr, id, objectBuffer, coordSystem); + } + } +} + +#endif diff --git a/rtgui/edit.h b/rtgui/editwidgets.h similarity index 61% rename from rtgui/edit.h rename to rtgui/editwidgets.h index 786339745..ec935291e 100644 --- a/rtgui/edit.h +++ b/rtgui/editwidgets.h @@ -1,863 +1,542 @@ -/* - * 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 . - */ -#pragma once -#include -#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" - -#ifdef GUIVERSION -#include "rtsurface.h" -#endif - -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 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 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 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& 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 &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0; - virtual void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0; - virtual void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0; -}; - -#ifdef GUIVERSION - -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 &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; -}; - -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 &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; -}; - -class Polyline : public Geometry -{ -public: - std::vector points; - bool filled; - - Polyline (); - - void drawOuterGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; -}; - -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 &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; -}; - -class OPIcon : public Geometry // OP stands for "On Preview" -{ - -private: - Cairo::RefPtr normalImg; - Cairo::RefPtr prelightImg; - Cairo::RefPtr activeImg; - Cairo::RefPtr draggedImg; - Cairo::RefPtr insensitiveImg; - - static void updateImages(); - void changeImage(Glib::ustring &newImage); - void drawImage (Cairo::RefPtr &img, Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem); - void drawMOImage (Cairo::RefPtr &img, Cairo::RefPtr &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 &normal, - const Cairo::RefPtr &active, - const Cairo::RefPtr &prelight = {}, - const Cairo::RefPtr &dragged = {}, - const Cairo::RefPtr &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 getNormalImg(); - const Cairo::RefPtr getPrelightImg(); - const Cairo::RefPtr getActiveImg(); - const Cairo::RefPtr getDraggedImg(); - const Cairo::RefPtr getInsensitiveImg(); - void drawOuterGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; - void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; -}; - -class OPAdjuster : public Geometry // OP stands for "On Preview" -{ - -}; - -#endif - -/// @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 visibleGeometry; /// displayed geometry - std::vector 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& getVisibleGeometry (); - - /** @brief Get the geometry to be drawn in the "mouse over" channel, hidden from the user */ - const std::vector& 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& 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 CSHandOpen; -} - -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& EditSubscriber::getVisibleGeometry () { - return visibleGeometry; -} - -inline const std::vector& 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 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.) { -} - -#ifdef GUIVERSION - -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 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 +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2019 Jean-Christophe FRISCH + * + * 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 . + */ +#pragma once + +#ifdef GUIVERSION + +#include "rtsurface.h" +#include "editbuffer.h" +#include "editcoordsys.h" +#include "../rtengine/coord.h" + +class ObjectMOBuffer; + +/** @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 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 ProcEvent used. + * + */ + +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 &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0; + virtual void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) = 0; + virtual void drawToMOChannel (Cairo::RefPtr &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 &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; +}; + +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 &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; +}; + +class Polyline : public Geometry +{ +public: + std::vector points; + bool filled; + + Polyline (); + + void drawOuterGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; +}; + +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 &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; +}; + +class OPIcon : public Geometry // OP stands for "On Preview" +{ + +private: + Cairo::RefPtr normalImg; + Cairo::RefPtr prelightImg; + Cairo::RefPtr activeImg; + Cairo::RefPtr draggedImg; + Cairo::RefPtr insensitiveImg; + + static void updateImages(); + void changeImage(Glib::ustring &newImage); + void drawImage (Cairo::RefPtr &img, Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem); + void drawMOImage (Cairo::RefPtr &img, Cairo::RefPtr &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 &normal, + const Cairo::RefPtr &active, + const Cairo::RefPtr &prelight = {}, + const Cairo::RefPtr &dragged = {}, + const Cairo::RefPtr &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 getNormalImg(); + const Cairo::RefPtr getPrelightImg(); + const Cairo::RefPtr getActiveImg(); + const Cairo::RefPtr getDraggedImg(); + const Cairo::RefPtr getInsensitiveImg(); + void drawOuterGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; + void drawToMOChannel (Cairo::RefPtr &cr, unsigned short id, ObjectMOBuffer *objectBuffer, EditCoordSystem &coordSystem) override; +}; + +class OPAdjuster : public Geometry // OP stands for "On Preview" +{ + +}; + +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 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 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 () : + 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 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 + diff --git a/rtgui/exifpanel.cc b/rtgui/exifpanel.cc index 4ba3f6c44..c5036798e 100644 --- a/rtgui/exifpanel.cc +++ b/rtgui/exifpanel.cc @@ -20,6 +20,7 @@ #include "guiutils.h" #include "rtimage.h" +#include "options.h" #include "../rtengine/procparams.h" diff --git a/rtgui/gradient.cc b/rtgui/gradient.cc index fd99e9939..be5d455fe 100644 --- a/rtgui/gradient.cc +++ b/rtgui/gradient.cc @@ -3,6 +3,7 @@ */ #include "gradient.h" +#include "editwidgets.h" #include "rtimage.h" #include "../rtengine/procparams.h" diff --git a/rtgui/gradient.h b/rtgui/gradient.h index 8812d6670..e80661d33 100644 --- a/rtgui/gradient.h +++ b/rtgui/gradient.h @@ -6,8 +6,8 @@ #include #include "adjuster.h" +#include "editcallbacks.h" #include "toolpanel.h" -#include "edit.h" #include "guiutils.h" class Gradient : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public EditSubscriber diff --git a/rtgui/imagearea.h b/rtgui/imagearea.h index f95045532..0e8794840 100644 --- a/rtgui/imagearea.h +++ b/rtgui/imagearea.h @@ -27,10 +27,10 @@ #include "previewhandler.h" #include "imageareatoollistener.h" #include "cropwindow.h" +#include "editcallbacks.h" #include "zoompanel.h" #include "indclippedpanel.h" #include "previewmodepanel.h" -#include "edit.h" class ImageAreaPanel; class ImageArea : public Gtk::DrawingArea, public CropWindowListener, public EditDataProvider, public LockablePickerToolListener diff --git a/rtgui/labcurve.cc b/rtgui/labcurve.cc index e73fa31e0..13ea00b74 100644 --- a/rtgui/labcurve.cc +++ b/rtgui/labcurve.cc @@ -20,10 +20,9 @@ #include "labcurve.h" -#include "edit.h" - #include "../rtengine/improcfun.h" #include "../rtengine/procparams.h" +#include "editcallbacks.h" using namespace rtengine; using namespace rtengine::procparams; diff --git a/rtgui/labcurve.h b/rtgui/labcurve.h index eb582035a..b727cca84 100644 --- a/rtgui/labcurve.h +++ b/rtgui/labcurve.h @@ -26,6 +26,8 @@ #include "curveeditorgroup.h" #include "colorprovider.h" +class EditDataProvider; + class LCurve : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public CurveListener, public ColorProvider { diff --git a/rtgui/labgrid.cc b/rtgui/labgrid.cc index 81d4ee170..4576885e8 100644 --- a/rtgui/labgrid.cc +++ b/rtgui/labgrid.cc @@ -38,6 +38,8 @@ #include "labgrid.h" +#include "options.h" + using rtengine::Color; diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc index 85dcb992a..0dd3cb1b7 100644 --- a/rtgui/lensprofile.cc +++ b/rtgui/lensprofile.cc @@ -26,6 +26,7 @@ #include "guiutils.h" #include "rtimage.h" +#include "options.h" #include "../rtengine/lcp.h" #include "../rtengine/procparams.h" diff --git a/rtgui/lockablecolorpicker.h b/rtgui/lockablecolorpicker.h index 211f8de4d..cafea26be 100644 --- a/rtgui/lockablecolorpicker.h +++ b/rtgui/lockablecolorpicker.h @@ -23,7 +23,6 @@ #include "../rtengine/coord.h" #include "guiutils.h" -#include "edit.h" class CropWindow; diff --git a/rtgui/mydiagonalcurve.cc b/rtgui/mydiagonalcurve.cc index 59ac5a807..b8711d30c 100644 --- a/rtgui/mydiagonalcurve.cc +++ b/rtgui/mydiagonalcurve.cc @@ -18,6 +18,7 @@ */ #include "mydiagonalcurve.h" #include "../rtengine/curves.h" +#include "editcallbacks.h" #include #include diff --git a/rtgui/myflatcurve.cc b/rtgui/myflatcurve.cc index ee7a2de5f..863e125c4 100644 --- a/rtgui/myflatcurve.cc +++ b/rtgui/myflatcurve.cc @@ -18,6 +18,7 @@ */ #include "myflatcurve.h" #include "../rtengine/curves.h" +#include "editcallbacks.h" #include #include diff --git a/rtgui/preprocess.cc b/rtgui/preprocess.cc index 0d8933a98..65646dea3 100644 --- a/rtgui/preprocess.cc +++ b/rtgui/preprocess.cc @@ -21,6 +21,7 @@ #include "preprocess.h" #include "guiutils.h" +#include "options.h" #include "../rtengine/procparams.h" diff --git a/rtgui/rawcacorrection.cc b/rtgui/rawcacorrection.cc index 57b8ff4ac..06780559d 100644 --- a/rtgui/rawcacorrection.cc +++ b/rtgui/rawcacorrection.cc @@ -21,6 +21,7 @@ #include "eventmapper.h" #include "guiutils.h" #include "rtimage.h" +#include "options.h" #include "../rtengine/procparams.h" diff --git a/rtgui/rawexposure.cc b/rtgui/rawexposure.cc index 6f08e64c7..599c8fd12 100644 --- a/rtgui/rawexposure.cc +++ b/rtgui/rawexposure.cc @@ -21,6 +21,7 @@ #include "rawexposure.h" #include "guiutils.h" +#include "options.h" #include "../rtengine/procparams.h" diff --git a/rtgui/sharpenedge.cc b/rtgui/sharpenedge.cc index b6528e4c9..968cb75ad 100644 --- a/rtgui/sharpenedge.cc +++ b/rtgui/sharpenedge.cc @@ -22,6 +22,7 @@ #include "sharpenedge.h" #include "guiutils.h" +#include "options.h" #include "../rtengine/procparams.h" diff --git a/rtgui/tonecurve.cc b/rtgui/tonecurve.cc index 1bd8b2cbd..91b268e9c 100644 --- a/rtgui/tonecurve.cc +++ b/rtgui/tonecurve.cc @@ -23,11 +23,11 @@ #include "tonecurve.h" #include "adjuster.h" -#include "edit.h" #include "eventmapper.h" #include "ppversion.h" #include "../rtengine/procparams.h" +#include "editcallbacks.h" using namespace rtengine; using namespace rtengine::procparams; diff --git a/rtgui/tonecurve.h b/rtgui/tonecurve.h index 47789ec01..29b484137 100644 --- a/rtgui/tonecurve.h +++ b/rtgui/tonecurve.h @@ -27,6 +27,8 @@ #include "mycurve.h" #include "guiutils.h" +class EditDataProvider; + class ToneCurve : public ToolParamBlock, public AdjusterListener, public FoldableToolPanel, public rtengine::AutoExpListener, public CurveListener { private: diff --git a/rtgui/toolpanel.h b/rtgui/toolpanel.h index 521d52949..2da5f1bba 100644 --- a/rtgui/toolpanel.h +++ b/rtgui/toolpanel.h @@ -22,10 +22,10 @@ #include #include #include "../rtengine/rtengine.h" +#include "editbuffer.h" #include "guiutils.h" #include "multilangmgr.h" #include "paramsedited.h" -#include "edit.h" class ToolPanel; class FoldableToolPanel; diff --git a/rtgui/wavelet.cc b/rtgui/wavelet.cc index 60915fa02..b9cf792db 100644 --- a/rtgui/wavelet.cc +++ b/rtgui/wavelet.cc @@ -19,7 +19,8 @@ #include "wavelet.h" #include -#include "edit.h" + +#include "editcallbacks.h" #include "guiutils.h" #include "rtimage.h" diff --git a/rtgui/wavelet.h b/rtgui/wavelet.h index 163395d52..90a72f623 100644 --- a/rtgui/wavelet.h +++ b/rtgui/wavelet.h @@ -29,6 +29,8 @@ #include "guiutils.h" #include "options.h" +class EditDataProvider; + class Wavelet : public ToolParamBlock, public ThresholdAdjusterListener, diff --git a/rtgui/xtransrawexposure.cc b/rtgui/xtransrawexposure.cc index b58a6e72a..a863ecfea 100644 --- a/rtgui/xtransrawexposure.cc +++ b/rtgui/xtransrawexposure.cc @@ -21,6 +21,7 @@ #include "xtransrawexposure.h" #include "guiutils.h" +#include "options.h" #include "../rtengine/procparams.h"