/* -*- 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 "rtengine/profilestore.h"
#include "rtengine/rtengine.h"
#include "rtengine/dynamicprofile.h"
#include "rtengine/settings.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::Box *hb = Gtk::manage (new Gtk::Box());
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_optional (M ("EXIFFILTER_PATH"), has_path_, path_);
imagetype_ = Gtk::manage (new MyComboBoxText());
imagetype_->append(Glib::ustring("(") + M("DYNPROFILEEDITOR_IMGTYPE_ANY") + ")");
imagetype_->append(M("DYNPROFILEEDITOR_IMGTYPE_STD"));
imagetype_->append(M("DYNPROFILEEDITOR_IMGTYPE_HDR"));
imagetype_->append(M("DYNPROFILEEDITOR_IMGTYPE_PS"));
imagetype_->set_active(0);
hb = Gtk::manage (new Gtk::Box());
hb->pack_start (*Gtk::manage (new Gtk::Label (M ("EXIFFILTER_IMAGETYPE"))), false, false, 4);
hb->pack_start (*imagetype_, true, true, 2);
get_content_area()->pack_start (*hb, Gtk::PACK_SHRINK, 4);
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);
has_path_->set_active (rule.path.enabled);
path_->set_text (rule.path.value);
if (!rule.imagetype.enabled) {
imagetype_->set_active(0);
} else if (rule.imagetype.value == "STD") {
imagetype_->set_active(1);
} else if (rule.imagetype.value == "HDR") {
imagetype_->set_active(2);
} else if (rule.imagetype.value == "PS") {
imagetype_->set_active(3);
} else {
imagetype_->set_active(0);
}
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.path.enabled = has_path_->get_active();
ret.path.value = path_->get_text();
ret.imagetype.enabled = imagetype_->get_active_row_number() > 0;
switch (imagetype_->get_active_row_number()) {
case 1:
ret.imagetype.value = "STD";
break;
case 2:
ret.imagetype.value = "HDR";
break;
case 3:
ret.imagetype.value = "PS";
break;
default:
ret.imagetype.value = "";
}
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::Box *hb = Gtk::manage (new Gtk::Box());
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::Box *hb = Gtk::manage (new Gtk::Box());
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"))
{
set_orientation(Gtk::ORIENTATION_VERTICAL);
add (vbox_);
treeview_.set_grid_lines (Gtk::TREE_VIEW_GRID_LINES_VERTICAL);
scrolledwindow_.add (treeview_);
scrolledwindow_.set_vexpand();
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_PATH"), *cell);
col = treeview_.get_column (cols_count - 1);
if (col) {
col->set_cell_data_func (
*cell, sigc::mem_fun (
*this, &DynamicProfilePanel::render_path));
}
cell = Gtk::manage (new Gtk::CellRendererText());
cols_count = treeview_.append_column (M ("EXIFFILTER_IMAGETYPE"), *cell);
col = treeview_.get_column (cols_count - 1);
if (col) {
col->set_cell_data_func (
*cell, sigc::mem_fun (
*this, &DynamicProfilePanel::render_imagetype));
}
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::getInstance()->getRules()) {
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_.path] = rule.path;
row[columns_.imagetype] = rule.imagetype;
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.path = row[columns_.path];
ret.profilepath = row[columns_.profilepath];
ret.imagetype = row[columns_.imagetype];
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::getInstance()->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::FramesMetaData::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::FramesMetaData::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);
}
void DynamicProfilePanel::render_path (
Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
{
RENDER_OPTIONAL_ (path);
}
void DynamicProfilePanel::render_imagetype (
Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
{
auto row = *iter;
Gtk::CellRendererText *ct = static_cast(cell);
DynamicProfileRule::Optional o = row[columns_.imagetype];
if (o.enabled) {
ct->property_text() = M(std::string("DYNPROFILEEDITOR_IMGTYPE_") + o.value);
} else { \
ct->property_text() = "";
}
}
#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()));
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++));
}
ProfileStore::getInstance()->setRules (rules);
if (!ProfileStore::getInstance()->storeRules()) {
printf ("Error in saving dynamic profile rules\n");
} else if (rtengine::settings->verbose) {
printf ("Saved %d dynamic profile rules\n", int (rules.size()));
}
}