Deleting a spot while hovering over one of the circles for adjusting the radius or feathering could trigger a crash. This is because immediately after deleting a geometry object, EditSubscriber::getCursor is given an object ID based on old data, causing the spot tool to attempt to get information about the deleted geometry object. This commit fixes the issue by returning the default cursor if the active spot is -1. It works because the active spot is set to -1 when deleting a spot, and there is no need to use a special cursor when there is no active spot. A better long-term solution would be updating the geometry data before calling EditSubscriber::getCursor.
818 lines
28 KiB
C++
818 lines
28 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2019 Jean-Christophe FRISCH <natureh.510@gmail.com>
|
|
*
|
|
* RawTherapee is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* RawTherapee is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "editcallbacks.h"
|
|
#include "spot.h"
|
|
#include "rtimage.h"
|
|
#include <iomanip>
|
|
#include "../rtengine/rt_math.h"
|
|
#include "guiutils.h"
|
|
#include "eventmapper.h"
|
|
#include "../rtengine/refreshmap.h"
|
|
|
|
using namespace rtengine;
|
|
using namespace rtengine::procparams;
|
|
|
|
#define STATIC_VISIBLE_OBJ_NBR 6
|
|
#define STATIC_MO_OBJ_NBR 6
|
|
|
|
Spot::Spot() :
|
|
FoldableToolPanel(this, "spot", M ("TP_SPOT_LABEL"), true, true),
|
|
EditSubscriber(ET_OBJECTS),
|
|
draggedSide(DraggedSide::NONE),
|
|
lastObject(-1),
|
|
activeSpot(-1),
|
|
sourceIcon("spot-normal.png", "spot-active.png", "spot-prelight.png", "", "", Geometry::DP_CENTERCENTER),
|
|
editedCheckBox(nullptr)
|
|
{
|
|
countLabel = Gtk::manage (new Gtk::Label (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0)));
|
|
|
|
edit = Gtk::manage (new Gtk::ToggleButton());
|
|
edit->add (*Gtk::manage (new RTImage ("edit-point.png")));
|
|
editConn = edit->signal_toggled().connect ( sigc::mem_fun (*this, &Spot::editToggled) );
|
|
edit->set_tooltip_text(M("TP_SPOT_HINT"));
|
|
|
|
reset = Gtk::manage (new Gtk::Button ());
|
|
reset->add (*Gtk::manage (new RTImage ("undo-small.png")));
|
|
reset->set_relief (Gtk::RELIEF_NONE);
|
|
reset->set_border_width (0);
|
|
reset->signal_clicked().connect ( sigc::mem_fun (*this, &Spot::resetPressed) );
|
|
|
|
labelBox = Gtk::manage (new Gtk::HBox());
|
|
labelBox->set_spacing (2);
|
|
labelBox->pack_start (*countLabel, false, false, 0);
|
|
labelBox->pack_end (*edit, false, false, 0);
|
|
labelBox->pack_end (*reset, false, false, 0);
|
|
pack_start (*labelBox);
|
|
|
|
sourceIcon.datum = Geometry::IMAGE;
|
|
sourceIcon.setActive (false);
|
|
sourceIcon.state = Geometry::ACTIVE;
|
|
sourceCircle.datum = Geometry::IMAGE;
|
|
sourceCircle.setActive (false);
|
|
sourceCircle.radiusInImageSpace = true;
|
|
sourceCircle.setDashed(true);
|
|
sourceMODisc.datum = Geometry::IMAGE;
|
|
sourceMODisc.setActive (false);
|
|
sourceMODisc.radiusInImageSpace = true;
|
|
sourceMODisc.filled = true;
|
|
sourceMODisc.innerLineWidth = 0.;
|
|
targetCircle.datum = Geometry::IMAGE;
|
|
targetCircle.setActive (false);
|
|
targetCircle.radiusInImageSpace = true;
|
|
targetMODisc.datum = Geometry::IMAGE;
|
|
targetMODisc.setActive (false);
|
|
targetMODisc.radiusInImageSpace = true;
|
|
targetMODisc.filled = true;
|
|
targetMODisc.innerLineWidth = 0.;
|
|
sourceFeatherCircle.datum = Geometry::IMAGE;
|
|
sourceFeatherCircle.setActive (false);
|
|
sourceFeatherCircle.radiusInImageSpace = true;
|
|
sourceFeatherCircle.setDashed(true);
|
|
sourceFeatherCircle.innerLineWidth = 0.7;
|
|
targetFeatherCircle.datum = Geometry::IMAGE;
|
|
targetFeatherCircle.setActive (false);
|
|
targetFeatherCircle.radiusInImageSpace = true;
|
|
targetFeatherCircle.innerLineWidth = 0.7;
|
|
link.datum = Geometry::IMAGE;
|
|
link.setActive (false);
|
|
|
|
auto m = ProcEventMapper::getInstance();
|
|
EvSpotEnabled = m->newEvent(ALLNORAW, "TP_SPOT_LABEL");
|
|
EvSpotEnabledOPA = m->newEvent(SPOTADJUST, "TP_SPOT_LABEL");
|
|
EvSpotEntry = m->newEvent(SPOTADJUST, "HISTORY_MSG_SPOT_ENTRY");
|
|
EvSpotEntryOPA = m->newEvent(SPOTADJUST, "HISTORY_MSG_SPOT_ENTRY");
|
|
|
|
show_all();
|
|
}
|
|
|
|
Spot::~Spot()
|
|
{
|
|
// delete all dynamically allocated geometry
|
|
if (EditSubscriber::visibleGeometry.size()) {
|
|
for (size_t i = 0; i < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i) { // static visible geometry at the end if the list
|
|
delete EditSubscriber::visibleGeometry.at (i);
|
|
}
|
|
}
|
|
|
|
// We do not delete the mouseOverGeometry, because the referenced objects are either
|
|
// shared with visibleGeometry or instantiated by the class's ctor
|
|
}
|
|
|
|
void Spot::read (const ProcParams* pp, const ParamsEdited* pedited)
|
|
{
|
|
disableListener ();
|
|
|
|
size_t oldSize = spots.size();
|
|
spots = pp->spot.entries;
|
|
|
|
if (pedited) {
|
|
set_inconsistent (multiImage && !pedited->spot.enabled);
|
|
}
|
|
|
|
setEnabled (pp->spot.enabled);
|
|
lastEnabled = pp->spot.enabled;
|
|
activeSpot = -1;
|
|
lastObject = -1;
|
|
|
|
if (batchMode) {
|
|
editedCheckBox->set_label(Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
|
|
}
|
|
else {
|
|
if (spots.size() != oldSize) {
|
|
createGeometry();
|
|
}
|
|
|
|
updateGeometry();
|
|
}
|
|
|
|
enableListener ();
|
|
}
|
|
|
|
void Spot::write (ProcParams* pp, ParamsEdited* pedited)
|
|
{
|
|
pp->spot.enabled = getEnabled();
|
|
pp->spot.entries = spots;
|
|
|
|
if (pedited) {
|
|
pedited->spot.enabled = !get_inconsistent();
|
|
pedited->spot.entries = editedCheckBox->get_active();
|
|
}
|
|
}
|
|
|
|
void Spot::resetPressed()
|
|
{
|
|
if (batchMode) {
|
|
// no need to handle the Geometry in batch mode, since point editing is disabled
|
|
spots.clear();
|
|
editedConn.block (true);
|
|
editedCheckBox->set_active (true);
|
|
editedConn.block (false);
|
|
|
|
editedCheckBox->set_label(Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
|
|
|
|
if (listener) {
|
|
listener->panelChanged (EvSpotEntry, Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0));
|
|
}
|
|
} else {
|
|
if (!spots.empty()) {
|
|
spots.clear();
|
|
activeSpot = -1;
|
|
lastObject = -1;
|
|
createGeometry();
|
|
updateGeometry();
|
|
|
|
if (listener) {
|
|
listener->panelChanged (edit->get_active() ? EvSpotEntryOPA : EvSpotEntry, Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spot::setBatchMode (bool batchMode)
|
|
{
|
|
ToolPanel::setBatchMode (batchMode);
|
|
|
|
if (batchMode) {
|
|
removeIfThere (labelBox, edit, false);
|
|
|
|
if (!editedCheckBox) {
|
|
removeIfThere (labelBox, countLabel, false);
|
|
countLabel = nullptr;
|
|
editedCheckBox = Gtk::manage (new Gtk::CheckButton (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), 0)));
|
|
labelBox->pack_start (*editedCheckBox, Gtk::PACK_SHRINK, 2);
|
|
labelBox->reorder_child (*editedCheckBox, 0);
|
|
editedConn = editedCheckBox->signal_toggled().connect ( sigc::mem_fun (*this, &Spot::editedToggled) );
|
|
editedCheckBox->show();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spot::editedToggled ()
|
|
{
|
|
if (listener) {
|
|
listener->panelChanged (EvSpotEntry, !editedCheckBox->get_active() ? M ("GENERAL_UNCHANGED") : Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), spots.size()));
|
|
}
|
|
}
|
|
|
|
void Spot::enabledChanged ()
|
|
{
|
|
if (listener) {
|
|
if (get_inconsistent()) {
|
|
listener->panelChanged (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_UNCHANGED"));
|
|
} else if (getEnabled()) {
|
|
listener->panelChanged (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_ENABLED"));
|
|
} else {
|
|
listener->panelChanged (edit->get_active() ? EvSpotEnabledOPA : EvSpotEnabled, M ("GENERAL_DISABLED"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Spot::setEditProvider (EditDataProvider* provider)
|
|
{
|
|
EditSubscriber::setEditProvider (provider);
|
|
}
|
|
|
|
void Spot::editToggled ()
|
|
{
|
|
if (listener) {
|
|
if (edit->get_active()) {
|
|
listener->setTweakOperator(this);
|
|
listener->refreshPreview(EvSpotEnabledOPA); // reprocess the preview w/o creating History entry
|
|
subscribe();
|
|
} else {
|
|
unsubscribe();
|
|
listener->unsetTweakOperator(this);
|
|
listener->refreshPreview(EvSpotEnabled); // reprocess the preview w/o creating History entry
|
|
}
|
|
}
|
|
}
|
|
|
|
Geometry* Spot::getVisibleGeometryFromMO (int MOID)
|
|
{
|
|
if (MOID == -1) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (MOID == 0) {
|
|
return getActiveSpotIcon();
|
|
}
|
|
|
|
if (MOID == 1) { // sourceMODisc
|
|
return &sourceIcon;
|
|
}
|
|
|
|
if (MOID > STATIC_MO_OBJ_NBR) {
|
|
return EditSubscriber::visibleGeometry.at(MOID - STATIC_MO_OBJ_NBR);
|
|
}
|
|
|
|
return EditSubscriber::mouseOverGeometry.at (MOID);
|
|
}
|
|
|
|
void Spot::createGeometry ()
|
|
{
|
|
int nbrEntry = spots.size();
|
|
|
|
if (!batchMode) {
|
|
countLabel->set_text (Glib::ustring::compose (M ("TP_SPOT_COUNTLABEL"), nbrEntry));
|
|
}
|
|
|
|
//printf("CreateGeometry(%d)\n", nbrEntry);
|
|
// delete all dynamically allocated geometry
|
|
if (EditSubscriber::visibleGeometry.size() > STATIC_VISIBLE_OBJ_NBR)
|
|
for (size_t i = 0; i < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i) { // static visible geometry at the end if the list
|
|
delete EditSubscriber::visibleGeometry.at (i);
|
|
}
|
|
|
|
// mouse over geometry starts with the static geometry, then the spot's icon geometry
|
|
EditSubscriber::mouseOverGeometry.resize (STATIC_MO_OBJ_NBR + nbrEntry);
|
|
// visible geometry starts with the spot's icon geometry, then the static geometry
|
|
EditSubscriber::visibleGeometry.resize (nbrEntry + STATIC_VISIBLE_OBJ_NBR);
|
|
|
|
size_t i = 0, j = 0;
|
|
EditSubscriber::mouseOverGeometry.at (i++) = &targetMODisc; // STATIC_MO_OBJ_NBR + 0
|
|
EditSubscriber::mouseOverGeometry.at (i++) = &sourceMODisc; // STATIC_MO_OBJ_NBR + 1
|
|
EditSubscriber::mouseOverGeometry.at (i++) = &targetCircle; // STATIC_MO_OBJ_NBR + 2
|
|
EditSubscriber::mouseOverGeometry.at (i++) = &sourceCircle; // STATIC_MO_OBJ_NBR + 3
|
|
EditSubscriber::mouseOverGeometry.at (i++) = &targetFeatherCircle; // STATIC_MO_OBJ_NBR + 4
|
|
EditSubscriber::mouseOverGeometry.at (i++) = &sourceFeatherCircle; // STATIC_MO_OBJ_NBR + 5
|
|
|
|
// recreate all spots geometry
|
|
Cairo::RefPtr<RTSurface> normalImg = sourceIcon.getNormalImg();
|
|
Cairo::RefPtr<RTSurface> prelightImg = sourceIcon.getPrelightImg();
|
|
Cairo::RefPtr<RTSurface> activeImg = sourceIcon.getActiveImg();
|
|
|
|
for (; j < EditSubscriber::visibleGeometry.size() - STATIC_VISIBLE_OBJ_NBR; ++i, ++j) {
|
|
EditSubscriber::mouseOverGeometry.at (i) = EditSubscriber::visibleGeometry.at (j) = new OPIcon (normalImg, activeImg, prelightImg, Cairo::RefPtr<RTSurface> (nullptr), Cairo::RefPtr<RTSurface> (nullptr), Geometry::DP_CENTERCENTER);
|
|
EditSubscriber::visibleGeometry.at (j)->setActive (true);
|
|
EditSubscriber::visibleGeometry.at (j)->datum = Geometry::IMAGE;
|
|
EditSubscriber::visibleGeometry.at (j)->state = Geometry::NORMAL;
|
|
//printf("mouseOverGeometry.at(%d) = %p\n", (unsigned int)i, (void*)EditSubscriber::mouseOverGeometry.at(i));
|
|
}
|
|
|
|
EditSubscriber::visibleGeometry.at (j++) = &sourceIcon; // STATIC_VISIBLE_OBJ_NBR + 0
|
|
EditSubscriber::visibleGeometry.at (j++) = &sourceFeatherCircle; // STATIC_VISIBLE_OBJ_NBR + 1
|
|
EditSubscriber::visibleGeometry.at (j++) = &link; // STATIC_VISIBLE_OBJ_NBR + 2
|
|
EditSubscriber::visibleGeometry.at (j++) = &sourceCircle; // STATIC_VISIBLE_OBJ_NBR + 3
|
|
EditSubscriber::visibleGeometry.at (j++) = &targetFeatherCircle; // STATIC_VISIBLE_OBJ_NBR + 4
|
|
EditSubscriber::visibleGeometry.at (j++) = &targetCircle; // STATIC_VISIBLE_OBJ_NBR + 5
|
|
}
|
|
|
|
void Spot::updateGeometry()
|
|
{
|
|
EditDataProvider* dataProvider = getEditProvider();
|
|
|
|
if (dataProvider) {
|
|
int imW, imH;
|
|
dataProvider->getImageSize (imW, imH);
|
|
|
|
if (activeSpot > -1) {
|
|
// Target point circle
|
|
targetCircle.center = spots.at (activeSpot).targetPos;
|
|
targetCircle.radius = spots.at (activeSpot).radius;
|
|
targetCircle.setActive (true);
|
|
|
|
// Target point Mouse Over disc
|
|
targetMODisc.center = targetCircle.center;
|
|
targetMODisc.radius = targetCircle.radius;
|
|
targetMODisc.setActive (true);
|
|
|
|
// Source point Icon
|
|
sourceIcon.position = spots.at (activeSpot).sourcePos;
|
|
sourceIcon.setActive (true);
|
|
|
|
// Source point circle
|
|
sourceCircle.center = spots.at (activeSpot).sourcePos;
|
|
sourceCircle.radius = spots.at (activeSpot).radius;
|
|
sourceCircle.setActive (true);
|
|
|
|
// Source point Mouse Over disc
|
|
sourceMODisc.center = sourceCircle.center;
|
|
sourceMODisc.radius = sourceCircle.radius;
|
|
sourceMODisc.setActive (true);
|
|
|
|
// Target point feather circle
|
|
targetFeatherCircle.center = spots.at (activeSpot).targetPos;
|
|
targetFeatherCircle.radius = float (spots.at (activeSpot).radius) * (1.f + spots.at (activeSpot).feather);
|
|
targetFeatherCircle.radiusInImageSpace = true;
|
|
targetFeatherCircle.setActive (true);
|
|
|
|
// Source point feather circle
|
|
sourceFeatherCircle.center = spots.at (activeSpot).sourcePos;
|
|
sourceFeatherCircle.radius = targetFeatherCircle.radius;
|
|
sourceFeatherCircle.setActive (true);
|
|
|
|
// Link line
|
|
PolarCoord p;
|
|
p = targetCircle.center - sourceCircle.center;
|
|
|
|
if (p.radius > sourceCircle.radius + targetCircle.radius) {
|
|
PolarCoord p2 (sourceCircle.radius, p.angle);
|
|
Coord p3;
|
|
p3 = p2;
|
|
link.begin = sourceCircle.center + p3;
|
|
p2.set (targetCircle.radius, p.angle + 180);
|
|
p3 = p2;
|
|
link.end = targetCircle.center + p3;
|
|
link.setActive (true);
|
|
} else {
|
|
link.setActive (false);
|
|
}
|
|
|
|
sourceCircle.setVisible(draggedSide != DraggedSide::SOURCE);
|
|
targetCircle.setVisible(draggedSide != DraggedSide::TARGET);
|
|
link.setVisible(draggedSide == DraggedSide::NONE);
|
|
} else {
|
|
targetCircle.setActive (false);
|
|
targetMODisc.setActive (false);
|
|
sourceIcon.setActive (false);
|
|
sourceCircle.setActive (false);
|
|
sourceMODisc.setActive (false);
|
|
targetFeatherCircle.setActive (false);
|
|
sourceFeatherCircle.setActive (false);
|
|
link.setActive (false);
|
|
}
|
|
|
|
for (size_t i = 0; i < spots.size(); ++i) {
|
|
// Target point icon
|
|
OPIcon* geom = static_cast<OPIcon*> (EditSubscriber::visibleGeometry.at (i));
|
|
geom->position = spots.at (i).targetPos;
|
|
geom->setActive (true);
|
|
|
|
if (int (i) == activeSpot) {
|
|
geom->setHoverable (false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OPIcon *Spot::getActiveSpotIcon()
|
|
{
|
|
if (activeSpot > -1) {
|
|
return static_cast<OPIcon*> (EditSubscriber::visibleGeometry.at (activeSpot));
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void Spot::addNewEntry()
|
|
{
|
|
EditDataProvider* editProvider = getEditProvider();
|
|
// we create a new entry
|
|
SpotEntry se;
|
|
se.targetPos = editProvider->posImage;
|
|
se.sourcePos = se.targetPos;
|
|
spots.push_back (se); // this make a copy of se ...
|
|
activeSpot = spots.size() - 1;
|
|
lastObject = 1;
|
|
|
|
//printf("ActiveSpot = %d\n", activeSpot);
|
|
|
|
createGeometry();
|
|
updateGeometry();
|
|
EditSubscriber::visibleGeometry.at (activeSpot)->state = Geometry::ACTIVE;
|
|
sourceIcon.state = Geometry::DRAGGED;
|
|
// TODO: find a way to disable the active spot's Mouse Over geometry but still displaying its location...
|
|
|
|
if (listener) {
|
|
listener->panelChanged (EvSpotEntryOPA, M ("TP_SPOT_ENTRYCHANGED"));
|
|
}
|
|
}
|
|
|
|
void Spot::deleteSelectedEntry()
|
|
{
|
|
// delete the activeSpot
|
|
if (activeSpot > -1) {
|
|
std::vector<rtengine::procparams::SpotEntry>::iterator i = spots.begin();
|
|
|
|
for (int j = 0; j < activeSpot; ++j) {
|
|
++i;
|
|
}
|
|
|
|
spots.erase (i);
|
|
}
|
|
|
|
lastObject = -1;
|
|
activeSpot = -1;
|
|
|
|
createGeometry();
|
|
updateGeometry();
|
|
|
|
if (listener) {
|
|
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
|
}
|
|
}
|
|
|
|
CursorShape Spot::getCursor (int objectID, int xPos, int yPos) const
|
|
{
|
|
const EditDataProvider* editProvider = getEditProvider();
|
|
if (editProvider && activeSpot > -1) {
|
|
if (draggedSide != DraggedSide::NONE) {
|
|
return CSEmpty;
|
|
}
|
|
|
|
if (objectID == 0 || objectID == 1) {
|
|
return CSMove2D;
|
|
}
|
|
if (objectID >= 2 && objectID <= 5) {
|
|
Coord delta(Coord(xPos, yPos) - ((objectID == 3 || objectID == 5) ? spots.at(activeSpot).sourcePos : spots.at(activeSpot).targetPos));
|
|
PolarCoord polarPos(delta);
|
|
if (polarPos.angle < 0.) {
|
|
polarPos.angle += 180.;
|
|
}
|
|
if (polarPos.angle < 22.5 || polarPos.angle >= 157.5) {
|
|
return CSMove1DH;
|
|
}
|
|
if (polarPos.angle < 67.5) {
|
|
return CSResizeBottomRight;
|
|
}
|
|
if (polarPos.angle < 112.5) {
|
|
return CSMove1DV;
|
|
}
|
|
return CSResizeBottomLeft;
|
|
}
|
|
}
|
|
return CSCrosshair;
|
|
}
|
|
|
|
bool Spot::mouseOver (int modifierKey)
|
|
{
|
|
EditDataProvider* editProvider = getEditProvider();
|
|
|
|
if (editProvider && editProvider->getObject() != lastObject) {
|
|
if (lastObject > -1) {
|
|
if (EditSubscriber::mouseOverGeometry.at (lastObject) == &targetMODisc) {
|
|
getVisibleGeometryFromMO (lastObject)->state = Geometry::ACTIVE;
|
|
} else {
|
|
getVisibleGeometryFromMO (lastObject)->state = Geometry::NORMAL;
|
|
}
|
|
|
|
sourceIcon.state = Geometry::ACTIVE;
|
|
}
|
|
|
|
if (editProvider->getObject() > -1) {
|
|
getVisibleGeometryFromMO (editProvider->getObject())->state = Geometry::PRELIGHT;
|
|
|
|
if (editProvider->getObject() >= STATIC_MO_OBJ_NBR) {
|
|
// a Spot is being edited
|
|
int oldActiveSpot = activeSpot;
|
|
activeSpot = editProvider->getObject() - STATIC_MO_OBJ_NBR;
|
|
|
|
if (activeSpot != oldActiveSpot) {
|
|
if (oldActiveSpot > -1) {
|
|
EditSubscriber::visibleGeometry.at (oldActiveSpot)->state = Geometry::NORMAL;
|
|
EditSubscriber::mouseOverGeometry.at (oldActiveSpot + STATIC_MO_OBJ_NBR)->state = Geometry::NORMAL;
|
|
}
|
|
|
|
EditSubscriber::visibleGeometry.at (activeSpot)->state = Geometry::PRELIGHT;
|
|
EditSubscriber::mouseOverGeometry.at (activeSpot + STATIC_MO_OBJ_NBR)->state = Geometry::PRELIGHT;
|
|
//printf("ActiveSpot = %d (was %d before)\n", activeSpot, oldActiveSpot);
|
|
}
|
|
}
|
|
}
|
|
|
|
lastObject = editProvider->getObject();
|
|
|
|
if (lastObject > -1 && EditSubscriber::mouseOverGeometry.at (lastObject) == getActiveSpotIcon()) {
|
|
lastObject = 0; // targetMODisc
|
|
}
|
|
|
|
updateGeometry();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Create a new Target and Source point or start the drag of a Target point under the cursor
|
|
bool Spot::button1Pressed (int modifierKey)
|
|
{
|
|
EditDataProvider* editProvider = getEditProvider();
|
|
|
|
if (editProvider) {
|
|
if (lastObject == -1 && (modifierKey & GDK_CONTROL_MASK)) {
|
|
draggedSide = DraggedSide::SOURCE;
|
|
addNewEntry();
|
|
EditSubscriber::action = EditSubscriber::Action::DRAGGING;
|
|
return true;
|
|
} else if (lastObject > -1) {
|
|
draggedSide = lastObject == 0 ? DraggedSide::TARGET : lastObject == 1 ? DraggedSide::SOURCE : DraggedSide::NONE;
|
|
getVisibleGeometryFromMO (lastObject)->state = Geometry::DRAGGED;
|
|
EditSubscriber::action = EditSubscriber::Action::DRAGGING;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// End the drag of a Target point
|
|
bool Spot::button1Released()
|
|
{
|
|
Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
|
|
|
|
if (!loGeom) {
|
|
EditSubscriber::action = EditSubscriber::Action::NONE;
|
|
return false;
|
|
}
|
|
|
|
loGeom->state = Geometry::PRELIGHT;
|
|
EditSubscriber::action = EditSubscriber::Action::NONE;
|
|
draggedSide = DraggedSide::NONE;
|
|
updateGeometry();
|
|
return true;
|
|
}
|
|
|
|
// Delete a point
|
|
bool Spot::button2Pressed (int modifierKey)
|
|
{
|
|
EditDataProvider* editProvider = getEditProvider();
|
|
|
|
if (!editProvider || lastObject == -1 || activeSpot == -1) {
|
|
return false;
|
|
}
|
|
|
|
if (! (modifierKey & (GDK_SHIFT_MASK | GDK_SHIFT_MASK))) {
|
|
EditSubscriber::action = EditSubscriber::Action::PICKING;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Create a new Target and Source point or start the drag of a Target point under the cursor
|
|
bool Spot::button3Pressed (int modifierKey)
|
|
{
|
|
EditDataProvider* editProvider = getEditProvider();
|
|
|
|
if (!editProvider || lastObject == -1 || activeSpot == -1) {
|
|
return false;
|
|
}
|
|
|
|
if ((modifierKey & GDK_CONTROL_MASK) && (EditSubscriber::mouseOverGeometry.at (lastObject) == &targetMODisc || lastObject >= STATIC_MO_OBJ_NBR)) {
|
|
lastObject = 1; // sourceMODisc
|
|
sourceIcon.state = Geometry::DRAGGED;
|
|
EditSubscriber::action = EditSubscriber::Action::DRAGGING;
|
|
draggedSide = DraggedSide::SOURCE;
|
|
return true;
|
|
} else if (! (modifierKey & (GDK_SHIFT_MASK | GDK_SHIFT_MASK))) {
|
|
EditSubscriber::action = EditSubscriber::Action::PICKING;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Spot::button3Released()
|
|
{
|
|
Geometry *loGeom = getVisibleGeometryFromMO (lastObject);
|
|
|
|
if (!loGeom) {
|
|
EditSubscriber::action = EditSubscriber::Action::NONE;
|
|
return false;
|
|
}
|
|
|
|
lastObject = -1;
|
|
sourceIcon.state = Geometry::ACTIVE;
|
|
draggedSide = DraggedSide::NONE;
|
|
updateGeometry();
|
|
EditSubscriber::action = EditSubscriber::Action::NONE;
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Spot::drag1 (int modifierKey)
|
|
{
|
|
EditDataProvider *editProvider = getEditProvider();
|
|
int imW, imH;
|
|
editProvider->getImageSize (imW, imH);
|
|
bool modified = false;
|
|
|
|
//printf("Drag1 / LastObject=%d\n", lastObject);
|
|
|
|
Geometry *loGeom = EditSubscriber::mouseOverGeometry.at (lastObject);
|
|
|
|
if (loGeom == &sourceMODisc) {
|
|
//printf("sourceMODisc / deltaPrevImage = %d / %d\n", editProvider->deltaPrevImage.x, editProvider->deltaPrevImage.y);
|
|
rtengine::Coord currPos = spots.at (activeSpot).sourcePos;
|
|
spots.at (activeSpot).sourcePos += editProvider->deltaPrevImage;
|
|
spots.at (activeSpot).sourcePos.clip (imW, imH);
|
|
|
|
if (spots.at (activeSpot).sourcePos != currPos) {
|
|
modified = true;
|
|
}
|
|
|
|
EditSubscriber::mouseOverGeometry.at (activeSpot + STATIC_MO_OBJ_NBR)->state = Geometry::DRAGGED;
|
|
} else if (loGeom == &targetMODisc || lastObject >= STATIC_MO_OBJ_NBR) {
|
|
//printf("targetMODisc / deltaPrevImage = %d / %d\n", editProvider->deltaPrevImage.x, editProvider->deltaPrevImage.y);
|
|
rtengine::Coord currPos = spots.at (activeSpot).targetPos;
|
|
spots.at (activeSpot).targetPos += editProvider->deltaPrevImage;
|
|
spots.at (activeSpot).targetPos.clip (imW, imH);
|
|
|
|
if (spots.at (activeSpot).targetPos != currPos) {
|
|
modified = true;
|
|
}
|
|
} else if (loGeom == &sourceCircle) {
|
|
//printf("sourceCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
|
int lastRadius = spots.at (activeSpot).radius;
|
|
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
|
rtengine::PolarCoord currPolar (currPos - spots.at (activeSpot).sourcePos);
|
|
spots.at (activeSpot).radius = LIM<int> (int (currPolar.radius), SpotParams::minRadius, SpotParams::maxRadius);
|
|
|
|
if (spots.at (activeSpot).radius != lastRadius) {
|
|
modified = true;
|
|
}
|
|
} else if (loGeom == &targetCircle) {
|
|
//printf("targetCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
|
int lastRadius = spots.at (activeSpot).radius;
|
|
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
|
rtengine::PolarCoord currPolar (currPos - spots.at (activeSpot).targetPos);
|
|
spots.at (activeSpot).radius = LIM<int> (int (currPolar.radius), SpotParams::minRadius, SpotParams::maxRadius);
|
|
|
|
if (spots.at (activeSpot).radius != lastRadius) {
|
|
modified = true;
|
|
}
|
|
} else if (loGeom == &sourceFeatherCircle) {
|
|
//printf("sourceFeatherCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
|
float currFeather = spots.at (activeSpot).feather;
|
|
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
|
rtengine::PolarCoord currPolar (currPos - spots.at (activeSpot).sourcePos);
|
|
spots.at (activeSpot).feather = LIM01<float> ((currPolar.radius - double (spots.at (activeSpot).radius)) / double (spots.at (activeSpot).radius));
|
|
|
|
if (spots.at (activeSpot).feather != currFeather) {
|
|
modified = true;
|
|
}
|
|
} else if (loGeom == &targetFeatherCircle) {
|
|
//printf("targetFeatherCircle / deltaPrevImage = %d / %d\n", editProvider->deltaImage.x, editProvider->deltaImage.y);
|
|
float currFeather = spots.at (activeSpot).feather;
|
|
rtengine::Coord currPos = editProvider->posImage + editProvider->deltaImage;
|
|
rtengine::PolarCoord currPolar (currPos - spots.at (activeSpot).targetPos);
|
|
spots.at (activeSpot).feather = LIM01<float> ((currPolar.radius - double (spots.at (activeSpot).radius)) / double (spots.at (activeSpot).radius));
|
|
|
|
if (spots.at (activeSpot).feather != currFeather) {
|
|
modified = true;
|
|
}
|
|
}
|
|
|
|
if (listener && modified) {
|
|
updateGeometry();
|
|
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
|
}
|
|
|
|
return modified;
|
|
}
|
|
|
|
bool Spot::drag3 (int modifierKey)
|
|
{
|
|
EditDataProvider *editProvider = getEditProvider();
|
|
int imW, imH;
|
|
editProvider->getImageSize (imW, imH);
|
|
bool modified = false;
|
|
|
|
Geometry *loGeom = EditSubscriber::mouseOverGeometry.at (lastObject);
|
|
|
|
if (loGeom == &sourceMODisc) {
|
|
rtengine::Coord currPos = spots.at (activeSpot).sourcePos;
|
|
spots.at (activeSpot).sourcePos += editProvider->deltaPrevImage;
|
|
spots.at (activeSpot).sourcePos.clip (imW, imH);
|
|
|
|
if (spots.at (activeSpot).sourcePos != currPos) {
|
|
modified = true;
|
|
}
|
|
}
|
|
|
|
if (listener) {
|
|
updateGeometry();
|
|
listener->panelChanged (EvSpotEntry, M ("TP_SPOT_ENTRYCHANGED"));
|
|
}
|
|
|
|
return modified;
|
|
}
|
|
|
|
bool Spot::pick2 (bool picked)
|
|
{
|
|
return pick3 (picked);
|
|
}
|
|
|
|
bool Spot::pick3 (bool picked)
|
|
{
|
|
EditDataProvider* editProvider = getEditProvider();
|
|
|
|
if (!picked) {
|
|
if (editProvider->getObject() != lastObject) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Object is picked, we delete it
|
|
deleteSelectedEntry();
|
|
EditSubscriber::action = EditSubscriber::Action::NONE;
|
|
updateGeometry();
|
|
return true;
|
|
}
|
|
|
|
|
|
void Spot::switchOffEditMode ()
|
|
{
|
|
if (edit->get_active()) {
|
|
// switching off the toggle button
|
|
bool wasBlocked = editConn.block (true);
|
|
edit->set_active (false);
|
|
|
|
if (!wasBlocked) {
|
|
editConn.block (false);
|
|
}
|
|
}
|
|
|
|
EditSubscriber::switchOffEditMode(); // disconnect
|
|
listener->unsetTweakOperator(this);
|
|
listener->refreshPreview(EvSpotEnabled); // reprocess the preview w/o creating History entry
|
|
}
|
|
|
|
void Spot::tweakParams(procparams::ProcParams& pparams)
|
|
{
|
|
//params->raw.bayersensor.method = RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::FAST);
|
|
//params->raw.xtranssensor.method = RAWParams::XTransSensor::getMethodString(RAWParams::XTransSensor::Method::FAST);
|
|
|
|
// -> disabling all transform
|
|
//pparams.coarse = CoarseTransformParams();
|
|
pparams.lensProf = LensProfParams();
|
|
pparams.cacorrection = CACorrParams();
|
|
pparams.distortion = DistortionParams();
|
|
pparams.rotate = RotateParams();
|
|
pparams.perspective = PerspectiveParams();
|
|
pparams.vignetting = VignettingParams();
|
|
|
|
// -> disabling standard crop
|
|
pparams.crop.enabled = false;
|
|
|
|
// -> disabling time consuming and unnecessary tool
|
|
pparams.sh.enabled = false;
|
|
pparams.blackwhite.enabled = false;
|
|
pparams.dehaze.enabled = false;
|
|
pparams.wavelet.enabled = false;
|
|
pparams.filmSimulation.enabled = false;
|
|
pparams.sharpenEdge.enabled = false;
|
|
pparams.sharpenMicro.enabled = false;
|
|
pparams.sharpening.enabled = false;
|
|
pparams.softlight.enabled = false;
|
|
pparams.gradient.enabled = false;
|
|
pparams.pcvignette.enabled = false;
|
|
pparams.colorappearance.enabled = false;
|
|
}
|