diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 753fd1e41..467b622ec 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -34,6 +34,7 @@ set(NONCLISOURCEFILES colorappearance.cc coloredbar.cc colortoning.cc + controlspotpanel.cc coordinateadjuster.cc crop.cc crophandler.cc diff --git a/rtgui/controlspotpanel.cc b/rtgui/controlspotpanel.cc new file mode 100644 index 000000000..60c5f3d16 --- /dev/null +++ b/rtgui/controlspotpanel.cc @@ -0,0 +1,1114 @@ +/* + * This file is part of RawTherapee. + */ + +#include "../rtengine/rt_math.h" +#include "controlspotpanel.h" +#include "multilangmgr.h" +#include + +using namespace rtengine; + +//----------------------------------------------------------------------------- +// ControlSpotPanel +//----------------------------------------------------------------------------- + +ControlSpotPanel::ControlSpotPanel(): + EditSubscriber(ET_OBJECTS), + + button_add_ ("Add"), + button_delete_ ("Delete"), + button_rename_ ("Rename"), + + shape_ (Gtk::manage (new MyComboBoxText ())), + spotMethod_ (Gtk::manage (new MyComboBoxText ())), + shapeMethod_ (Gtk::manage (new MyComboBoxText ())), + qualityMethod_ (Gtk::manage (new MyComboBoxText ())), + + locX_ (Gtk::manage (new Adjuster (M ("TP_LOCAL_WIDTH"), 0, 2250, 1, 250))), + locXL_ (Gtk::manage (new Adjuster (M ("TP_LOCAL_WIDTH_L"), 0, 2250, 1, 250))), + locY_ (Gtk::manage (new Adjuster (M ("TP_LOCAL_HEIGHT"), 0, 2250, 1, 250))), + locYT_ (Gtk::manage (new Adjuster (M ("TP_LOCAL_HEIGHT_T"), 0, 2250, 1, 250))), + centerX_ (Gtk::manage (new Adjuster (M ("TP_LOCALLAB_CENTER_X"), -1000, 1000, 1, 0))), + centerY_ (Gtk::manage (new Adjuster (M ("TP_LOCALLAB_CENTER_Y"), -1000, 1000, 1, 0))), + circrad_ (Gtk::manage (new Adjuster (M ("TP_LOCALLAB_CIRCRADIUS"), 2, 150, 1, 18))), + transit_ (Gtk::manage (new Adjuster (M ("TP_LOCALLAB_TRANSIT"), 5, 95, 1, 60))), + thresh_ (Gtk::manage (new Adjuster (M ("TP_LOCALLAB_THRES"), 1, 35, 1, 18))), + iter_ (Gtk::manage (new Adjuster (M ("TP_LOCALLAB_PROXI"), 0, 60, 1, 0))), + + lastObject_ (-1), + lastCoord_ (new Coord ()) +{ + treeview_.set_grid_lines (Gtk::TREE_VIEW_GRID_LINES_VERTICAL); + + scrolledwindow_.add (treeview_); + scrolledwindow_.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + scrolledwindow_.set_min_content_height(150); + + pack_start (buttonbox_); + pack_start (scrolledwindow_); + + buttonbox_.pack_start(button_add_, Gtk::PACK_SHRINK, 4); + buttonbox_.pack_start(button_delete_, Gtk::PACK_SHRINK, 4); + buttonbox_.pack_start(button_rename_); + buttonbox_.set_layout(Gtk::BUTTONBOX_START); + + button_add_.signal_clicked().connect ( + sigc::mem_fun (*this, &ControlSpotPanel::on_button_add)); + button_delete_.signal_clicked().connect ( + sigc::mem_fun (*this, &ControlSpotPanel::on_button_delete)); + button_rename_.signal_clicked().connect ( + sigc::mem_fun (*this, &ControlSpotPanel::on_button_rename)); + + treemodel_ = Gtk::ListStore::create (spots_); + + treeview_.set_model (treemodel_); + treeview_.get_selection()->signal_changed().connect( + sigc::mem_fun ( + *this, &ControlSpotPanel::controlspotChanged)); + + auto cell = Gtk::manage (new Gtk::CellRendererText()); + int cols_count = treeview_.append_column ("ID", *cell); + auto col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func ( + *cell, sigc::mem_fun ( + *this, &ControlSpotPanel::render_id)); + } + + cell = Gtk::manage (new Gtk::CellRendererText()); + cols_count = treeview_.append_column("Name", *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func ( + *cell, sigc::mem_fun ( + *this, &ControlSpotPanel::render_name)); + } + + cell = Gtk::manage (new Gtk::CellRendererText()); + cols_count = treeview_.append_column("Status", *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func ( + *cell, sigc::mem_fun ( + *this, &ControlSpotPanel::render_isvisible)); + } + + // TODO Reload saved control spots (don't forget autosize) + + // TODO Rectangle + + Gtk::HBox* const ctboxshape = Gtk::manage (new Gtk::HBox ()); + Gtk::Label* const labelshape = Gtk::manage (new Gtk::Label (M ("TP_LOCALLAB_SHAPETYPE") + ":")); + ctboxshape->pack_start (*labelshape, Gtk::PACK_SHRINK, 4); + shape_->append (M ("TP_LOCALLAB_ELI")); + shape_->append (M ("TP_LOCALLAB_RECT")); + shape_->set_active (0); + shapeconn_ = shape_->signal_changed ().connect ( + sigc::mem_fun ( + *this, &ControlSpotPanel::shapeChanged)); + ctboxshape->pack_start (*shape_); + pack_start (*ctboxshape); + + Gtk::HBox* const ctboxspotmethod = Gtk::manage (new Gtk::HBox ()); + Gtk::Label* const labelspotmethod = Gtk::manage (new Gtk::Label (M ("TP_LOCALLAB_EXCLUTYPE") + ":")); + ctboxspotmethod->pack_start (*labelspotmethod, Gtk::PACK_SHRINK, 4); + ctboxspotmethod->set_tooltip_markup (M ("TP_LOCALLAB_EXCLUTYPE_TOOLTIP")); + spotMethod_->append (M ("TP_LOCALLAB_EXNORM")); + spotMethod_->append (M ("TP_LOCALLAB_EXECLU")); + spotMethod_->set_active (0); + spotMethodconn_ = spotMethod_->signal_changed ().connect ( + sigc::mem_fun ( + *this, &ControlSpotPanel::save_ControlSpot_param)); + ctboxspotmethod->pack_start(*spotMethod_); + pack_start(*ctboxspotmethod); + + Gtk::HBox* const ctboxshapemethod = Gtk::manage (new Gtk::HBox ()); + Gtk::Label* const labelshapemethod = Gtk::manage (new Gtk::Label (M ("TP_LOCALLAB_STYPE") + ":")); + ctboxshapemethod->pack_start (*labelshapemethod, Gtk::PACK_SHRINK, 4); + ctboxshapemethod->set_tooltip_markup (M ("TP_LOCALLAB_STYPE_TOOLTIP")); + shapeMethod_->append (M ("TP_LOCALLAB_IND")); + shapeMethod_->append (M ("TP_LOCALLAB_SYM")); + shapeMethod_->append (M ("TP_LOCALLAB_INDSL")); + shapeMethod_->append (M ("TP_LOCALLAB_SYMSL")); + shapeMethod_->set_active (0); + shapeMethodconn_ = shapeMethod_->signal_changed ().connect ( + sigc::mem_fun ( + *this, &ControlSpotPanel::shapeMethodeChanged)); + ctboxshapemethod->pack_start (*shapeMethod_); + pack_start (*ctboxshapemethod); + + pack_start (*locX_); + locX_->setAdjusterListener (this); + + pack_start (*locXL_); + locXL_->setAdjusterListener (this); + + pack_start (*locY_); + locY_->setAdjusterListener (this); + + pack_start (*locYT_); + locYT_->setAdjusterListener (this); + + pack_start (*centerX_); + centerX_->setAdjusterListener (this); + + pack_start (*centerY_); + centerY_->setAdjusterListener (this); + + pack_start (*circrad_); + circrad_->setAdjusterListener (this); + + Gtk::HBox* const ctboxqualitymethod = Gtk::manage (new Gtk::HBox ()); + Gtk::Label* const labelqualitymethod = Gtk::manage (new Gtk::Label (M ("TP_LOCALLAB_QUAL_METHOD") + ":")); + ctboxqualitymethod->pack_start (*labelqualitymethod, Gtk::PACK_SHRINK, 4); + ctboxqualitymethod->set_tooltip_markup(M("TP_LOCALLAB_METHOD_TOOLTIP")); + qualityMethod_->append (M ("TP_LOCALLAB_STD")); + qualityMethod_->append (M ("TP_LOCALLAB_ENH")); + qualityMethod_->append (M ("TP_LOCALLAB_ENHDEN")); + qualityMethod_->set_active(0); + qualityMethodconn_ = qualityMethod_->signal_changed ().connect ( + sigc::mem_fun ( + *this, &ControlSpotPanel::save_ControlSpot_param)); + ctboxqualitymethod->pack_start (*qualityMethod_); + pack_start (*ctboxqualitymethod); + + pack_start (*transit_); + transit_->set_tooltip_text (M ("TP_LOCALLAB_TRANSIT_TOOLTIP")); + transit_->setAdjusterListener (this); + + Gtk::Frame* const artifFrame = Gtk::manage (new Gtk::Frame (M ("TP_LOCALLAB_ARTIF"))); + artifFrame->set_label_align (0.025, 0.5); + artifFrame->set_tooltip_text (M ("TP_LOCALLAB_ARTIF_TOOLTIP")); + ToolParamBlock* const artifBox = Gtk::manage (new ToolParamBlock ()); + artifBox->pack_start (*thresh_); + thresh_->setAdjusterListener (this); + artifBox->pack_start (*iter_); + iter_->setAdjusterListener (this); + artifFrame->add (*artifBox); + pack_start (*artifFrame); + + // Set param widgets sensitive if there is at least one control spot + auto s = treeview_.get_selection(); + if (!s->count_selected_rows ()) { + setParamEditable (false); + } else { + setParamEditable (true); + } + + show_all (); +} + +void ControlSpotPanel::setEditProvider (EditDataProvider* provider) +{ + EditSubscriber::setEditProvider (provider); +} + +namespace +{ + +template +Glib::ustring to_str (V n, int precision = 1) +{ + std::ostringstream buf; + buf << std::setprecision (precision) << std::fixed << n; + return buf.str (); +} + +} // namespace + +void ControlSpotPanel::render_id ( + Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter) +{ + auto row = *iter; + Gtk::CellRendererText *ct = static_cast (cell); + int value = row[spots_.id]; + ct->property_text() = to_str (value); +} + +void ControlSpotPanel::render_name ( + Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter) +{ + auto row = *iter; + Gtk::CellRendererText *ct = static_cast (cell); + auto value = row[spots_.name]; + ct->property_text() = value; +} + +void ControlSpotPanel::render_isvisible ( + Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter) +{ + auto row = *iter; + Gtk::CellRendererText *ct = static_cast (cell); + auto value = row[spots_.isvisible]; + if (value) { + ct->property_text () = "Visible"; + } else { + ct->property_text () = "Not visible"; + } +} + +void ControlSpotPanel::on_button_add () +{ + printf("on_button_add\n"); + // Looking for maximum used id + int max_row_id = 0; + Gtk::TreeModel::Children children = treemodel_->children (); + Gtk::TreeModel::Children::iterator iter; + for (iter = children.begin (); iter != children.end (); iter++) + { + Gtk::TreeModel::Row row = *iter; + int iter_id = row[spots_.id]; + max_row_id = std::max (max_row_id, iter_id); + } + + // Adding row + Gtk::TreeModel::Row row = * (treemodel_->append ()); + row[spots_.id] = max_row_id + 1; + row[spots_.name] = "Control Spot #" + to_str(row[spots_.id]); + row[spots_.isvisible] = true; + row[spots_.curveid] = 0; // No associated curve + row[spots_.shape] = 0; + row[spots_.spotMethod] = 0; + row[spots_.shapeMethod] = 2; + row[spots_.locX] = 250; + row[spots_.locXL] = 250; + row[spots_.locY] = 250; + row[spots_.locYT] = 250; + row[spots_.centerX] = 0; + row[spots_.centerY] = 0; + row[spots_.circrad] = 18; + row[spots_.qualityMethod] = 0; + row[spots_.transit] = 60; + row[spots_.thresh] = 18; + row[spots_.iter] = 0; + setParamEditable (true); + + // Select newly added row + treeview_.set_cursor (treemodel_->get_path (row)); + + // Add associated control spot curve + addControlSpotCurve (row); + updateControlSpotCurve (row); + subscribe (); +} + +void ControlSpotPanel::on_button_delete () +{ + auto s = treeview_.get_selection (); + if (!s->count_selected_rows ()) { + return; + } + auto iter = s->get_selected (); + Gtk::TreeModel::Row row = *iter; + deleteControlSpotCurve (row); + treemodel_->erase (iter); + + // Set param widgets unsensitive and unsubscribe if there is no more control spot + s = treeview_.get_selection (); + if (!s->count_selected_rows ()) { + unsubscribe (); + setParamEditable (false); + } +} + +void ControlSpotPanel::on_button_rename () +{ + // Get actual control spot name + const auto s = treeview_.get_selection (); + if (!s->count_selected_rows ()) { + return; + } + const auto iter = s->get_selected (); + const Gtk::TreeModel::Row row = *iter; + const Glib::ustring actualname = row[spots_.name]; + + RenameDialog d (actualname, + static_cast (*get_toplevel ())); + int status = d.run (); + + if (status == 1) { + const auto newname = d.get_new_name (); + row[spots_.name] = newname; + } + + treeview_.columns_autosize (); +} + +void ControlSpotPanel::save_ControlSpot_param () +{ + printf("save_ControlSpot_param\n"); + // Get selected control spot + const auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + const auto iter = s->get_selected(); + const Gtk::TreeModel::Row row = *iter; + + // Save param in selected control spot + row[spots_.shape] = shape_->get_active_row_number (); + row[spots_.spotMethod] = spotMethod_->get_active_row_number (); + row[spots_.shapeMethod] = shapeMethod_->get_active_row_number (); + row[spots_.locX] = static_cast (locX_->getValue ()); + row[spots_.locXL] = static_cast (locXL_->getValue ()); + row[spots_.locY] = static_cast (locY_->getValue ()); + row[spots_.locYT] = static_cast (locYT_->getValue ()); + row[spots_.centerX] = static_cast (centerX_->getValue ()); + row[spots_.centerY] = static_cast (centerY_->getValue ()); + row[spots_.circrad] = static_cast (circrad_->getValue ()); + row[spots_.qualityMethod] = qualityMethod_->get_active_row_number (); + row[spots_.transit] = static_cast (transit_->getValue ()); + row[spots_.thresh] = static_cast (thresh_->getValue ()); + row[spots_.iter] = static_cast (iter_->getValue ()); +} + +void ControlSpotPanel::load_ControlSpot_param() +{ + printf("load_ControlSpot_param\n"); + // Get selected control spot + const auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + const auto iter = s->get_selected(); + const Gtk::TreeModel::Row row = *iter; + + // Listener are deactivated to avoid unexpected even during param load + disableParamlistener (true); + + // Load param in selected control spot + shape_->set_active (row[spots_.shape]); + spotMethod_->set_active (row[spots_.spotMethod]); + shapeMethod_->set_active (row[spots_.shapeMethod]); + locX_->setValue (static_cast (row[spots_.locX])); + locXL_->setValue (static_cast (row[spots_.locXL])); + locY_->setValue (static_cast (row[spots_.locY])); + locYT_->setValue (static_cast (row[spots_.locYT])); + centerX_->setValue (static_cast (row[spots_.centerX])); + centerY_->setValue (static_cast (row[spots_.centerY])); + circrad_->setValue (static_cast (row[spots_.circrad])); + qualityMethod_->set_active (row[spots_.qualityMethod]); + transit_->setValue (static_cast (row[spots_.transit])); + thresh_->setValue (static_cast (row[spots_.thresh])); + iter_->setValue (static_cast (row[spots_.iter])); + + // Listener are reactivated + disableParamlistener (false); + + updateParamVisibility (); +} + +void ControlSpotPanel::controlspotChanged () +{ + printf("controlspotChanged\n"); + load_ControlSpot_param(); +} + +void ControlSpotPanel::shapeChanged () +{ + save_ControlSpot_param(); + + printf("shapeChanged\n"); + const auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + updateControlSpotCurve (row); +} + +void ControlSpotPanel::shapeMethodeChanged () +{ + printf("shapeMethodeChanged\n"); + const int method = shapeMethod_->get_active_row_number (); + + if (method == 1 || method == 3) { // Symmetrical cases + locXL_->setValue (locX_->getValue ()); + locYT_->setValue (locY_->getValue ()); + + // Update associated control spot curve + const auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + save_ControlSpot_param (); + updateControlSpotCurve (row); + } else { + save_ControlSpot_param (); + } + + updateParamVisibility (); +} + +void ControlSpotPanel::updateParamVisibility () +{ + printf("updateParamVisibility\n"); + const int method = shapeMethod_->get_active_row_number (); + + if (method == 1 || method == 3) { // Symmetrical cases + locXL_->hide (); + locYT_->hide (); + if (method == 1) { // 1 = Symmetrical (mouse) + locX_->hide (); + locY_->hide (); + centerX_->hide (); + centerY_->hide (); + } else { // 3 = Symmetrical (mouse + sliders) + locX_->show (); + locY_->show (); + centerX_->show (); + centerY_->show (); + } + } else { // Independent cases + if (method == 0) { // 0 = Independent (mouse) + locX_->hide (); + locXL_->hide (); + locY_->hide (); + locYT_->hide (); + centerX_->hide (); + centerY_->hide (); + } else { // 2 = Independent (mouse + sliders) + locX_->show (); + locXL_->show (); + locY_->show (); + locYT_->show (); + centerX_->show (); + centerY_->show (); + } + } +} + +void ControlSpotPanel::adjusterChanged(Adjuster* a, double newval) +{ + printf("adjusterChanged\n"); + const int method = shapeMethod_->get_active_row_number (); + if (method == 1 || method == 3) { // Symmetrical cases + locXL_->setValue (locX_->getValue ()); + locYT_->setValue (locY_->getValue ()); + } + + save_ControlSpot_param(); + + // Update associated control spot curve + const auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return; + } + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + updateControlSpotCurve (row); +} + +void ControlSpotPanel::disableParamlistener(bool cond) +{ + printf("disableParamlistener: %d\n", cond); + shapeconn_.block(cond); + spotMethodconn_.block(cond); + shapeMethodconn_.block(cond); + locX_->block(cond); + locXL_->block(cond); + locY_->block(cond); + locYT_->block(cond); + centerX_->block(cond); + centerY_->block(cond); + circrad_->block(cond); + qualityMethodconn_.block(cond); + transit_->block(cond); + thresh_->block(cond); + iter_->block(cond); +} + +void ControlSpotPanel::setParamEditable(bool cond) +{ + printf("setParamEditable: %d\n", cond); + shape_->set_sensitive(cond); + spotMethod_->set_sensitive(cond); + shapeMethod_->set_sensitive(cond); + locX_->set_sensitive(cond); + locXL_->set_sensitive(cond); + locY_->set_sensitive(cond); + locYT_->set_sensitive(cond); + centerX_->set_sensitive(cond); + centerY_->set_sensitive(cond); + circrad_->set_sensitive(cond); + qualityMethod_->set_sensitive(cond); + transit_->set_sensitive(cond); + thresh_->set_sensitive(cond); + iter_->set_sensitive(cond); +} + +void ControlSpotPanel::addControlSpotCurve (Gtk::TreeModel::Row row) +{ + printf("addControlSpotCurve\n"); + if (row[spots_.curveid] > 0) { // Row has already an associated curve + return; + } + + // Creation of visibleGeometry + Line* lineX; + lineX = new Line (); + lineX->innerLineWidth = 2.5; + lineX->datum = Geometry::IMAGE; + Line* lineXL; + lineXL = new Line (); + lineXL->innerLineWidth = 2.5; + lineXL->datum = Geometry::IMAGE; + Line* lineY; + lineY = new Line (); + lineY->innerLineWidth = 2.5; + lineY->datum = Geometry::IMAGE; + Line* lineYT; + lineYT = new Line (); + lineYT->innerLineWidth = 2.5; + lineYT->datum = Geometry::IMAGE; + Circle* centerCircle; + centerCircle = new Circle (); + centerCircle->datum = Geometry::IMAGE; + centerCircle->radiusInImageSpace = true; + Arcellipse* arc1; + arc1 = new Arcellipse (); + arc1->innerLineWidth = 0.7; + arc1->datum = Geometry::IMAGE; + arc1->radiusInImageSpace = true; + Arcellipse* arc2; + arc2 = new Arcellipse (); + arc2->innerLineWidth = 0.7; + arc2->datum = Geometry::IMAGE; + arc2->radiusInImageSpace = true; + Arcellipse* arc3; + arc3 = new Arcellipse (); + arc3->innerLineWidth = 0.7; + arc3->datum = Geometry::IMAGE; + arc3->radiusInImageSpace = true; + Arcellipse* arc4; + arc4 = new Arcellipse (); + arc4->innerLineWidth = 0.7; + arc4->datum = Geometry::IMAGE; + arc4->radiusInImageSpace = true; + Rectangle* rec; + rec = new Rectangle (); + rec->innerLineWidth = 0.7; + rec->datum = Geometry::IMAGE; + EditSubscriber::visibleGeometry.push_back(lineX); // (curveid - 1) * 10 + EditSubscriber::visibleGeometry.push_back(lineXL); // (curveid - 1) * 10 + 1 + EditSubscriber::visibleGeometry.push_back(lineY); // (curveid - 1) * 10 + 2 + EditSubscriber::visibleGeometry.push_back(lineYT); // (curveid - 1) * 10 + 3 + EditSubscriber::visibleGeometry.push_back(centerCircle); // (curveid - 1) * 10 + 4 + EditSubscriber::visibleGeometry.push_back(arc1); // (curveid - 1) * 10 + 5 + EditSubscriber::visibleGeometry.push_back(arc2); // (curveid - 1) * 10 + 6 + EditSubscriber::visibleGeometry.push_back(arc3); // (curveid - 1) * 10 + 7 + EditSubscriber::visibleGeometry.push_back(arc4); // (curveid - 1) * 10 + 8 + EditSubscriber::visibleGeometry.push_back(rec); // (curveid - 1) * 10 + 9 + + // Creation of mouseOverGeometry + lineX = new Line (); + lineX->innerLineWidth = 2.5; + lineX->datum = Geometry::IMAGE; + lineXL = new Line (); + lineXL->innerLineWidth = 2.5; + lineXL->datum = Geometry::IMAGE; + lineY = new Line (); + lineY->innerLineWidth = 2.5; + lineY->datum = Geometry::IMAGE; + lineYT = new Line (); + lineYT->innerLineWidth = 2.5; + lineYT->datum = Geometry::IMAGE; + centerCircle = new Circle (); + centerCircle->datum = Geometry::IMAGE; + centerCircle->radiusInImageSpace = true; + arc1 = new Arcellipse (); + arc1->innerLineWidth = 0.7; + arc1->datum = Geometry::IMAGE; + arc1->radiusInImageSpace = true; + arc2 = new Arcellipse (); + arc2->innerLineWidth = 0.7; + arc2->datum = Geometry::IMAGE; + arc2->radiusInImageSpace = true; + arc3 = new Arcellipse (); + arc3->innerLineWidth = 0.7; + arc3->datum = Geometry::IMAGE; + arc3->radiusInImageSpace = true; + arc4 = new Arcellipse (); + arc4->innerLineWidth = 0.7; + arc4->datum = Geometry::IMAGE; + arc4->radiusInImageSpace = true; + rec = new Rectangle (); + rec->innerLineWidth = 0.7; + rec->datum = Geometry::IMAGE; + EditSubscriber::mouseOverGeometry.push_back(lineX); // (curveid - 1) * 10 + EditSubscriber::mouseOverGeometry.push_back(lineXL); // (curveid - 1) * 10 + 1 + EditSubscriber::mouseOverGeometry.push_back(lineY); // (curveid - 1) * 10 + 2 + EditSubscriber::mouseOverGeometry.push_back(lineYT); // (curveid - 1) * 10 + 3 + EditSubscriber::mouseOverGeometry.push_back(centerCircle); // (curveid - 1) * 10 + 4 + EditSubscriber::mouseOverGeometry.push_back(arc1); // (curveid - 1) * 10 + 5 + EditSubscriber::mouseOverGeometry.push_back(arc2); // (curveid - 1) * 10 + 6 + EditSubscriber::mouseOverGeometry.push_back(arc3); // (curveid - 1) * 10 + 7 + EditSubscriber::mouseOverGeometry.push_back(arc4); // (curveid - 1) * 10 + 8 + EditSubscriber::mouseOverGeometry.push_back(rec); // (curveid - 1) * 10 + 9 + + row[spots_.curveid] = EditSubscriber::visibleGeometry.size () / 10; +} + +void ControlSpotPanel::updateControlSpotCurve (Gtk::TreeModel::Row row) +{ + const int curveid_ = static_cast (row[spots_.curveid]); + if (curveid_ == 0) { // Row has no associated curve + return; + } + const int centerX_ = static_cast (row[spots_.centerX]); + const int centerY_ = static_cast (row[spots_.centerY]); + const int circrad_ = static_cast (row[spots_.circrad]); + const int locX_ = static_cast (row[spots_.locX]); + const int locXL_ = static_cast (row[spots_.locXL]); + const int locY_ = static_cast (row[spots_.locY]); + const int locYT_ = static_cast (row[spots_.locYT]); + const int shape_ = static_cast (row[spots_.shape]); + + printf("updateControlSpotCurve: %d\n", curveid_); + + EditDataProvider* dataProvider = getEditProvider(); + + if (!dataProvider) { + return; + } + int imW = 0; + int imH = 0; + dataProvider->getImageSize(imW, imH); + if (!imW || !imH) { + return; + } + + const double decayX = (locX_) * (double (imW)) / 2000.; + const double decayXL = (locXL_) * (double (imW)) / 2000.; + const double decayY = (locY_) * double (imH) / 2000.; + const double decayYT = (locYT_) * double (imH) / 2000.; + rtengine::Coord origin (imW / 2 + centerX_ * imW / 2000.f, imH / 2 + centerY_ * imH / 2000.f); + + const auto updateLineWithDecay = [&](Geometry * geometry, const float radius, const float decal, const float offSetAngle, const double decay) { + const auto line = static_cast(geometry); // 180 + line->begin = PolarCoord (radius, decal) + PolarCoord (decay, offSetAngle); + line->begin += origin; // 0 + line->end = PolarCoord(radius, decal - 180) + PolarCoord(decay, offSetAngle); + line->end += origin; + }; + + const auto updateCircle = [&](Geometry * geometry) { + const auto circle = static_cast (geometry); + circle->center = origin; + circle->radius = circrad_; + }; + + const auto updateArcellipse = [&](Geometry * geometry, const double dRad_, const double dRad2_, const double begang_, const double endang_) { + const auto arcellipse = static_cast (geometry); + arcellipse->center = origin; + arcellipse->begang = begang_; + arcellipse->endang = endang_; + arcellipse->radius = dRad_; + arcellipse->radius2 = dRad2_; + }; + + const auto updateRectangle = [&](Geometry * geometry) { + const auto rectangle = static_cast (geometry); + rectangle->bottomRight.x = origin.x + (int) decayX; + rectangle->bottomRight.y = origin.y + (int) decayY; + rectangle->topLeft.x = origin.x - (int) decayXL; + rectangle->topLeft.y = origin.y - (int) decayYT; + }; + + updateLineWithDecay(visibleGeometry.at((curveid_ - 1) * 10), 500., 90., 0., decayX); + updateLineWithDecay(mouseOverGeometry.at((curveid_ - 1) * 10), 500., 90., 0., decayX); + + updateLineWithDecay(visibleGeometry.at((curveid_ - 1) * 10 + 1), 500., 90., 180., decayXL); + updateLineWithDecay(mouseOverGeometry.at((curveid_ - 1) * 10 + 1), 500., 90., 180., decayXL); + + updateLineWithDecay(visibleGeometry.at((curveid_ - 1) * 10 + 2), 500., 180., 90., decayY); + updateLineWithDecay(mouseOverGeometry.at((curveid_ - 1) * 10 + 2), 500., 180., 90., decayY); + + updateLineWithDecay(visibleGeometry.at((curveid_ - 1) * 10 + 3), 500., 180., 270., decayYT); + updateLineWithDecay(mouseOverGeometry.at((curveid_ - 1) * 10 + 3), 500., 180., 270., decayYT); + + updateCircle(visibleGeometry.at((curveid_ - 1) * 10 + 4)); + updateCircle(mouseOverGeometry.at((curveid_ - 1) * 10 + 4)); + + updateArcellipse(visibleGeometry.at((curveid_ - 1) * 10 + 5), decayX, decayYT, 3*RT_PI_2, 2 * RT_PI); + updateArcellipse(visibleGeometry.at((curveid_ - 1) * 10 + 6), decayXL, decayYT, RT_PI, 3*RT_PI_2); + updateArcellipse(visibleGeometry.at((curveid_ - 1) * 10 + 7), decayXL, decayY, RT_PI_2, RT_PI); + updateArcellipse(visibleGeometry.at((curveid_ - 1) * 10 + 8), decayX, decayY, 0., RT_PI_2); + updateArcellipse(mouseOverGeometry.at((curveid_ - 1) * 10 + 5), decayX, decayYT, 3*RT_PI_2, 2 * RT_PI); + updateArcellipse(mouseOverGeometry.at((curveid_ - 1) * 10 + 6), decayXL, decayYT, RT_PI, 3*RT_PI_2); + updateArcellipse(mouseOverGeometry.at((curveid_ - 1) * 10 + 7), decayXL, decayY, RT_PI_2, RT_PI); + updateArcellipse(mouseOverGeometry.at((curveid_ - 1) * 10 + 8), decayX, decayY, 0., RT_PI_2); + + updateRectangle(visibleGeometry.at((curveid_ - 1) * 10 + 9)); + updateRectangle(mouseOverGeometry.at((curveid_ - 1) * 10 + 9)); + + // Update Arcellipse/Rectangle visibility according to shape + if (shape_ == 0) { // 0 = Ellipse + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 5)->setActive(true); // arc1 + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 6)->setActive(true); // arc2 + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 7)->setActive(true); // arc3 + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 8)->setActive(true); // arc4 + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 9)->setActive(false); // rec + + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 5)->setActive(true); // arc1 + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 6)->setActive(true); // arc2 + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 7)->setActive(true); // arc3 + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 8)->setActive(true); // arc4 + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 9)->setActive(false); // rec + } else { // 1 = Rectangle + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 5)->setActive(false); // arc1 + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 6)->setActive(false); // arc2 + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 7)->setActive(false); // arc3 + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 8)->setActive(false); // arc4 + EditSubscriber::visibleGeometry.at((curveid_ - 1) * 10 + 9)->setActive(true); // rec + + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 5)->setActive(false); // arc1 + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 6)->setActive(false); // arc2 + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 7)->setActive(false); // arc3 + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 8)->setActive(false); // arc4 + EditSubscriber::mouseOverGeometry.at((curveid_ - 1) * 10 + 9)->setActive(true); // rec + } +} + +void ControlSpotPanel::deleteControlSpotCurve (Gtk::TreeModel::Row row) +{ + const int curveid_ = static_cast (row[spots_.curveid]); + if (curveid_ == 0) { // Row has no associated curve + return; + } + printf("deleteControlSpotCurve: %d\n", curveid_); + + // visibleGeometry + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 9); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 8); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 7); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 6); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 5); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 4); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 3); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 2); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10 + 1); + EditSubscriber::visibleGeometry.erase(EditSubscriber::visibleGeometry.begin () + (curveid_ - 1) * 10); + + // mouseOverGeometry + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 9); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 8); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 7); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 6); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 5); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 4); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 3); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 2); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10 + 1); + EditSubscriber::mouseOverGeometry.erase(EditSubscriber::mouseOverGeometry.begin () + (curveid_ - 1) * 10); + + row[spots_.curveid] = 0; // Reset associated curve id + + // Reordering curve id + Gtk::TreeModel::Children children = treemodel_->children (); + for (auto iter = children.begin (); iter != children.end (); iter++) { + Gtk::TreeModel::Row r = *iter; + if (r[spots_.curveid] > curveid_) { + r[spots_.curveid] = r[spots_.curveid] - 1; + } + } +} + +CursorShape ControlSpotPanel::getCursor (int objectID) +{ + printf ("Object ID: %d\n", objectID); + int rem_ = objectID % 10; + + switch (rem_) { + case (0): // LocX: (curveid_ - 1) * 10 + return CSMove1DH; + case (1): // LocXL: (curveid_ - 1) * 10 + 1 + return CSMove1DH; + case (2): // LocY: (curveid_ - 1) * 10 + 2 + return CSMove1DV; + case (3): // LocYT: (curveid_ - 1) * 10 + 3 + return CSMove1DV; + case (4): // centerCircle: (curveid_ - 1) * 10 + 4 + return CSMove2D; + case (5): // arc1: (curveid_ - 1) * 10 + 5 + return CSMove2D; + case (6): // arc2: (curveid_ - 1) * 10 + 6 + return CSMove2D; + case (7): // arc3: (curveid_ - 1) * 10 + 7 + return CSMove2D; + case (8): // arc4: (curveid_ - 1) * 10 + 8 + return CSMove2D; + case (9): // rec: (curveid_ - 1) * 10 + 9 + return CSMove2D; + default: + return CSOpenHand; + } +} + +bool ControlSpotPanel::mouseOver (int modifierKey) +{ + EditDataProvider* editProvider_ = getEditProvider(); + if (!editProvider_) { + return false; + } + + int object_ = editProvider_->object; + + if (object_ != lastObject_) { + if (object_ == -1) { + for (int it_ = 0; it_ < (int) EditSubscriber::visibleGeometry.size (); it_++) { + EditSubscriber::visibleGeometry.at(it_)->state = Geometry::NORMAL; + } + lastObject_ = object_; + return false; + } + + int curveId_ = object_ / 10 + 1; + int rem = object_ % 10; + for (int it_ = 0; it_ < (int) EditSubscriber::visibleGeometry.size (); it_++) { + if ((it_ < ((curveId_ - 1) * 10)) || (it_ > ((curveId_ - 1) * 10) + 9)) { // it_ does not belong to cursor pointed curve + EditSubscriber::visibleGeometry.at(it_)->state = Geometry::NORMAL; + } + } + + const int method = shapeMethod_->get_active_row_number (); + + // LocX + if (rem == 0) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10)->state = Geometry::PRELIGHT; + + if (method == 1 || method == 3) { // Symmetrical cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 1)->state = Geometry::PRELIGHT; + } + } else { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10)->state = Geometry::NORMAL; + } + + // LocXL + if (rem == 1) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 1)->state = Geometry::PRELIGHT; + + if (method == 1 || method == 3) { // Symmetrical cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10)->state = Geometry::PRELIGHT; + } + } else { + if (method == 0 || method == 2) { // Independent cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 1)->state = Geometry::NORMAL; + } + } + + // LocY + if (rem == 2) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 2)->state = Geometry::PRELIGHT; + + if (method == 1 || method == 3) { // Symmetrical cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 3)->state = Geometry::PRELIGHT; + } + } else { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 2)->state = Geometry::NORMAL; + } + + // LocYT + if (rem == 3) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 3)->state = Geometry::PRELIGHT; + + if (method == 1 || method == 3) { // Symmetrical cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 2)->state = Geometry::PRELIGHT; + } + } else { + if (method == 0 || method == 2) { // Independent cases + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 3)->state = Geometry::NORMAL; + } + } + + // Circle, Arcellipses and Rectangle + if (rem >= 4) { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 4)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 5)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 6)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 7)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 8)->state = Geometry::PRELIGHT; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 9)->state = Geometry::PRELIGHT; + } else { + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 4)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 5)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 6)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 7)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 8)->state = Geometry::NORMAL; + EditSubscriber::visibleGeometry.at((curveId_ - 1) * 10 + 9)->state = Geometry::NORMAL; + } + + lastObject_ = object_; + return true; + } + + return false; +} + +bool ControlSpotPanel::button1Pressed (int modifierKey) +{ + printf("button1Pressed\n"); + EditDataProvider *provider = getEditProvider (); + if (!provider || lastObject_ == -1) { + return false; + } + + // Select associated control spot + int curveId_ = lastObject_ / 10 + 1; + Gtk::TreeModel::Children children = treemodel_->children (); + for (auto iter = children.begin (); iter != children.end (); iter++) { + Gtk::TreeModel::Row r = *iter; + if (r[spots_.curveid] == curveId_) { + treeview_.set_cursor (treemodel_->get_path (r)); + break; + } + } + + lastCoord_->set (provider->posImage.x + provider->deltaImage.x, provider->posImage.y + provider->deltaImage.y); + EditSubscriber::action = ES_ACTION_DRAGGING; + return true; +} + +bool ControlSpotPanel::button1Released () +{ + printf("button1Released\n"); + EditSubscriber::action = ES_ACTION_NONE; + return true; +} + +bool ControlSpotPanel::drag1 (int modifierKey) +{ + printf("drag1\n"); + + EditDataProvider *provider = getEditProvider (); + if (!provider || lastObject_ == -1) { + return false; + } + + // Get associated control spot + const auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { + return false; + } + const auto iter = s->get_selected(); + Gtk::TreeModel::Row row = *iter; + + int imW, imH; + provider->getImageSize (imW, imH); + int rem = lastObject_ % 10; + int method = shapeMethod_->get_active_row_number (); + Coord* newCoord = new Coord (provider->posImage.x + provider->deltaImage.x, provider->posImage.y + provider->deltaImage.y); + + // LocX + if (rem == 0) { + double deltaX = (double (newCoord->x) - double (lastCoord_->x)) * 2000. / double (imW); + locX_->setValue (locX_->getValue () + deltaX); + if (method == 1 || method == 3) { // Symmetrical cases + locXL_->setValue (locX_->getValue ()); + } + save_ControlSpot_param (); + updateControlSpotCurve (row); + } + + // LocXL + if (rem == 1) { + double deltaXL = (double (lastCoord_->x) - double (newCoord->x)) * 2000. / double (imW); + locXL_->setValue (locXL_->getValue () + deltaXL); + if (method == 1 || method == 3) { // Symmetrical cases + locX_->setValue (locXL_->getValue ()); + } + save_ControlSpot_param (); + updateControlSpotCurve (row); + } + + // LocY + if (rem == 2) { + double deltaY = (double (newCoord->y) - double (lastCoord_->y)) * 2000. / double (imH); + locY_->setValue (locY_->getValue () + deltaY); + if (method == 1 || method == 3) { // Symmetrical cases + locYT_->setValue (locY_->getValue ()); + } + save_ControlSpot_param (); + updateControlSpotCurve (row); + } + + // LocYT + if (rem == 3) { + double deltaYT = (double (lastCoord_->y) - double (newCoord->y)) * 2000. / double (imH); + locYT_->setValue (locYT_->getValue () + deltaYT); + if (method == 1 || method == 3) { // Symmetrical cases + locY_->setValue (locYT_->getValue ()); + } + save_ControlSpot_param (); + updateControlSpotCurve (row); + } + + // Circle, Arcellipses and Rectangle + if (rem >= 4) { + double deltaX = (double (newCoord->x) - double (lastCoord_->x)) * 2000. / double (imW); + double deltaY = (double (newCoord->y) - double (lastCoord_->y)) * 2000. / double (imH); + centerX_->setValue (centerX_->getValue () + deltaX); + centerY_->setValue (centerY_->getValue () + deltaY); + save_ControlSpot_param (); + updateControlSpotCurve (row); + } + + lastCoord_->set (newCoord->x, newCoord->y); + return true; +} + +//----------------------------------------------------------------------------- +// ControlSpots +//----------------------------------------------------------------------------- + +ControlSpotPanel::ControlSpots::ControlSpots() +{ + add (id); + add (name); + add (isvisible); + add (curveid); + add (shape); + add (spotMethod); + add (shapeMethod); + add (locX); + add (locXL); + add (locYT); + add (locY); + add (centerX); + add (centerY); + add (circrad); + add (qualityMethod); + add (transit); + add (thresh); + add (iter); +} + +//----------------------------------------------------------------------------- +// RenameDialog +//----------------------------------------------------------------------------- + +ControlSpotPanel::RenameDialog::RenameDialog(const Glib::ustring &actualname, Gtk::Window &parent): + Gtk::Dialog ("Renaming Control Spot", parent) +{ + Gtk::HBox *hb = Gtk::manage (new Gtk::HBox()); + hb->pack_start (*Gtk::manage (new Gtk::Label ("Enter the new Control Spot name")), false, false, 4); + + newname_.set_text(actualname); + hb->pack_start(newname_); + + get_content_area()->pack_start (*hb, Gtk::PACK_SHRINK, 4); + + add_button (M ("GENERAL_OK"), 1); + add_button (M ("GENERAL_CANCEL"), 2); + + show_all_children(); +} + +Glib::ustring ControlSpotPanel::RenameDialog::get_new_name() +{ + return newname_.get_text(); +} diff --git a/rtgui/controlspotpanel.h b/rtgui/controlspotpanel.h new file mode 100644 index 000000000..d8aa6c068 --- /dev/null +++ b/rtgui/controlspotpanel.h @@ -0,0 +1,131 @@ +/* + * This file is part of RawTherapee. + */ + +#ifndef _CONTROLSPOTPANEL_H_ +#define _CONTROLSPOTPANEL_H_ + +#include "../rtengine/coord.h" +#include "adjuster.h" +#include "edit.h" +#include "guiutils.h" +#include "toolpanel.h" +#include +#include + +class ControlSpotPanel: + public ToolParamBlock, + public AdjusterListener, + public EditSubscriber +{ +public: + ControlSpotPanel(); + void setEditProvider(EditDataProvider* provider); + +private: + // cell renderer + void render_id (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_name (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_isvisible (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + + void on_button_add(); + void on_button_delete(); + void on_button_rename(); + // TODO Add visibility button + // TODO Add duplication button + + void save_ControlSpot_param(); + void load_ControlSpot_param(); + + void controlspotChanged(); + + void shapeChanged(); + void shapeMethodeChanged(); + void updateParamVisibility(); + void adjusterChanged(Adjuster* a, double newval); + void disableParamlistener(bool cond); + void setParamEditable(bool cond); + + void addControlSpotCurve(Gtk::TreeModel::Row row); + void updateControlSpotCurve(Gtk::TreeModel::Row row); + void deleteControlSpotCurve(Gtk::TreeModel::Row row); + CursorShape getCursor(int objectID); + bool mouseOver(int modifierKey); + bool button1Pressed(int modifierKey); + bool button1Released(); + bool drag1(int modifierKey); + + class ControlSpots: + public Gtk::TreeModel::ColumnRecord + { + public: + ControlSpots(); + + Gtk::TreeModelColumn id; // Control spot id + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn isvisible; + Gtk::TreeModelColumn curveid; // Associated curve id + Gtk::TreeModelColumn shape; // 0 = Ellipse, 1 = Rectangle + Gtk::TreeModelColumn spotMethod; // 0 = Normal, 1 = Excluding + Gtk::TreeModelColumn shapeMethod; // 0 = Independent (mouse), 1 = Symmetrical (mouse), 2 = Independent (mouse + sliders), 3 = Symmetrical (mouse + sliders) + Gtk::TreeModelColumn locX; + Gtk::TreeModelColumn locXL; + Gtk::TreeModelColumn locY; + Gtk::TreeModelColumn locYT; + Gtk::TreeModelColumn centerX; + Gtk::TreeModelColumn centerY; + Gtk::TreeModelColumn circrad; + Gtk::TreeModelColumn qualityMethod; // 0 = Standard, 1 = Enhanced, 2 = Enhanced + chroma denoise + Gtk::TreeModelColumn transit; + Gtk::TreeModelColumn thresh; + Gtk::TreeModelColumn iter; + }; + + class RenameDialog: + public Gtk::Dialog + { + public: + RenameDialog (const Glib::ustring &actualname, Gtk::Window &parent); + Glib::ustring get_new_name(); + + private: + Gtk::Entry newname_; + }; + + ControlSpots spots_; + + // Child widgets + Gtk::ScrolledWindow scrolledwindow_; + Gtk::TreeView treeview_; + Glib::RefPtr treemodel_; + + Gtk::ButtonBox buttonbox_; + Gtk::Button button_add_; + Gtk::Button button_delete_; + Gtk::Button button_rename_; + + MyComboBoxText* const shape_; + sigc::connection shapeconn_; + MyComboBoxText* const spotMethod_; + sigc::connection spotMethodconn_; + MyComboBoxText* const shapeMethod_; + sigc::connection shapeMethodconn_; + MyComboBoxText* const qualityMethod_; + sigc::connection qualityMethodconn_; + + Adjuster* const locX_; + Adjuster* const locXL_; + Adjuster* const locY_; + Adjuster* const locYT_; + Adjuster* const centerX_; + Adjuster* const centerY_; + Adjuster* const circrad_; + Adjuster* const transit_; + Adjuster* const thresh_; + Adjuster* const iter_; + + int lastObject_; + rtengine::Coord* lastCoord_; +}; + +#endif // _CONTROLSPOTPANEL_H_ diff --git a/rtgui/edit.cc b/rtgui/edit.cc index 3d0ca3957..71870105b 100644 --- a/rtgui/edit.cc +++ b/rtgui/edit.cc @@ -312,14 +312,9 @@ void Arcellipse::drawOuterGeometry (Cairo::RefPtr &cr, ObjectMOB rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius)) : double (radius); -// double radius2_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius2)) : double (radius2); - double scalx_ = scalx; //radius2_ / radius_; - - double scaly_ = scaly; + double radius2_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius2)) : double (radius2); double begang_ = begang; double endang_ = endang; - double translax_ = translax; - double translay_ = translay; if (datum == IMAGE) { coordSystem.imageCoordToScreen (center.x, center.y, center_.x, center_.y); @@ -329,16 +324,23 @@ void Arcellipse::drawOuterGeometry (Cairo::RefPtr &cr, ObjectMOB center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; } - cr->save(); - cr->translate (translax_, translay_); + if (radius_ > 0 && radius2_ > 0.) { + cr->save(); - cr->scale (scalx_, scaly_); - cr->translate (- translax_ , - translay_); + // To have an ellipse with radius of (rad1, rad2), a circle of radius rad1 shall be twisted with a scale + // of rad2 / rad1 for y axis + // Center of coordinates (x, y) in previous coordinates system becomes (X, Y) = (x, rad2 / rad1 * y) in new one + // To go back to previous location, center shall be translated to t = -Y * (1 - rad1 / rad2) in y axis + // (Y = rad2 / rad1 * y and y = t + Y) + double scale_ = radius2_ / radius_; + cr->scale (1., scale_); + cr->translate (0., - center_.y * (1 - 1 / scale_)); - cr->arc (center_.x + 0.5, center_.y + 0.5, radius_, begang_, endang_); + cr->arc (center_.x + 0.5, center_.y + 0.5, radius_, begang_, endang_); - cr->restore(); - cr->stroke(); + cr->restore(); + cr->stroke(); + } } } @@ -363,15 +365,9 @@ void Arcellipse::drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOB rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius)) : double (radius); -// double radius2_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius2)) : double (radius2); - - double scalx_ = scalx; //radius2_ / radius_; - - double scaly_ = scaly; + double radius2_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius2)) : double (radius2); double begang_ = begang; double endang_ = endang; - double translax_ = translax; - double translay_ = translay; if (datum == IMAGE) { coordSystem.imageCoordToScreen (center.x, center.y, center_.x, center_.y); @@ -382,16 +378,23 @@ void Arcellipse::drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOB } if (filled && state != INSENSITIVE) { - cr->save(); + if (radius_ > 0 && radius2_ > 0.) { + cr->save(); - cr->translate (translax_, translay_); + // To have an ellipse with radius of (rad1, rad2), a circle of radius rad1 shall be twisted with a scale + // of rad2 / rad1 for y axis + // Center of coordinates (x, y) in previous coordinates system becomes (X, Y) = (x, rad2 / rad1 * y) in new one + // To go back to previous location, center shall be translated to t = -Y * (1 - rad1 / rad2) in y axis + // (Y = rad2 / rad1 * y and y = t + Y) + double scale_ = radius2_ / radius_; + cr->scale (1., scale_); + cr->translate (0., - center_.y * (1 - 1 / scale_)); - cr->scale (scalx_, scaly_); - cr->translate (- translax_ , - translay_); + cr->arc (center_.x + 0.5, center_.y + 0.5, radius_, begang_, endang_); - cr->arc (center_.x + 0.5, center_.y + 0.5, radius_, begang_, endang_); - - cr->restore(); + cr->restore(); + cr->stroke(); + } if (innerLineWidth > 0.) { cr->fill_preserve(); @@ -400,16 +403,23 @@ void Arcellipse::drawInnerGeometry (Cairo::RefPtr &cr, ObjectMOB cr->fill(); } } else if (innerLineWidth > 0.) { - cr->save(); + if (radius_ > 0 && radius2_ > 0.) { + cr->save(); - cr->translate (translax_ , translay_); + // To have an ellipse with radius of (rad1, rad2), a circle of radius rad1 shall be twisted with a scale + // of rad2 / rad1 for y axis + // Center of coordinates (x, y) in previous coordinates system becomes (X, Y) = (x, rad2 / rad1 * y) in new one + // To go back to previous location, center shall be translated to t = -Y * (1 - rad1 / rad2) in y axis + // (Y = rad2 / rad1 * y and y = t + Y) + double scale_ = radius2_ / radius_; + cr->scale (1., scale_); + cr->translate (0., - center_.y * (1 - 1 / scale_)); - cr->scale (scalx_, scaly_); - cr->translate (- translax_ , - translay_); + cr->arc (center_.x + 0.5, center_.y + 0.5, radius_, begang_, endang_); - cr->arc (center_.x + 0.5, center_.y + 0.5, radius_, begang_, endang_); - - cr->restore(); + cr->restore(); + cr->stroke(); + } if (state == INSENSITIVE) { std::valarray ds (1); @@ -437,14 +447,9 @@ void Arcellipse::drawToMOChannel (Cairo::RefPtr &cr, unsigned sh cr->set_line_width ( getMouseOverLineWidth() ); rtengine::Coord center_ = center; double radius_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius)) : double (radius); -// double radius2_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius2)) : double (radius2); - - double scalx_ = scalx ; //radius2_ / radius_; - double scaly_ = scaly; + double radius2_ = radiusInImageSpace ? coordSystem.scaleValueToCanvas (double (radius2)) : double (radius2); double begang_ = begang; double endang_ = endang; - double translax_ = translax; - double translay_ = translay; if (datum == IMAGE) { coordSystem.imageCoordToCropCanvas (center.x, center.y, center_.x, center_.y); @@ -454,15 +459,23 @@ void Arcellipse::drawToMOChannel (Cairo::RefPtr &cr, unsigned sh center_ += objectBuffer->getDataProvider()->posScreen + objectBuffer->getDataProvider()->deltaScreen; } - cr->save(); + if (radius_ > 0 && radius2_ > 0.) { + cr->save(); - cr->translate (translax_ , translay_); + // To have an ellipse with radius of (rad1, rad2), a circle of radius rad1 shall be twisted with a scale + // of rad2 / rad1 for y axis + // Center of coordinates (x, y) in previous coordinates system becomes (X, Y) = (x, rad2 / rad1 * y) in new one + // To go back to previous location, center shall be translated to t = -Y * (1 - rad1 / rad2) in y axis + // (Y = rad2 / rad1 * y and y = t + Y) + double scale_ = radius2_ / radius_; + cr->scale (1., scale_); + cr->translate (0., - center_.y * (1 - 1 / scale_)); - cr->scale (scalx_, scaly_); - cr->translate (- translax_ , - translay_); - cr->arc (center_.x + 0.5, center_.y + 0.5, radius_, begang_, endang_); + cr->arc (center_.x + 0.5, center_.y + 0.5, radius_, begang_, endang_); - cr->restore(); + cr->restore(); + cr->stroke(); + } if (filled) { if (innerLineWidth > 0.) { diff --git a/rtgui/edit.h b/rtgui/edit.h index 097b3fed2..8555e2931 100644 --- a/rtgui/edit.h +++ b/rtgui/edit.h @@ -357,9 +357,9 @@ class Arcellipse : public Geometry { public: rtengine::Coord center; -// rtengine::Coord scalx; -// rtengine::Coord scaly; - + // rtengine::Coord scalx; + // rtengine::Coord scaly; + // TODO translax, translay, scalx and scaly are not used double radius; double radius2; double translax; diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc index 891c82ea1..d56de590b 100644 --- a/rtgui/locallab.cc +++ b/rtgui/locallab.cc @@ -280,6 +280,8 @@ Locallab::Locallab(): Evlocallabshapemethod = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_LOCSHAPEMETH");// = 600, Evlocallabspotduplicated = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_LOCSPOTDUP"); + spotPanel = Gtk::manage(new ControlSpotPanel()); + expsettings->add(*spotPanel); editHBox = Gtk::manage(new Gtk::HBox()); edit = Gtk::manage(new Gtk::ToggleButton()); edit->add(*Gtk::manage(new RTImage("editmodehand.png"))); @@ -4879,7 +4881,7 @@ void Locallab::setEditProvider(EditDataProvider * provider) EditSubscriber::setEditProvider(provider); cTgainshape->setEditProvider(provider); cTgainshaperab->setEditProvider(provider); - + spotPanel->setEditProvider(provider); } void Locallab::editToggled() diff --git a/rtgui/locallab.h b/rtgui/locallab.h index 9e86fb9b0..f5aef8116 100644 --- a/rtgui/locallab.h +++ b/rtgui/locallab.h @@ -16,7 +16,7 @@ #include #include "../rtengine/improcfun.h" #include "thresholdadjuster.h" - +#include "controlspotpanel.h" class Locallab : public ToolParamBlock, @@ -165,7 +165,7 @@ private: CurveEditorGroup* const LocalcurveEditorgainTrab; CurveEditorGroup* const llCurveEditorG; - + ControlSpotPanel *spotPanel; Gtk::HBox *editHBox; Gtk::ToggleButton* edit;