diff --git a/rtdata/languages/default b/rtdata/languages/default
index 5bcfc2892..31b14aa2f 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -1078,6 +1078,7 @@ PREFERENCES_TAB_GENERAL;General
PREFERENCES_TAB_IMPROC;Image Processing
PREFERENCES_TAB_PERFORMANCE;Performance & Quality
PREFERENCES_TAB_SOUND;Sounds
+PREFERENCES_TAB_DYNAMICPROFILE;Dynamic Profile Rules
PREFERENCES_TIMAX;High
PREFERENCES_TINB;Number of tiles
PREFERENCES_TISTD;Standard
@@ -1105,6 +1106,7 @@ PROFILEPANEL_PCUSTOM;Custom
PROFILEPANEL_PFILE;From file
PROFILEPANEL_PINTERNAL;Neutral
PROFILEPANEL_PLASTSAVED;Last Saved
+PROFILEPANEL_PDYNAMIC;Dynamic
PROFILEPANEL_SAVEDLGLABEL;Save Processing Parameters...
PROFILEPANEL_SAVEPPASTE;Parameters to save
PROFILEPANEL_TOOLTIPCOPY;Copy current processing profile to clipboard.\nCtrl-click to select the parameters to copy.
@@ -2037,3 +2039,12 @@ ZOOMPANEL_ZOOMFITCROPSCREEN;Fit crop to screen\nShortcut: Alt-f
ZOOMPANEL_ZOOMFITSCREEN;Fit whole image to screen\nShortcut: f
ZOOMPANEL_ZOOMIN;Zoom In\nShortcut: +
ZOOMPANEL_ZOOMOUT;Zoom Out\nShortcut: -
+DYNPROFILEEDITOR_PROFILE;Processing Profile
+DYNPROFILEEDITOR_MOVE_UP;Move Up
+DYNPROFILEEDITOR_MOVE_DOWN;Move Down
+DYNPROFILEEDITOR_NEW;New
+DYNPROFILEEDITOR_EDIT;Edit
+DYNPROFILEEDITOR_DELETE;Delete
+DYNPROFILEEDITOR_NEW_RULE;New Dynamic Profile Rule
+DYNPROFILEEDITOR_EDIT_RULE;Edit Dynamic Profile Rule
+DYNPROFILEEDITOR_ENTRY_TOOLTIP;The matching is case insensitive.\nUse the "re:" prefix to enter\na regular expression.
diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc
index 7f4ae11f8..f01fa57d2 100644
--- a/rtengine/procparams.cc
+++ b/rtengine/procparams.cc
@@ -8079,6 +8079,8 @@ int PartialProfile::load (const Glib::ustring &fName)
if (fName == DEFPROFILE_INTERNAL) {
return 0;
+ } else if (fName == DEFPROFILE_DYNAMIC) {
+ return -1; // should not happen here
} else {
return pparams->load(fName, pedited);
}
diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt
index 021f6163f..153c1a0ef 100644
--- a/rtgui/CMakeLists.txt
+++ b/rtgui/CMakeLists.txt
@@ -30,7 +30,8 @@ set (BASESOURCEFILES
darkframe.cc flatfield.cc rawcacorrection.cc rawexposure.cc wavelet.cc
dirpyrequalizer.cc hsvequalizer.cc defringe.cc
popupcommon.cc popupbutton.cc popuptogglebutton.cc sharpenedge.cc sharpenmicro.cc colorappearance.cc
- filmsimulation.cc prsharpening.cc)
+ filmsimulation.cc prsharpening.cc
+ dynamicprofile.cc dynamicprofilepanel.cc)
include_directories (BEFORE "${CMAKE_CURRENT_BINARY_DIR}")
diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc
new file mode 100644
index 000000000..4439a1562
--- /dev/null
+++ b/rtgui/dynamicprofile.cc
@@ -0,0 +1,255 @@
+/* -*- C++ -*-
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2017 Alberto Griggio
+ *
+ * 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 "dynamicprofile.h"
+#include "profilestore.h"
+#include
+#include
+
+using namespace rtengine;
+using namespace rtengine::procparams;
+
+namespace {
+
+const int ISO_MAX = 512000;
+const double FNUMBER_MAX = 100.0;
+const double FOCALLEN_MAX = 10000.0;
+const double SHUTTERSPEED_MAX = 1000.0;
+const double EXPCOMP_MIN = -20.0;
+const double EXPCOMP_MAX = 20.0;
+
+} // namespace
+
+
+bool DynamicProfileRule::Optional::operator()(const Glib::ustring &val) const
+{
+ if (!enabled) {
+ return true;
+ }
+ if (value.find("re:") == 0) {
+ // this is a regexp
+ return Glib::Regex::match_simple(value.substr(3), val,
+ Glib::REGEX_CASELESS);
+ } else {
+ // normal string comparison
+ return value.casefold() == val.casefold();
+ }
+}
+
+
+DynamicProfileRule::DynamicProfileRule():
+ serial_number(0),
+ iso(0, ISO_MAX),
+ fnumber(0, FNUMBER_MAX),
+ focallen(0, FOCALLEN_MAX),
+ shutterspeed(0, SHUTTERSPEED_MAX),
+ expcomp(EXPCOMP_MIN, EXPCOMP_MAX)
+{
+}
+
+
+bool DynamicProfileRule::operator<(const DynamicProfileRule &other) const
+{
+ return serial_number < other.serial_number;
+}
+
+
+bool DynamicProfileRule::matches(const rtengine::ImageMetaData *im) const
+{
+ return (iso(im->getISOSpeed())
+ && fnumber(im->getFNumber())
+ && focallen(im->getFocalLen())
+ && shutterspeed(im->getShutterSpeed())
+ && expcomp(im->getExpComp())
+ && camera(im->getCamera())
+ && lens(im->getLens()));
+}
+
+namespace {
+
+void get_int_range(DynamicProfileRule::Range &dest,
+ const Glib::KeyFile &kf, const Glib::ustring &group,
+ const Glib::ustring &key)
+{
+ try {
+ int min = kf.get_integer(group, key + "_min");
+ int max = kf.get_integer(group, key + "_max");
+ if (min <= max) {
+ dest.min = min;
+ dest.max = max;
+ }
+ } catch (Glib::KeyFileError &e) {
+ }
+}
+
+
+void get_double_range(DynamicProfileRule::Range &dest,
+ const Glib::KeyFile &kf, const Glib::ustring &group,
+ const Glib::ustring &key)
+{
+ try {
+ double min = kf.get_double(group, key + "_min");
+ double max = kf.get_double(group, key + "_max");
+ if (min <= max) {
+ dest.min = min;
+ dest.max = max;
+ }
+ } catch (Glib::KeyFileError &e) {
+ }
+}
+
+
+void get_optional(DynamicProfileRule::Optional &dest,
+ const Glib::KeyFile &kf, const Glib::ustring &group,
+ const Glib::ustring &key)
+{
+ try {
+ bool e = kf.get_boolean(group, key + "_enabled");
+ if (e) {
+ Glib::ustring s = kf.get_string(group, key + "_value");
+ dest.enabled = e;
+ dest.value = s;
+ }
+ } catch (Glib::KeyFileError &) {
+ }
+}
+
+void set_int_range(Glib::KeyFile &kf, const Glib::ustring &group,
+ const Glib::ustring &key,
+ const DynamicProfileRule::Range &val)
+{
+ kf.set_integer(group, key + "_min", val.min);
+ kf.set_integer(group, key + "_max", val.max);
+}
+
+void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group,
+ const Glib::ustring &key,
+ const DynamicProfileRule::Range &val)
+{
+ kf.set_double(group, key + "_min", val.min);
+ kf.set_double(group, key + "_max", val.max);
+}
+
+void set_optional(Glib::KeyFile &kf, const Glib::ustring &group,
+ const Glib::ustring &key,
+ const DynamicProfileRule::Optional &val)
+{
+ kf.set_boolean(group, key + "_enabled", val.enabled);
+ kf.set_string(group, key + "_value", val.value);
+}
+
+} // namespace
+
+
+bool loadDynamicProfileRules(std::vector &out)
+{
+ out.clear();
+ Glib::KeyFile kf;
+ try {
+ if (!kf.load_from_file(
+ Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) {
+ return false;
+ }
+ } catch (Glib::Error &e) {
+ return false;
+ }
+ if (options.rtSettings.verbose) {
+ printf("loading dynamic profiles...\n");
+ }
+ auto groups = kf.get_groups();
+ for (auto group : groups) {
+ // groups are of the form "rule N", where N is a positive integer
+ if (group.find("rule ") != 0) {
+ return false;
+ }
+ std::istringstream buf(group.c_str() + 5);
+ int serial = 0;
+ if (!(buf >> serial) || !buf.eof()) {
+ return false;
+ }
+ if (options.rtSettings.verbose) {
+ printf(" loading rule %d\n", serial);
+ }
+
+ out.emplace_back(DynamicProfileRule());
+ DynamicProfileRule &rule = out.back();
+ rule.serial_number = serial;
+ get_int_range(rule.iso, kf, group, "iso");
+ get_double_range(rule.fnumber, kf, group, "fnumber");
+ get_double_range(rule.focallen, kf, group, "focallen");
+ get_double_range(rule.shutterspeed, kf, group, "shutterspeed");
+ get_double_range(rule.expcomp, kf, group, "expcomp");
+ get_optional(rule.camera, kf, group, "camera");
+ get_optional(rule.lens, kf, group, "lens");
+ try {
+ rule.profilepath = kf.get_string(group, "profilepath");
+ } catch (Glib::KeyFileError &) {
+ out.pop_back();
+ }
+ }
+ std::sort(out.begin(), out.end());
+ return true;
+}
+
+
+bool storeDynamicProfileRules(const std::vector &rules)
+{
+ if (options.rtSettings.verbose) {
+ printf("saving dynamic profiles...\n");
+ }
+ Glib::KeyFile kf;
+ for (auto &rule : rules) {
+ std::ostringstream buf;
+ buf << "rule " << rule.serial_number;
+ Glib::ustring group = buf.str();
+ set_int_range(kf, group, "iso", rule.iso);
+ set_double_range(kf, group, "fnumber", rule.fnumber);
+ set_double_range(kf, group, "focallen", rule.focallen);
+ set_double_range(kf, group, "shutterspeed", rule.shutterspeed);
+ set_double_range(kf, group, "expcomp", rule.expcomp);
+ set_optional(kf, group, "camera", rule.camera);
+ set_optional(kf, group, "lens", rule.lens);
+ kf.set_string(group, "profilepath", rule.profilepath);
+ }
+ return kf.save_to_file(
+ Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"));
+}
+
+
+PartialProfile *loadDynamicProfile(const ImageMetaData *im)
+{
+ PartialProfile *ret = new PartialProfile(true, true);
+ for (auto &rule : profileStore.getDynamicProfileRules()) {
+ if (rule.matches(im)) {
+ if (options.rtSettings.verbose) {
+ printf("found matching profile %s\n",
+ rule.profilepath.c_str());
+ }
+ const PartialProfile *p =
+ profileStore.getProfile(rule.profilepath);
+ if (p != nullptr) {
+ p->applyTo(ret->pparams);
+ } else {
+ printf("ERROR loading matching profile from: %s\n",
+ rule.profilepath.c_str());
+ }
+ }
+ }
+ return ret;
+}
diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h
new file mode 100644
index 000000000..4c5e552e4
--- /dev/null
+++ b/rtgui/dynamicprofile.h
@@ -0,0 +1,74 @@
+/* -*- C++ -*-
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2017 Alberto Griggio
+ *
+ * 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 .
+ */
+#ifndef _DYNAMICPROFILE_H_
+#define _DYNAMICPROFILE_H_
+
+#include
+#include
+#include "options.h"
+
+
+class DynamicProfileRule {
+public:
+ template
+ struct Range {
+ T min;
+ T max;
+ explicit Range(T l=T(), T u=T()): min(l), max(u) {}
+
+ bool operator()(T val) const
+ {
+ return val >= min && val <= max;
+ }
+ };
+
+ struct Optional {
+ Glib::ustring value;
+ bool enabled;
+ explicit Optional(const Glib::ustring v="", bool e=false):
+ value(v), enabled(e) {}
+
+ bool operator()(const Glib::ustring &val) const;
+ };
+
+ DynamicProfileRule();
+ bool matches(const rtengine::ImageMetaData *im) const;
+ bool operator<(const DynamicProfileRule &other) const;
+
+ int serial_number;
+ Range iso;
+ Range fnumber;
+ Range focallen;
+ Range shutterspeed;
+ Range expcomp;
+ Optional camera;
+ Optional lens;
+ Glib::ustring profilepath;
+};
+
+
+bool loadDynamicProfileRules(std::vector &out);
+bool storeDynamicProfileRules(
+ const std::vector &rules);
+
+rtengine::procparams::PartialProfile *loadDynamicProfile(
+ const rtengine::ImageMetaData *im);
+
+
+#endif // _DYNAMICPROFILE_H_
diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc
new file mode 100644
index 000000000..e5f9ae1cc
--- /dev/null
+++ b/rtgui/dynamicprofilepanel.cc
@@ -0,0 +1,533 @@
+/* -*- C++ -*-
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2017 Alberto Griggio
+ *
+ * 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 "dynamicprofilepanel.h"
+#include "multilangmgr.h"
+#include "profilestore.h"
+#include "../rtengine/rtengine.h"
+#include
+#include
+
+
+//-----------------------------------------------------------------------------
+// DynamicProfilePanel::EditDialog
+//-----------------------------------------------------------------------------
+
+DynamicProfilePanel::EditDialog::EditDialog(const Glib::ustring &title,
+ Gtk::Window &parent):
+ Gtk::Dialog(title, parent)
+{
+ profilepath_ = Gtk::manage(new ProfileStoreComboBox());
+ Gtk::HBox *hb = Gtk::manage(new Gtk::HBox());
+ hb->pack_start(*Gtk::manage(new Gtk::Label(M("DYNPROFILEEDITOR_PROFILE"))),
+ false, false, 4);
+ hb->pack_start(*profilepath_, true, true, 2);
+ get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4);
+
+ add_optional(M("EXIFFILTER_CAMERA"), has_camera_, camera_);
+ add_optional(M("EXIFFILTER_LENS"), has_lens_, lens_);
+
+ add_range(M("EXIFFILTER_ISO"), iso_min_, iso_max_);
+ add_range(M("EXIFFILTER_APERTURE"), fnumber_min_, fnumber_max_);
+ add_range(M("EXIFFILTER_FOCALLEN"), focallen_min_, focallen_max_);
+ add_range(M("EXIFFILTER_SHUTTER"), shutterspeed_min_, shutterspeed_max_);
+ add_range(M("EXIFFILTER_EXPOSURECOMPENSATION"), expcomp_min_, expcomp_max_);
+
+ add_button(M("GENERAL_OK"), 1);
+ add_button(M("GENERAL_CANCEL"), 2);
+
+ set_ranges();
+
+ show_all_children();
+}
+
+
+void DynamicProfilePanel::EditDialog::set_rule(
+ const DynamicProfileRule &rule)
+{
+ iso_min_->set_value(rule.iso.min);
+ iso_max_->set_value(rule.iso.max);
+
+ fnumber_min_->set_value(rule.fnumber.min);
+ fnumber_max_->set_value(rule.fnumber.max);
+
+ focallen_min_->set_value(rule.focallen.min);
+ focallen_max_->set_value(rule.focallen.max);
+
+ shutterspeed_min_->set_value(rule.shutterspeed.min);
+ shutterspeed_max_->set_value(rule.shutterspeed.max);
+
+ expcomp_min_->set_value(rule.expcomp.min);
+ expcomp_max_->set_value(rule.expcomp.max);
+
+ has_camera_->set_active(rule.camera.enabled);
+ camera_->set_text(rule.camera.value);
+
+ has_lens_->set_active(rule.lens.enabled);
+ lens_->set_text(rule.lens.value);
+
+ profilepath_->updateProfileList();
+ if (!profilepath_->setActiveRowFromFullPath(rule.profilepath)) {
+ profilepath_->setInternalEntry();
+ }
+}
+
+
+DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule()
+{
+ DynamicProfileRule ret;
+ ret.iso.min = iso_min_->get_value_as_int();
+ ret.iso.max = iso_max_->get_value_as_int();
+
+ ret.fnumber.min = fnumber_min_->get_value();
+ ret.fnumber.max = fnumber_max_->get_value();
+
+ ret.focallen.min = focallen_min_->get_value();
+ ret.focallen.max = focallen_max_->get_value();
+
+ ret.shutterspeed.min = shutterspeed_min_->get_value();
+ ret.shutterspeed.max = shutterspeed_max_->get_value();
+
+ ret.expcomp.min = expcomp_min_->get_value();
+ ret.expcomp.max = expcomp_max_->get_value();
+
+ ret.camera.enabled = has_camera_->get_active();
+ ret.camera.value = camera_->get_text();
+
+ ret.lens.enabled = has_lens_->get_active();
+ ret.lens.value = lens_->get_text();
+
+ ret.profilepath = profilepath_->getFullPathFromActiveRow();
+
+ return ret;
+}
+
+void DynamicProfilePanel::EditDialog::set_ranges()
+{
+ DynamicProfileRule default_rule;
+ iso_min_->set_digits(0);
+ iso_max_->set_digits(0);
+ iso_min_->set_increments(1, 10);
+ iso_max_->set_increments(1, 10);
+ iso_min_->set_range(default_rule.iso.min, default_rule.iso.max);
+ iso_max_->set_range(default_rule.iso.min, default_rule.iso.max);
+ iso_min_->set_value(default_rule.iso.min);
+ iso_max_->set_value(default_rule.iso.max);
+
+#define DOIT_(name) \
+ name ## _min_->set_digits(1); \
+ name ## _max_->set_digits(1); \
+ name ## _min_->set_increments(0.1, 1); \
+ name ## _max_->set_increments(0.1, 1); \
+ name ## _min_->set_range(default_rule. name .min, \
+ default_rule. name .max); \
+ name ## _max_->set_range(default_rule. name .min, \
+ default_rule. name .max); \
+ name ## _min_->set_value(default_rule. name .min); \
+ name ## _max_->set_value(default_rule. name .max)
+
+ DOIT_(fnumber);
+ DOIT_(focallen);
+ DOIT_(shutterspeed);
+ DOIT_(expcomp);
+#undef DOIT_
+ shutterspeed_min_->set_digits(4);
+ shutterspeed_max_->set_digits(4);
+
+ profilepath_->setInternalEntry();
+}
+
+
+void DynamicProfilePanel::EditDialog::add_range(const Glib::ustring &name,
+ Gtk::SpinButton *&from, Gtk::SpinButton *&to)
+{
+ Gtk::HBox *hb = Gtk::manage(new Gtk::HBox());
+ hb->pack_start(*Gtk::manage(new Gtk::Label(name)), false, false, 4);
+ from = Gtk::manage(new Gtk::SpinButton());
+ to = Gtk::manage(new Gtk::SpinButton());
+ from->set_numeric(true);
+ to->set_numeric(true);
+ hb->pack_start(*from, true, true, 2);
+ hb->pack_start(*Gtk::manage(new Gtk::Label(" - ")),
+ false, false, 4);
+ hb->pack_start(*to, true, true, 2);
+ get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4);
+}
+
+
+void DynamicProfilePanel::EditDialog::add_optional(const Glib::ustring &name,
+ Gtk::CheckButton *&check, Gtk::Entry *&field)
+{
+ check = Gtk::manage (new Gtk::CheckButton(name));
+ Gtk::HBox *hb = Gtk::manage(new Gtk::HBox());
+ hb->pack_start(*check, Gtk::PACK_SHRINK, 4);
+ field = Gtk::manage(new Gtk::Entry());
+ hb->pack_start(*field, true, true, 2);
+ get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4);
+ field->set_tooltip_text(M("DYNPROFILEEDITOR_ENTRY_TOOLTIP"));
+}
+
+
+//-----------------------------------------------------------------------------
+// DynamicProfilePanel
+//-----------------------------------------------------------------------------
+
+DynamicProfilePanel::DynamicProfilePanel():
+ vbox_(Gtk::ORIENTATION_VERTICAL),
+ button_up_(M("DYNPROFILEEDITOR_MOVE_UP")),
+ button_down_(M("DYNPROFILEEDITOR_MOVE_DOWN")),
+ button_new_(M("DYNPROFILEEDITOR_NEW")),
+ button_edit_(M("DYNPROFILEEDITOR_EDIT")),
+ button_delete_(M("DYNPROFILEEDITOR_DELETE"))
+{
+ add(vbox_);
+
+ treeview_.set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_VERTICAL);
+ scrolledwindow_.add(treeview_);
+
+ scrolledwindow_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ vbox_.pack_start(scrolledwindow_);
+ vbox_.pack_start(buttonbox_, Gtk::PACK_SHRINK);
+
+ buttonbox_.pack_start(button_new_, Gtk::PACK_SHRINK);
+ buttonbox_.pack_start(button_edit_, Gtk::PACK_SHRINK);
+ buttonbox_.pack_start(button_delete_, Gtk::PACK_SHRINK);
+ buttonbox_.pack_start(button_up_, Gtk::PACK_SHRINK);
+ buttonbox_.pack_start(button_down_, Gtk::PACK_SHRINK);
+ buttonbox_.set_border_width(5);
+ buttonbox_.set_layout(Gtk::BUTTONBOX_END);
+ button_up_.signal_clicked().connect(
+ sigc::mem_fun(*this, &DynamicProfilePanel::on_button_up));
+ button_down_.signal_clicked().connect(
+ sigc::mem_fun(*this, &DynamicProfilePanel::on_button_down));
+ button_new_.signal_clicked().connect(
+ sigc::mem_fun(*this, &DynamicProfilePanel::on_button_new));
+ button_edit_.signal_clicked().connect(
+ sigc::mem_fun(*this, &DynamicProfilePanel::on_button_edit));
+ button_delete_.signal_clicked().connect(
+ sigc::mem_fun(*this, &DynamicProfilePanel::on_button_delete));
+
+ treemodel_ = Gtk::ListStore::create(columns_);
+ treeview_.set_model(treemodel_);
+
+ auto cell = Gtk::manage(new Gtk::CellRendererText());
+ int cols_count = treeview_.append_column(
+ M("DYNPROFILEEDITOR_PROFILE"), *cell);
+ auto col = treeview_.get_column(cols_count - 1);
+ if (col) {
+ col->set_cell_data_func(
+ *cell, sigc::mem_fun(
+ *this, &DynamicProfilePanel::render_profilepath));
+ }
+ cell = Gtk::manage(new Gtk::CellRendererText());
+ cols_count = treeview_.append_column(
+ M("EXIFFILTER_CAMERA"), *cell);
+ col = treeview_.get_column(cols_count - 1);
+ if (col) {
+ col->set_cell_data_func(
+ *cell, sigc::mem_fun(
+ *this, &DynamicProfilePanel::render_camera));
+ }
+ cell = Gtk::manage(new Gtk::CellRendererText());
+ cols_count = treeview_.append_column(M("EXIFFILTER_LENS"), *cell);
+ col = treeview_.get_column(cols_count - 1);
+ if (col) {
+ col->set_cell_data_func(
+ *cell, sigc::mem_fun(
+ *this, &DynamicProfilePanel::render_lens));
+ }
+ cell = Gtk::manage(new Gtk::CellRendererText());
+ cols_count = treeview_.append_column(M("EXIFFILTER_ISO"), *cell);
+ col = treeview_.get_column(cols_count - 1);
+ if (col) {
+ col->set_cell_data_func(
+ *cell, sigc::mem_fun(
+ *this, &DynamicProfilePanel::render_iso));
+ }
+ cell = Gtk::manage(new Gtk::CellRendererText());
+ cols_count = treeview_.append_column(M("EXIFFILTER_APERTURE"), *cell);
+ col = treeview_.get_column(cols_count - 1);
+ if (col) {
+ col->set_cell_data_func(
+ *cell, sigc::mem_fun(
+ *this, &DynamicProfilePanel::render_fnumber));
+ }
+ cell = Gtk::manage(new Gtk::CellRendererText());
+ cols_count = treeview_.append_column(M("EXIFFILTER_FOCALLEN"), *cell);
+ col = treeview_.get_column(cols_count - 1);
+ if (col) {
+ col->set_cell_data_func(
+ *cell, sigc::mem_fun(
+ *this, &DynamicProfilePanel::render_focallen));
+ }
+ cell = Gtk::manage(new Gtk::CellRendererText());
+ cols_count = treeview_.append_column(M("EXIFFILTER_SHUTTER"), *cell);
+ col = treeview_.get_column(cols_count - 1);
+ if (col) {
+ col->set_cell_data_func(
+ *cell, sigc::mem_fun(
+ *this, &DynamicProfilePanel::render_shutterspeed));
+ }
+ cell = Gtk::manage(new Gtk::CellRendererText());
+ cols_count = treeview_.append_column(
+ M("EXIFFILTER_EXPOSURECOMPENSATION"), *cell);
+ col = treeview_.get_column(cols_count - 1);
+ if (col) {
+ col->set_cell_data_func(
+ *cell, sigc::mem_fun(
+ *this, &DynamicProfilePanel::render_expcomp));
+ }
+
+ show_all_children();
+
+ for (auto &r : profileStore.getDynamicProfileRules()) {
+ add_rule(r);
+ }
+}
+
+
+void DynamicProfilePanel::update_rule(Gtk::TreeModel::Row row,
+ const DynamicProfileRule &rule)
+{
+ row[columns_.iso] = rule.iso;
+ row[columns_.fnumber] = rule.fnumber;
+ row[columns_.focallen] = rule.focallen;
+ row[columns_.shutterspeed] = rule.shutterspeed;
+ row[columns_.expcomp] = rule.expcomp;
+ row[columns_.camera] = rule.camera;
+ row[columns_.lens] = rule.lens;
+ row[columns_.profilepath] = rule.profilepath;
+}
+
+void DynamicProfilePanel::add_rule(const DynamicProfileRule &rule)
+{
+ auto row = *(treemodel_->append());
+ update_rule(row, rule);
+}
+
+
+DynamicProfileRule DynamicProfilePanel::to_rule(Gtk::TreeModel::Row row,
+ int serial)
+{
+ DynamicProfileRule ret;
+ ret.serial_number = serial;
+ ret.iso = row[columns_.iso];
+ ret.fnumber = row[columns_.fnumber];
+ ret.focallen = row[columns_.focallen];
+ ret.shutterspeed = row[columns_.shutterspeed];
+ ret.expcomp = row[columns_.expcomp];
+ ret.camera = row[columns_.camera];
+ ret.lens = row[columns_.lens];
+ ret.profilepath = row[columns_.profilepath];
+ return ret;
+}
+
+
+void DynamicProfilePanel::render_profilepath(
+ Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
+{
+ auto row = *iter;
+ Gtk::CellRendererText *ct = static_cast(cell);
+ auto value = row[columns_.profilepath];
+ auto pse = profileStore.findEntryFromFullPath(value);
+ if (pse != nullptr) {
+ ct->property_text() = pse->label;
+ } else {
+ ct->property_text() = value;
+ }
+}
+
+
+#define RENDER_RANGE_(tp, name, tostr) \
+ auto row = *iter; \
+ Gtk::CellRendererText *ct = static_cast(cell); \
+ DynamicProfileRule::Range r = row[columns_. name]; \
+ DynamicProfileRule dflt; \
+ if (r.min > dflt.name.min || r.max < dflt.name.max) { \
+ auto value = tostr(r.min) + " - " + tostr(r.max); \
+ ct->property_text() = value; \
+ } else { \
+ ct->property_text() = ""; \
+ }
+
+
+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 DynamicProfilePanel::render_iso(
+ Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
+{
+ RENDER_RANGE_(int, iso, to_str);
+}
+
+
+void DynamicProfilePanel::render_fnumber(
+ Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
+{
+ RENDER_RANGE_(double, fnumber,
+ [](double f)
+ { return std::string("f/") +
+ rtengine::ImageMetaData::apertureToString(f); });
+}
+
+
+void DynamicProfilePanel::render_focallen(
+ Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
+{
+ RENDER_RANGE_(double, focallen, to_str);
+}
+
+
+void DynamicProfilePanel::render_shutterspeed(
+ Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
+{
+ RENDER_RANGE_(double, shutterspeed,
+ rtengine::ImageMetaData::shutterToString);
+}
+
+
+void DynamicProfilePanel::render_expcomp(
+ Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
+{
+ RENDER_RANGE_(double, expcomp, to_str);
+}
+
+#undef RENDER_RANGE_
+
+#define RENDER_OPTIONAL_(name) \
+ auto row = *iter; \
+ Gtk::CellRendererText *ct = static_cast(cell); \
+ DynamicProfileRule::Optional o = row[columns_. name]; \
+ if (o.enabled) { \
+ ct->property_text() = o.value; \
+ } else { \
+ ct->property_text() = ""; \
+ }
+
+void DynamicProfilePanel::render_camera(
+ Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
+{
+ RENDER_OPTIONAL_(camera);
+}
+
+
+void DynamicProfilePanel::render_lens(
+ Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
+{
+ RENDER_OPTIONAL_(lens);
+}
+
+#undef RENDER_OPTIONAL_
+
+void DynamicProfilePanel::on_button_up()
+{
+ auto s = treeview_.get_selection();
+ if (!s->count_selected_rows()) {
+ return;
+ }
+ auto it = s->get_selected();
+ if (it != treemodel_->children().begin()) {
+ auto it2 = it;
+ --it2;
+ treemodel_->iter_swap(it, it2);
+ }
+}
+
+void DynamicProfilePanel::on_button_down()
+{
+ auto s = treeview_.get_selection();
+ if (!s->count_selected_rows()) {
+ return;
+ }
+ auto it = s->get_selected();
+ auto it2 = it;
+ ++it2;
+ if (it2 != treemodel_->children().end()) {
+ treemodel_->iter_swap(it, it2);
+ }
+}
+
+
+void DynamicProfilePanel::on_button_delete()
+{
+ auto s = treeview_.get_selection();
+ if (!s->count_selected_rows()) {
+ return;
+ }
+ auto it = s->get_selected();
+ treemodel_->erase(it);
+}
+
+
+void DynamicProfilePanel::on_button_new()
+{
+ EditDialog d(M("DYNPROFILEEDITOR_NEW_RULE"),
+ static_cast(*get_toplevel()));
+ int status = d.run();
+ if (status == 1) {
+ DynamicProfileRule rule = d.get_rule();
+ add_rule(rule);
+ }
+}
+
+
+void DynamicProfilePanel::on_button_edit()
+{
+ auto s = treeview_.get_selection();
+ if (!s->count_selected_rows()) {
+ return;
+ }
+ EditDialog d(M("DYNPROFILEEDITOR_EDIT_RULE"),
+ static_cast(*get_toplevel()));
+ auto it = s->get_selected();
+ Gtk::TreeModel::Row row = *(s->get_selected());
+ d.set_rule(to_rule(row));
+ int status = d.run();
+ if (status == 1) {
+ update_rule(row, d.get_rule());
+ }
+}
+
+
+void DynamicProfilePanel::save()
+{
+ std::vector rules;
+ int serial = 1;
+ for (auto row : treemodel_->children()) {
+ rules.emplace_back(to_rule(row, serial++));
+ }
+ if (!storeDynamicProfileRules(rules)) {
+ printf("Error in saving dynamic profile rules\n");
+ } else {
+ profileStore.setDynamicProfileRules(rules);
+ if (options.rtSettings.verbose) {
+ printf("Saved %d dynamic profile rules\n", int(rules.size()));
+ }
+ }
+}
diff --git a/rtgui/dynamicprofilepanel.h b/rtgui/dynamicprofilepanel.h
new file mode 100644
index 000000000..72ff95b9a
--- /dev/null
+++ b/rtgui/dynamicprofilepanel.h
@@ -0,0 +1,140 @@
+/* -*- C++ -*-
+ * This file is part of RawTherapee.
+ *
+ * Copyright (c) 2017 Alberto Griggio
+ *
+ * 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 .
+ */
+#ifndef _DYNAMICPROFILEPANEL_H_
+#define _DYNAMICPROFILEPANEL_H_
+
+#include
+#include "dynamicprofile.h"
+#include "profilestore.h"
+
+class DynamicProfilePanel: public Gtk::VBox {
+public:
+ DynamicProfilePanel();
+ void save();
+
+private:
+ void update_rule(Gtk::TreeModel::Row row,
+ const DynamicProfileRule &rule);
+ void add_rule(const DynamicProfileRule &rule);
+ DynamicProfileRule to_rule(Gtk::TreeModel::Row row, int serial=0);
+
+ void on_button_quit();
+ void on_button_up();
+ void on_button_down();
+ void on_button_new();
+ void on_button_edit();
+ void on_button_delete();
+
+ class DynamicProfileColumns: public Gtk::TreeModel::ColumnRecord {
+ public:
+ DynamicProfileColumns()
+ {
+ add(iso);
+ add(fnumber);
+ add(focallen);
+ add(shutterspeed);
+ add(expcomp);
+ add(camera);
+ add(lens);
+ add(profilepath);
+ }
+
+ Gtk::TreeModelColumn> iso;
+ Gtk::TreeModelColumn> fnumber;
+ Gtk::TreeModelColumn> focallen;
+ Gtk::TreeModelColumn> shutterspeed;
+ Gtk::TreeModelColumn> expcomp;
+ Gtk::TreeModelColumn camera;
+ Gtk::TreeModelColumn lens;
+ Gtk::TreeModelColumn profilepath;
+ };
+
+ // cell renderers
+ void render_iso(Gtk::CellRenderer* cell,
+ const Gtk::TreeModel::iterator& iter);
+ void render_fnumber(Gtk::CellRenderer* cell,
+ const Gtk::TreeModel::iterator& iter);
+ void render_focallen(Gtk::CellRenderer* cell,
+ const Gtk::TreeModel::iterator& iter);
+ void render_shutterspeed(Gtk::CellRenderer* cell,
+ const Gtk::TreeModel::iterator& iter);
+ void render_expcomp(Gtk::CellRenderer* cell,
+ const Gtk::TreeModel::iterator& iter);
+ void render_camera(Gtk::CellRenderer* cell,
+ const Gtk::TreeModel::iterator& iter);
+ void render_lens(Gtk::CellRenderer* cell,
+ const Gtk::TreeModel::iterator& iter);
+ void render_profilepath(Gtk::CellRenderer* cell,
+ const Gtk::TreeModel::iterator& iter);
+
+ class EditDialog: public Gtk::Dialog {
+ public:
+ EditDialog(const Glib::ustring &title, Gtk::Window &parent);
+ void set_rule(const DynamicProfileRule &rule);
+ DynamicProfileRule get_rule();
+
+ private:
+ void set_ranges();
+ void add_range(const Glib::ustring &name,
+ Gtk::SpinButton *&from, Gtk::SpinButton *&to);
+ void add_optional(const Glib::ustring &name,
+ Gtk::CheckButton *&check, Gtk::Entry *&field);
+
+ Gtk::SpinButton *iso_min_;
+ Gtk::SpinButton *iso_max_;
+
+ Gtk::SpinButton *fnumber_min_;
+ Gtk::SpinButton *fnumber_max_;
+
+ Gtk::SpinButton *focallen_min_;
+ Gtk::SpinButton *focallen_max_;
+
+ Gtk::SpinButton *shutterspeed_min_;
+ Gtk::SpinButton *shutterspeed_max_;
+
+ Gtk::SpinButton *expcomp_min_;
+ Gtk::SpinButton *expcomp_max_;
+
+ Gtk::CheckButton *has_camera_;
+ Gtk::Entry *camera_;
+
+ Gtk::CheckButton *has_lens_;
+ Gtk::Entry *lens_;
+
+ ProfileStoreComboBox *profilepath_;
+ };
+
+ DynamicProfileColumns columns_;
+
+ //Child widgets:
+ Gtk::Box vbox_;
+
+ Gtk::ScrolledWindow scrolledwindow_;
+ Gtk::TreeView treeview_;
+ Glib::RefPtr treemodel_;
+
+ Gtk::ButtonBox buttonbox_;
+ Gtk::Button button_up_;
+ Gtk::Button button_down_;
+ Gtk::Button button_new_;
+ Gtk::Button button_edit_;
+ Gtk::Button button_delete_;
+};
+
+#endif // _DYNAMICPROFILEPANEL_H_
diff --git a/rtgui/main.cc b/rtgui/main.cc
index d6b99fdc6..ee3d3210e 100644
--- a/rtgui/main.cc
+++ b/rtgui/main.cc
@@ -37,6 +37,7 @@
#include "rtimage.h"
#include "version.h"
#include "extprog.h"
+#include "dynamicprofile.h"
#ifndef WIN32
#include
@@ -710,7 +711,7 @@ int processLineParams( int argc, char **argv )
rawParams = new rtengine::procparams::PartialProfile(true, true);
Glib::ustring profPath = options.findProfilePath(options.defProfRaw);
- if (options.is_defProfRawMissing() || profPath.empty() || rawParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfRaw.substr(5) + paramFileExtension))) {
+ if (options.is_defProfRawMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && rawParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfRaw.substr(5) + paramFileExtension)))) {
std::cerr << "Error: default raw processing profile not found" << std::endl;
rawParams->deleteInstance();
delete rawParams;
@@ -721,7 +722,7 @@ int processLineParams( int argc, char **argv )
imgParams = new rtengine::procparams::PartialProfile(true);
profPath = options.findProfilePath(options.defProfImg);
- if (options.is_defProfImgMissing() || profPath.empty() || imgParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfImg.substr(5) + paramFileExtension))) {
+ if (options.is_defProfImgMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && imgParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, options.defProfImg.substr(5) + paramFileExtension)))) {
std::cerr << "Error: default non-raw processing profile not found" << std::endl;
imgParams->deleteInstance();
delete imgParams;
@@ -793,9 +794,19 @@ int processLineParams( int argc, char **argv )
if (useDefault) {
if (isRaw) {
+ if (options.defProfRaw == DEFPROFILE_DYNAMIC) {
+ rawParams->deleteInstance();
+ delete rawParams;
+ rawParams = loadDynamicProfile(ii->getMetaData());
+ }
std::cout << " Merging default raw processing profile" << std::endl;
rawParams->applyTo(¤tParams);
} else {
+ if (options.defProfImg == DEFPROFILE_DYNAMIC) {
+ imgParams->deleteInstance();
+ delete imgParams;
+ imgParams = loadDynamicProfile(ii->getMetaData());
+ }
std::cout << " Merging default non-raw processing profile" << std::endl;
imgParams->applyTo(¤tParams);
}
diff --git a/rtgui/options.cc b/rtgui/options.cc
index 5c0c032eb..2f71a7106 100644
--- a/rtgui/options.cc
+++ b/rtgui/options.cc
@@ -241,6 +241,10 @@ Glib::ustring Options::findProfilePath (Glib::ustring &profName)
return profName;
}
+ if (profName == DEFPROFILE_DYNAMIC) {
+ return profName;
+ }
+
Glib::ustring p = profName.substr (0, 4);
if (p == "${U}") {
diff --git a/rtgui/options.h b/rtgui/options.h
index 43f64a4df..efc649417 100644
--- a/rtgui/options.h
+++ b/rtgui/options.h
@@ -39,6 +39,8 @@
#define DEFPROFILE_IMG "Neutral"
// Profile name to use for internal values' profile
#define DEFPROFILE_INTERNAL "Neutral"
+// Special name for the Dynamic profile
+#define DEFPROFILE_DYNAMIC "Dynamic"
class SaveFormat
{
diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc
index 0de355bd2..ed613c61f 100644
--- a/rtgui/preferences.cc
+++ b/rtgui/preferences.cc
@@ -82,6 +82,7 @@ Preferences::Preferences (RTWindow *rtwindow)
nb->append_page (*getGeneralPanel(), M("PREFERENCES_TAB_GENERAL"));
nb->append_page (*getProcParamsPanel(), M("PREFERENCES_TAB_IMPROC"));
+ nb->append_page (*getDynProfilePanel(), M("PREFERENCES_TAB_DYNAMICPROFILE"));
nb->append_page (*getFileBrowserPanel(), M("PREFERENCES_TAB_BROWSER"));
nb->append_page (*getColorManagementPanel(), M("PREFERENCES_TAB_COLORMGR"));
nb->append_page (*getBatchProcPanel(), M("PREFERENCES_BATCH_PROCESSING"));
@@ -420,6 +421,14 @@ void Preferences::behSetRadioToggled (const Glib::ustring& path)
iter->set_value (behavColumns.badd, false);
}
+
+Gtk::Widget *Preferences::getDynProfilePanel()
+{
+ dynProfilePanel = Gtk::manage(new DynamicProfilePanel());
+ return dynProfilePanel;
+}
+
+
Gtk::Widget* Preferences::getProcParamsPanel ()
{
@@ -429,11 +438,13 @@ Gtk::Widget* Preferences::getProcParamsPanel ()
Gtk::VBox* vbpp = Gtk::manage (new Gtk::VBox ());
Gtk::Label* drlab = Gtk::manage (new Gtk::Label (M("PREFERENCES_FORRAW") + ":", Gtk::ALIGN_START));
rprofiles = Gtk::manage (new ProfileStoreComboBox ());
+ rprofiles->addRow(profileStore.getInternalDynamicPSE());
setExpandAlignProperties(rprofiles, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
rprofiles->set_size_request(50, -1);
rpconn = rprofiles->signal_changed().connect( sigc::mem_fun(*this, &Preferences::forRAWComboChanged) );
Gtk::Label* drimg = Gtk::manage (new Gtk::Label (M("PREFERENCES_FORIMAGE") + ":", Gtk::ALIGN_START));
iprofiles = Gtk::manage (new ProfileStoreComboBox ());
+ iprofiles->addRow(profileStore.getInternalDynamicPSE());
iprofiles->set_size_request(50, -1);
setExpandAlignProperties(iprofiles, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
ipconn = iprofiles->signal_changed().connect( sigc::mem_fun(*this, &Preferences::forImageComboChanged) );
@@ -2020,6 +2031,7 @@ void Preferences::okPressed ()
options.copyFrom (&moptions);
options.filterOutParsedExtensions();
Options::save ();
+ dynProfilePanel->save();
hide ();
}
@@ -2181,6 +2193,8 @@ void Preferences::updateProfileList()
{
rprofiles->updateProfileList();
iprofiles->updateProfileList();
+ rprofiles->addRow(profileStore.getInternalDynamicPSE());
+ iprofiles->addRow(profileStore.getInternalDynamicPSE());
}
void Preferences::restoreValue()
diff --git a/rtgui/preferences.h b/rtgui/preferences.h
index 91951a2df..50f72a957 100644
--- a/rtgui/preferences.h
+++ b/rtgui/preferences.h
@@ -24,6 +24,7 @@
#include "options.h"
#include
#include "rtwindow.h"
+#include "dynamicprofilepanel.h"
class Preferences : public Gtk::Dialog, public ProfileStoreListener
{
@@ -201,6 +202,8 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener
Gtk::CheckButton* ckbHideTPVScrollbar;
Gtk::CheckButton* ckbUseIconNoText;
+ DynamicProfilePanel *dynProfilePanel;
+
Glib::ustring storedValueRaw;
Glib::ustring storedValueImg;
@@ -239,6 +242,7 @@ class Preferences : public Gtk::Dialog, public ProfileStoreListener
Gtk::Widget* getBatchProcPanel ();
Gtk::Widget* getPerformancePanel ();
Gtk::Widget* getSoundPanel ();
+ Gtk::Widget* getDynProfilePanel ();
public:
explicit Preferences (RTWindow *rtwindow);
diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc
index 6b1171d23..d7bf18ab5 100644
--- a/rtgui/profilestore.cc
+++ b/rtgui/profilestore.cc
@@ -20,13 +20,14 @@
#include "options.h"
#include "toolpanel.h"
#include "guiutils.h"
+#include "dynamicprofile.h"
ProfileStore profileStore;
using namespace rtengine;
using namespace rtengine::procparams;
-ProfileStore::ProfileStore () : parseMutex(nullptr), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(nullptr), internalDefaultEntry(nullptr)
+ProfileStore::ProfileStore () : parseMutex(nullptr), storeState(STORESTATE_NOTINITIALIZED), internalDefaultProfile(nullptr), internalDefaultEntry(nullptr), internalDynamicEntry(nullptr), dynamicRules(new std::vector())
{
internalDefaultProfile = new AutoPartialProfile();
internalDefaultProfile->set(true);
@@ -42,6 +43,7 @@ bool ProfileStore::init ()
storeState = STORESTATE_BEINGINITIALIZED;
parseMutex = new MyMutex();
_parseProfiles ();
+ loadDynamicProfileRules(*dynamicRules);
storeState = STORESTATE_INITIALIZED;
}
@@ -63,6 +65,8 @@ ProfileStore::~ProfileStore ()
partProfiles.clear ();
clearFileList();
delete internalDefaultProfile;
+ delete internalDefaultEntry;
+ delete internalDynamicEntry;
lock.release();
delete parseMutex;
parseMutex = nullptr;
@@ -140,6 +144,10 @@ void ProfileStore::_parseProfiles ()
entries.push_back(internalDefaultEntry);
partProfiles[internalDefaultEntry] = internalDefaultProfile;
+ if (!internalDynamicEntry) {
+ internalDynamicEntry = new ProfileStoreEntry(Glib::ustring("(") + M("PROFILEPANEL_PDYNAMIC") + Glib::ustring(")"), PSET_FILE, 0, 0);
+ // do not add it to the entries. This is here only for the preferences dialog
+ }
// Check if the default profiles has been found.
if (findEntryFromFullPathU(options.defProfRaw) == nullptr) {
@@ -273,7 +281,7 @@ const ProfileStoreEntry* ProfileStore::findEntryFromFullPathU(Glib::ustring path
return nullptr;
}
- if (path == DEFPROFILE_INTERNAL) {
+ if (path == DEFPROFILE_INTERNAL || path == DEFPROFILE_DYNAMIC) {
return internalDefaultEntry;
}
@@ -499,6 +507,20 @@ void ProfileStore::dumpFolderList()
printf("\n");
}
+
+const std::vector &ProfileStore::getDynamicProfileRules() const
+{
+ return *dynamicRules;
+}
+
+
+void ProfileStore::setDynamicProfileRules(const std::vector &r)
+{
+ *dynamicRules = r;
+}
+
+
+
ProfileStoreEntry::ProfileStoreEntry() : label(""), type(PSET_FOLDER), parentFolderId(0), folderId(0) {}
ProfileStoreEntry::ProfileStoreEntry(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder) : label(label), type(type), parentFolderId(parentFolder), folderId(folder) {}
@@ -570,12 +592,12 @@ void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow,
// creating and assigning the custom Label object
newSubMenu[methodColumns.label] = entry->label;
newSubMenu[methodColumns.profileStoreEntry] = entry;
-
+#if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION == 18
// HACK: Workaround for bug in Gtk+ 3.18...
Gtk::TreeModel::Row menuHeader = *(refTreeModel->append(newSubMenu->children()));
- menuHeader[methodColumns.label] = entry->label;
+ menuHeader[methodColumns.label] = "-";
menuHeader[methodColumns.profileStoreEntry] = entry;
-
+#endif
refreshProfileList_ (&newSubMenu, entry->folderId, false, entryList);
} else {
refreshProfileList_ (parentRow, entry->folderId, true, entryList);
@@ -603,7 +625,6 @@ void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow,
*/
void ProfileStoreComboBox::updateProfileList ()
{
-
// clear items
clear();
refTreeModel.clear();
@@ -710,6 +731,11 @@ Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath (Glib::ustring path)
return row;
}
+ if (path == DEFPROFILE_DYNAMIC) {
+ row = findRowFromEntry(profileStore.getInternalDynamicPSE());
+ return row;
+ }
+
// removing the filename
Glib::ustring fName = Glib::path_get_basename(path);
@@ -758,6 +784,10 @@ Glib::ustring ProfileStoreComboBox::getFullPathFromActiveRow()
return Glib::ustring(DEFPROFILE_INTERNAL);
}
+ if (currEntry == profileStore.getInternalDynamicPSE()) {
+ return Glib::ustring(DEFPROFILE_DYNAMIC);
+ }
+
path = Glib::build_filename(profileStore.getPathFromId(currEntry->parentFolderId), currEntry->label);
}
diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h
index ab45a0867..ad569c180 100644
--- a/rtgui/profilestore.h
+++ b/rtgui/profilestore.h
@@ -31,6 +31,9 @@
#include "guiutils.h"
+// forward decl
+class DynamicProfileRule;
+
/** @brief This will implement callback functions for the ProfileStore
*
*/
@@ -144,6 +147,7 @@ private:
StoreState storeState;
rtengine::procparams::AutoPartialProfile *internalDefaultProfile;
ProfileStoreEntry *internalDefaultEntry;
+ ProfileStoreEntry *internalDynamicEntry;
/** Alphabetically ordered list of folder and files through Gtk::Label sub-class;
* ready to be used in Menu and Combobox
@@ -160,6 +164,9 @@ private:
/** List of the client of this store */
std::list listeners;
+ /** cache for dynamic profile rules */
+ std::unique_ptr> dynamicRules;
+
/** @brief Method to recursively parse a profile folder with a level depth arbitrarily limited to 3
*
* @param realPath current full path of the scanned directory ; e.g.: ~/MyProfiles/
@@ -198,6 +205,14 @@ public:
return internalDefaultEntry;
}
+ const ProfileStoreEntry* getInternalDynamicPSE()
+ {
+ return internalDynamicEntry;
+ }
+
+ const std::vector &getDynamicProfileRules() const;
+ void setDynamicProfileRules(const std::vector &r);
+
void addListener(ProfileStoreListener *listener);
void removeListener(ProfileStoreListener *listener);
diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc
index 03ef49a5e..62f543d4a 100644
--- a/rtgui/thumbnail.cc
+++ b/rtgui/thumbnail.cc
@@ -30,6 +30,7 @@
#include "profilestore.h"
#include "batchqueue.h"
#include "extprog.h"
+#include "dynamicprofile.h"
using namespace rtengine::procparams;
@@ -216,8 +217,31 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu
const CacheImageData* cfs = getCacheImageData();
Glib::ustring defaultPparamsPath = options.findProfilePath(defProf);
+ const bool create = (!hasProcParams() || forceCPB);
- if (!options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) {
+ const Glib::ustring outFName =
+ (options.paramsLoadLocation == PLL_Input) ?
+ fname + paramFileExtension :
+ getCacheFileName("profiles", paramFileExtension);
+
+ if (defProf == DEFPROFILE_DYNAMIC && create && cfs && cfs->exifValid) {
+ rtengine::ImageMetaData* imageMetaData;
+ if (getType() == FT_Raw) {
+ rtengine::RawMetaDataLocation metaData = rtengine::Thumbnail::loadMetaDataFromRaw(fname);
+ imageMetaData = rtengine::ImageMetaData::fromFile (fname, &metaData);
+ } else {
+ imageMetaData = rtengine::ImageMetaData::fromFile (fname, nullptr);
+ }
+ PartialProfile *pp = loadDynamicProfile(imageMetaData);
+ int err = pp->pparams->save(outFName);
+ pp->deleteInstance();
+ delete pp;
+ if (!err) {
+ loadProcParams();
+ }
+ }
+
+ if (!options.CPBPath.empty() && !defaultPparamsPath.empty() && create && cfs && cfs->exifValid) {
// First generate the communication file, with general values and EXIF metadata
rtengine::ImageMetaData* imageMetaData;
@@ -233,14 +257,6 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu
const rtexif::TagDirectory* exifDir = nullptr;
if (imageMetaData && (exifDir = imageMetaData->getExifData())) {
- Glib::ustring outFName;
-
- if (options.paramsLoadLocation == PLL_Input) {
- outFName = fname + paramFileExtension;
- } else {
- outFName = getCacheFileName("profiles", paramFileExtension);
- }
-
exifDir->CPBDump(tmpFileName, fname, outFName,
defaultPparamsPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(defaultPparamsPath, Glib::path_get_basename(defProf) + paramFileExtension),
cfs,