From d3a5a8ee9606503927c33e4023d2065b2d65562c Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Wed, 22 Feb 2017 18:47:00 +0100 Subject: [PATCH 01/19] started working on support for dynamic processing profiles --- rtengine/procparams.cc | 2 + rtgui/CMakeLists.txt | 3 +- rtgui/dynamicprofile.cc | 180 ++++++++++++++++++++++++++++++++++++++++ rtgui/dynamicprofile.h | 73 ++++++++++++++++ rtgui/main.cc | 15 +++- rtgui/options.cc | 4 + rtgui/options.h | 2 + rtgui/profilestore.cc | 21 ++++- rtgui/profilestore.h | 6 ++ rtgui/thumbnail.cc | 19 ++++- 10 files changed, 318 insertions(+), 7 deletions(-) create mode 100644 rtgui/dynamicprofile.cc create mode 100644 rtgui/dynamicprofile.h 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..e327cb4b6 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) include_directories (BEFORE "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc new file mode 100644 index 000000000..e2cedbb0a --- /dev/null +++ b/rtgui/dynamicprofile.cc @@ -0,0 +1,180 @@ +/* -*- 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 + +using namespace rtengine; +using namespace rtengine::procparams; + +DynamicProfileEntry::DynamicProfileEntry(): + serial_number(0), + has_iso(false), iso_min(0), iso_max(1000000), + has_fnumber(false), fnumber_min(0.0), fnumber_max(1000.0), + has_focallen(false), focallen_min(0.0), focallen_max(1000000.0), + has_shutterspeed(false), shutterspeed_min(1000.0), shutterspeed_max(1.0/1000000.0), + has_expcomp(false), expcomp_min(-100.0), expcomp_max(100.0), + has_make(false), make(""), + has_model(false), model(""), + has_lens(false), lens(""), + profilepath("") +{ +} + + +bool DynamicProfileEntry::operator<(const DynamicProfileEntry &other) const +{ + return serial_number < other.serial_number; +} + + +bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) +{ + if (has_iso) { + int iso = im->getISOSpeed(); + if (iso < iso_min || iso > iso_max) { + return false; + } + } + if (has_fnumber) { + double fnumber = im->getFNumber(); + if (fnumber < fnumber_min || fnumber > fnumber_max) { + return false; + } + } + if (has_focallen) { + double focallen = im->getFocalLen(); + if (focallen < focallen_min || focallen > focallen_max) { + return false; + } + } + if (has_shutterspeed) { + double shutterspeed = im->getShutterSpeed(); + if (shutterspeed < shutterspeed_min || shutterspeed > shutterspeed_max){ + return false; + } + } + if (has_expcomp) { + double expcomp = im->getExpComp(); + if (expcomp < expcomp_min || expcomp > expcomp_max) { + return false; + } + } + if (has_make) { + if (im->getMake() != make) { + return false; + } + } + if (has_model) { + if (im->getModel() != model) { + return false; + } + } + if (has_lens) { + if (im->getLens() != lens) { + return false; + } + } + return true; +} + + +bool loadDynamicProfileEntries(std::vector &out) +{ + out.clear(); + Glib::KeyFile kf; + if (!kf.load_from_file( + Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) { + return false; + } + printf("loading dynamic profiles...\n"); + auto groups = kf.get_groups(); + for (auto group : groups) { + // groups are of the form "entry N", where N is a positive integer + if (group.find("entry ") != 0) { + return false; + } + std::istringstream buf(group.c_str() + 6); + int serial = 0; + if (!(buf >> serial) || !buf.eof()) { + return false; + } + printf(" loading entry %d\n", serial); + + out.emplace_back(DynamicProfileEntry()); + DynamicProfileEntry &entry = out.back(); + entry.serial_number = serial; + entry.has_iso = kf.get_boolean(group, "has_iso"); + entry.iso_min = kf.get_integer(group, "iso_min"); + entry.iso_max = kf.get_integer(group, "iso_max"); + + entry.has_fnumber = kf.get_boolean(group, "has_fnumber"); + entry.fnumber_min = kf.get_double(group, "fnumber_min"); + entry.fnumber_max = kf.get_double(group, "fnumber_max"); + + entry.has_focallen = kf.get_boolean(group, "has_focallen"); + entry.focallen_min = kf.get_double(group, "focallen_min"); + entry.focallen_max = kf.get_double(group, "focallen_max"); + + entry.has_shutterspeed = kf.get_boolean(group, "has_shutterspeed"); + entry.shutterspeed_min = kf.get_double(group, "shutterspeed_min"); + entry.shutterspeed_max = kf.get_double(group, "shutterspeed_max"); + + entry.has_expcomp = kf.get_boolean(group, "has_expcomp"); + entry.expcomp_min = kf.get_double(group, "expcomp_min"); + entry.expcomp_max = kf.get_double(group, "expcomp_max"); + + entry.has_make = kf.get_boolean(group, "has_make"); + entry.make = kf.get_string(group, "make"); + + entry.has_model = kf.get_boolean(group, "has_model"); + entry.model = kf.get_string(group, "model"); + + entry.has_lens = kf.get_boolean(group, "has_lens"); + entry.lens = kf.get_string(group, "lens"); + + entry.profilepath = kf.get_string(group, "profilepath"); + } + std::sort(out.begin(), out.end()); + return true; +} + + +PartialProfile *loadDynamicProfile(const ImageMetaData *im) +{ + PartialProfile *ret = new PartialProfile(true, true); + std::vector entries; + if (loadDynamicProfileEntries(entries)) { + for (auto &entry : entries) { + if (entry.matches(im)) { + printf("found matching profile %s\n", + entry.profilepath.c_str()); + PartialProfile p(true, true); + if (!p.load(options.findProfilePath(entry.profilepath))) { + p.applyTo(ret->pparams); + } else { + printf("ERROR loading matching profile\n"); + } + p.deleteInstance(); + } + } + } + return ret; +} + diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h new file mode 100644 index 000000000..5d8596c77 --- /dev/null +++ b/rtgui/dynamicprofile.h @@ -0,0 +1,73 @@ +/* -*- 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 "options.h" + + +class DynamicProfileEntry { +public: + DynamicProfileEntry(); + bool matches(const rtengine::ImageMetaData *im); + bool operator<(const DynamicProfileEntry &other) const; + + int serial_number; + + bool has_iso; + int iso_min; + int iso_max; + + bool has_fnumber; + double fnumber_min; + double fnumber_max; + + bool has_focallen; + double focallen_min; + double focallen_max; + + bool has_shutterspeed; + double shutterspeed_min; + double shutterspeed_max; + + bool has_expcomp; + double expcomp_min; + double expcomp_max; + + bool has_make; + std::string make; + + bool has_model; + std::string model; + + bool has_lens; + std::string lens; + + Glib::ustring profilepath; +}; + + +bool loadDynamicProfileEntries(std::vector &out); +rtengine::procparams::PartialProfile *loadDynamicProfile( + const rtengine::ImageMetaData *im); + + +#endif // _DYNAMICPROFILE_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/profilestore.cc b/rtgui/profilestore.cc index 6b1171d23..9f32b735f 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -26,7 +26,7 @@ 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) { internalDefaultProfile = new AutoPartialProfile(); internalDefaultProfile->set(true); @@ -63,6 +63,8 @@ ProfileStore::~ProfileStore () partProfiles.clear (); clearFileList(); delete internalDefaultProfile; + delete internalDefaultEntry; + delete internalDynamicEntry; lock.release(); delete parseMutex; parseMutex = nullptr; @@ -140,6 +142,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 +279,7 @@ const ProfileStoreEntry* ProfileStore::findEntryFromFullPathU(Glib::ustring path return nullptr; } - if (path == DEFPROFILE_INTERNAL) { + if (path == DEFPROFILE_INTERNAL || path == DEFPROFILE_DYNAMIC) { return internalDefaultEntry; } @@ -603,7 +609,6 @@ void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow, */ void ProfileStoreComboBox::updateProfileList () { - // clear items clear(); refTreeModel.clear(); @@ -622,6 +627,7 @@ void ProfileStoreComboBox::updateProfileList () // special case for the Internal default entry addRow(profileStore.getInternalDefaultPSE()); } + addRow(profileStore.getInternalDynamicPSE()); // releasing the profilestore's entry list mutex profileStore.releaseFileList(); @@ -710,6 +716,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 +769,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..5e5591e15 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -144,6 +144,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 @@ -198,6 +199,11 @@ public: return internalDefaultEntry; } + const ProfileStoreEntry* getInternalDynamicPSE() + { + return internalDynamicEntry; + } + void addListener(ProfileStoreListener *listener); void removeListener(ProfileStoreListener *listener); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 03ef49a5e..b560c5f7b 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; @@ -217,7 +218,23 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu const CacheImageData* cfs = getCacheImageData(); Glib::ustring defaultPparamsPath = options.findProfilePath(defProf); - if (!options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { + if (defProf == DEFPROFILE_DYNAMIC && (!hasProcParams() || forceCPB) && 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); + if (options.paramsLoadLocation == PLL_Input) { + pp->pparams->save(fname + paramFileExtension); + } else { + pp->pparams->save(getCacheFileName ("profiles", paramFileExtension)); + } + pp->deleteInstance(); + delete pp; + } else if (defProf != DEFPROFILE_DYNAMIC && !options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { // First generate the communication file, with general values and EXIF metadata rtengine::ImageMetaData* imageMetaData; From afda76cf3c06069dac9244451938cc1e90b3166e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 00:41:44 +0100 Subject: [PATCH 02/19] refactoring/cleanup of class for dynamic processing profile rules --- rtgui/dynamicprofile.cc | 231 +++++++++++++++++++++++++--------------- rtgui/dynamicprofile.h | 67 ++++++------ 2 files changed, 179 insertions(+), 119 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index e2cedbb0a..aa3255467 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -23,17 +23,26 @@ 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_MIN = 1.0/10000.0; +const double SHUTTERSPEED_MAX = 1000.0; +const double EXPCOMP_MIN = -20.0; +const double EXPCOMP_MAX = 20.0; + +} // namespace + + DynamicProfileEntry::DynamicProfileEntry(): serial_number(0), - has_iso(false), iso_min(0), iso_max(1000000), - has_fnumber(false), fnumber_min(0.0), fnumber_max(1000.0), - has_focallen(false), focallen_min(0.0), focallen_max(1000000.0), - has_shutterspeed(false), shutterspeed_min(1000.0), shutterspeed_max(1.0/1000000.0), - has_expcomp(false), expcomp_min(-100.0), expcomp_max(100.0), - has_make(false), make(""), - has_model(false), model(""), - has_lens(false), lens(""), - profilepath("") + iso(0, ISO_MAX), + fnumber(0, FNUMBER_MAX), + focallen(0, FOCALLEN_MAX), + shutterspeed(SHUTTERSPEED_MIN, SHUTTERSPEED_MAX), + expcomp(EXPCOMP_MIN, EXPCOMP_MAX) { } @@ -46,61 +55,102 @@ bool DynamicProfileEntry::operator<(const DynamicProfileEntry &other) const bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) { - if (has_iso) { - int iso = im->getISOSpeed(); - if (iso < iso_min || iso > iso_max) { - return false; - } - } - if (has_fnumber) { - double fnumber = im->getFNumber(); - if (fnumber < fnumber_min || fnumber > fnumber_max) { - return false; - } - } - if (has_focallen) { - double focallen = im->getFocalLen(); - if (focallen < focallen_min || focallen > focallen_max) { - return false; - } - } - if (has_shutterspeed) { - double shutterspeed = im->getShutterSpeed(); - if (shutterspeed < shutterspeed_min || shutterspeed > shutterspeed_max){ - return false; - } - } - if (has_expcomp) { - double expcomp = im->getExpComp(); - if (expcomp < expcomp_min || expcomp > expcomp_max) { - return false; - } - } - if (has_make) { - if (im->getMake() != make) { - return false; - } - } - if (has_model) { - if (im->getModel() != model) { - return false; - } - } - if (has_lens) { - if (im->getLens() != lens) { - return false; - } - } - return true; + return (iso(im->getISOSpeed()) + && fnumber(im->getFNumber()) + && focallen(im->getFocalLen()) + && shutterspeed(im->getShutterSpeed()) + && expcomp(im->getExpComp()) + && make(im->getMake()) + && model(im->getModel()) + && lens(im->getLens())); } +namespace { + +void get_int_range(DynamicProfileEntry::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(DynamicProfileEntry::Range &dest, + const Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key) +{ + try { + int min = kf.get_double(group, key + "_min"); + int max = kf.get_double(group, key + "_max"); + if (min <= max) { + dest.min = min; + dest.max = max; + } + } catch (Glib::KeyFileError &e) { + } +} + + +void get_optional(DynamicProfileEntry::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 DynamicProfileEntry::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 DynamicProfileEntry::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 DynamicProfileEntry::Optional &val) +{ + kf.set_boolean(group, key + "_enabled", val.enabled); + kf.set_string(group, key + "_value", val.value); +} + +} // namespace + bool loadDynamicProfileEntries(std::vector &out) { out.clear(); Glib::KeyFile kf; - if (!kf.load_from_file( - Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) { + try { + if (!kf.load_from_file( + Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) { + return false; + } + } catch (Glib::Error &e) { return false; } printf("loading dynamic profiles...\n"); @@ -120,42 +170,48 @@ bool loadDynamicProfileEntries(std::vector &out) out.emplace_back(DynamicProfileEntry()); DynamicProfileEntry &entry = out.back(); entry.serial_number = serial; - entry.has_iso = kf.get_boolean(group, "has_iso"); - entry.iso_min = kf.get_integer(group, "iso_min"); - entry.iso_max = kf.get_integer(group, "iso_max"); - - entry.has_fnumber = kf.get_boolean(group, "has_fnumber"); - entry.fnumber_min = kf.get_double(group, "fnumber_min"); - entry.fnumber_max = kf.get_double(group, "fnumber_max"); - - entry.has_focallen = kf.get_boolean(group, "has_focallen"); - entry.focallen_min = kf.get_double(group, "focallen_min"); - entry.focallen_max = kf.get_double(group, "focallen_max"); - - entry.has_shutterspeed = kf.get_boolean(group, "has_shutterspeed"); - entry.shutterspeed_min = kf.get_double(group, "shutterspeed_min"); - entry.shutterspeed_max = kf.get_double(group, "shutterspeed_max"); - - entry.has_expcomp = kf.get_boolean(group, "has_expcomp"); - entry.expcomp_min = kf.get_double(group, "expcomp_min"); - entry.expcomp_max = kf.get_double(group, "expcomp_max"); - - entry.has_make = kf.get_boolean(group, "has_make"); - entry.make = kf.get_string(group, "make"); - - entry.has_model = kf.get_boolean(group, "has_model"); - entry.model = kf.get_string(group, "model"); - - entry.has_lens = kf.get_boolean(group, "has_lens"); - entry.lens = kf.get_string(group, "lens"); - - entry.profilepath = kf.get_string(group, "profilepath"); + get_int_range(entry.iso, kf, group, "iso"); + get_double_range(entry.fnumber, kf, group, "fnumber"); + get_double_range(entry.focallen, kf, group, "focallen"); + get_double_range(entry.shutterspeed, kf, group, "shutterspeed"); + get_double_range(entry.expcomp, kf, group, "expcomp"); + get_optional(entry.make, kf, group, "make"); + get_optional(entry.model, kf, group, "model"); + get_optional(entry.lens, kf, group, "lens"); + try { + entry.profilepath = kf.get_string(group, "profilepath"); + } catch (Glib::KeyFileError &) { + out.pop_back(); + } } std::sort(out.begin(), out.end()); return true; } +bool storeDynamicProfileEntries(const std::vector &entries) +{ + printf("saving dynamic profiles...\n"); + Glib::KeyFile kf; + for (auto &entry : entries) { + std::ostringstream buf; + buf << "entry " << entry.serial_number; + Glib::ustring group = buf.str(); + set_int_range(kf, group, "iso", entry.iso); + set_double_range(kf, group, "fnumber", entry.fnumber); + set_double_range(kf, group, "focallen", entry.focallen); + set_double_range(kf, group, "shutterspeed", entry.shutterspeed); + set_double_range(kf, group, "expcomp", entry.expcomp); + set_optional(kf, group, "make", entry.make); + set_optional(kf, group, "model", entry.model); + set_optional(kf, group, "lens", entry.lens); + kf.set_string(group, "profilepath", entry.profilepath); + } + return kf.save_to_file( + Glib::build_filename(Options::rtdir, "dynamicprofile.cfg")); +} + + PartialProfile *loadDynamicProfile(const ImageMetaData *im) { PartialProfile *ret = new PartialProfile(true, true); @@ -177,4 +233,3 @@ PartialProfile *loadDynamicProfile(const ImageMetaData *im) } return ret; } - diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h index 5d8596c77..a70a0bff7 100644 --- a/rtgui/dynamicprofile.h +++ b/rtgui/dynamicprofile.h @@ -20,52 +20,57 @@ #define _DYNAMICPROFILE_H_ #include - +#include #include "options.h" class DynamicProfileEntry { 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; + } + }; + + template + struct Optional { + T value; + bool enabled; + explicit Optional(T v=T(), bool e=false): value(v), enabled(e) {} + + bool operator()(const T &val) const + { + return !enabled || value == val; + } + }; + DynamicProfileEntry(); bool matches(const rtengine::ImageMetaData *im); bool operator<(const DynamicProfileEntry &other) const; int serial_number; - - bool has_iso; - int iso_min; - int iso_max; - - bool has_fnumber; - double fnumber_min; - double fnumber_max; - - bool has_focallen; - double focallen_min; - double focallen_max; - - bool has_shutterspeed; - double shutterspeed_min; - double shutterspeed_max; - - bool has_expcomp; - double expcomp_min; - double expcomp_max; - - bool has_make; - std::string make; - - bool has_model; - std::string model; - - bool has_lens; - std::string lens; - + Range iso; + Range fnumber; + Range focallen; + Range shutterspeed; + Range expcomp; + Optional make; + Optional model; + Optional lens; Glib::ustring profilepath; }; bool loadDynamicProfileEntries(std::vector &out); +bool storeDynamicProfileEntries( + const std::vector &entries); + rtengine::procparams::PartialProfile *loadDynamicProfile( const rtengine::ImageMetaData *im); From 7d1bebf3419d97433bd4af5f4c0685d3367666f6 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 00:41:57 +0100 Subject: [PATCH 03/19] added GUI for editing dynamic processing profile rules --- rtgui/CMakeLists.txt | 2 +- rtgui/dynamicprofilepanel.cc | 559 +++++++++++++++++++++++++++++++++++ rtgui/dynamicprofilepanel.h | 149 ++++++++++ rtgui/preferences.cc | 12 + rtgui/preferences.h | 4 + rtgui/profilestore.cc | 1 - 6 files changed, 725 insertions(+), 2 deletions(-) create mode 100644 rtgui/dynamicprofilepanel.cc create mode 100644 rtgui/dynamicprofilepanel.h diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index e327cb4b6..153c1a0ef 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -31,7 +31,7 @@ set (BASESOURCEFILES dirpyrequalizer.cc hsvequalizer.cc defringe.cc popupcommon.cc popupbutton.cc popuptogglebutton.cc sharpenedge.cc sharpenmicro.cc colorappearance.cc filmsimulation.cc prsharpening.cc - dynamicprofile.cc) + dynamicprofile.cc dynamicprofilepanel.cc) include_directories (BEFORE "${CMAKE_CURRENT_BINARY_DIR}") diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc new file mode 100644 index 000000000..bfe311efb --- /dev/null +++ b/rtgui/dynamicprofilepanel.cc @@ -0,0 +1,559 @@ +/* -*- 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 +#include + +namespace { + +template +Glib::ustring to_str(V n) +{ + std::ostringstream buf; + buf << std::setprecision(1) << std::fixed << n; + return buf.str(); +} + + +int to_int(const Glib::ustring &s) +{ + std::istringstream buf(s); + int r = -1; + buf >> r; + return r; +} + + +double to_double(const Glib::ustring &s) +{ + std::istringstream buf(s); + double r = 0.0; + buf >> r; + return r; +} + +} // namespace + + +//----------------------------------------------------------------------------- +// 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("DYNPROFILEEDITOR_CAMERA_MAKE"), has_make, make); + add_optional(M("DYNPROFILEEDITOR_CAMERA_MODEL"), has_model, model); + 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_entry( + const DynamicProfileEntry &entry) +{ + iso_min_->set_value(entry.iso.min); + iso_max_->set_value(entry.iso.max); + + fnumber_min_->set_value(entry.fnumber.min); + fnumber_max_->set_value(entry.fnumber.max); + + focallen_min_->set_value(entry.focallen.min); + focallen_max_->set_value(entry.focallen.max); + + shutterspeed_min_->set_value(entry.shutterspeed.min); + shutterspeed_max_->set_value(entry.shutterspeed.max); + + expcomp_min_->set_value(entry.expcomp.min); + expcomp_max_->set_value(entry.expcomp.max); + + has_make->set_active(entry.make.enabled); + make->set_text(entry.make.value); + + has_model->set_active(entry.model.enabled); + model->set_text(entry.model.value); + + has_lens->set_active(entry.lens.enabled); + lens->set_text(entry.lens.value); + + profilepath_->updateProfileList(); + if (!profilepath_->setActiveRowFromFullPath(entry.profilepath)) { + profilepath_->setInternalEntry(); + } +} + + +DynamicProfileEntry DynamicProfilePanel::EditDialog::get_entry() +{ + DynamicProfileEntry 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.make.enabled = has_make->get_active(); + ret.make.value = make->get_text(); + + ret.model.enabled = has_model->get_active(); + ret.model.value = model->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() +{ + DynamicProfileEntry default_entry; + 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_entry.iso.min, default_entry.iso.max); + iso_max_->set_range(default_entry.iso.min, default_entry.iso.max); + iso_min_->set_value(default_entry.iso.min); + iso_max_->set_value(default_entry.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_entry. name .min, \ + default_entry. name .max); \ + name ## _max_->set_range(default_entry. name .min, \ + default_entry. name .max); \ + name ## _min_->set_value(default_entry. name .min); \ + name ## _max_->set_value(default_entry. name .max) + + DOIT_(fnumber); + DOIT_(focallen); + DOIT_(shutterspeed); + DOIT_(expcomp); +#undef DOIT_ + + 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); +} + + +//----------------------------------------------------------------------------- +// 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_); + + //Add the TreeView, inside a ScrolledWindow, with the button underneath: + scrolledwindow_.add(treeview_); + + //Only show the scrollbars when they are necessary: + 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)); + + //Create the Tree model: + treemodel_ = Gtk::ListStore::create(columns_); + treeview_.set_model(treemodel_); + + //Add the TreeView's view columns: + 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("DYNPROFILEEDITOR_CAMERA_MAKE"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_make)); + } + cell = Gtk::manage(new Gtk::CellRendererText()); + cols_count = treeview_.append_column( + M("DYNPROFILEEDITOR_CAMERA_MODEL"), *cell); + col = treeview_.get_column(cols_count - 1); + if (col) { + col->set_cell_data_func( + *cell, sigc::mem_fun( + *this, &DynamicProfilePanel::render_model)); + } + 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(); + + std::vector entries; + if (loadDynamicProfileEntries(entries)) { + for (auto &e : entries) { + add_entry(e); + } + } +} + + +void DynamicProfilePanel::update_entry(Gtk::TreeModel::Row row, + const DynamicProfileEntry &entry) +{ + row[columns_.iso] = entry.iso; + row[columns_.fnumber] = entry.fnumber; + row[columns_.focallen] = entry.focallen; + row[columns_.shutterspeed] = entry.shutterspeed; + row[columns_.expcomp] = entry.expcomp; + row[columns_.make] = entry.make; + row[columns_.model] = entry.model; + row[columns_.lens] = entry.lens; + row[columns_.profilepath] = entry.profilepath; +} + +void DynamicProfilePanel::add_entry(const DynamicProfileEntry &entry) +{ + auto row = *(treemodel_->append()); + update_entry(row, entry); +} + + +DynamicProfileEntry DynamicProfilePanel::to_entry(Gtk::TreeModel::Row row, + int serial) +{ + DynamicProfileEntry 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.make = row[columns_.make]; + ret.model = row[columns_.model]; + 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]; + ct->property_text() = value; +} + + +#define RENDER_RANGE_(tp, name) \ + auto row = *iter; \ + Gtk::CellRendererText *ct = static_cast(cell); \ + DynamicProfileEntry::Range r = row[columns_. name]; \ + auto value = to_str(r.min) + " - " + to_str(r.max); \ + ct->property_text() = value; + +void DynamicProfilePanel::render_iso( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(int, iso); +} + + +void DynamicProfilePanel::render_fnumber( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(double, fnumber); +} + + +void DynamicProfilePanel::render_focallen( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(double, focallen); +} + + +void DynamicProfilePanel::render_shutterspeed( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(double, shutterspeed); +} + + +void DynamicProfilePanel::render_expcomp( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_RANGE_(double, expcomp); +} + +#undef RENDER_RANGE_ + +#define RENDER_OPTIONAL_(name) \ + auto row = *iter; \ + Gtk::CellRendererText *ct = static_cast(cell); \ + DynamicProfileEntry::Optional o = row[columns_. name]; \ + if (o.enabled) { \ + ct->property_text() = o.value; \ + } else { \ + ct->property_text() = ""; \ + } + +void DynamicProfilePanel::render_make( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_OPTIONAL_(make); +} + + +void DynamicProfilePanel::render_model( + Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) +{ + RENDER_OPTIONAL_(model); +} + + +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) { + DynamicProfileEntry entry = d.get_entry(); + add_entry(entry); + } +} + + +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_entry(to_entry(row)); + int status = d.run(); + if (status == 1) { + update_entry(row, d.get_entry()); + } +} + + +void DynamicProfilePanel::save() +{ + std::vector entries; + int serial = 1; + for (auto row : treemodel_->children()) { + entries.emplace_back(to_entry(row, serial++)); + } + if (!storeDynamicProfileEntries(entries)) { + printf("Error in saving dynamic profile rules\n"); + } else { + printf("Saved %d dynamic profile rules\n", int(entries.size())); + } +} diff --git a/rtgui/dynamicprofilepanel.h b/rtgui/dynamicprofilepanel.h new file mode 100644 index 000000000..c24f9bfcb --- /dev/null +++ b/rtgui/dynamicprofilepanel.h @@ -0,0 +1,149 @@ +/* -*- 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_entry(Gtk::TreeModel::Row row, + const DynamicProfileEntry &entry); + void add_entry(const DynamicProfileEntry &entry); + DynamicProfileEntry to_entry(Gtk::TreeModel::Row row, int serial=0); + + //Signal handlers: + void on_button_quit(); + void on_button_up(); + void on_button_down(); + void on_button_new(); + void on_button_edit(); + void on_button_delete(); + + //Tree model columns: + class DynamicProfileColumns: public Gtk::TreeModel::ColumnRecord { + public: + DynamicProfileColumns() + { + add(iso); + add(fnumber); + add(focallen); + add(shutterspeed); + add(expcomp); + add(make); + add(model); + add(lens); + add(profilepath); + } + + Gtk::TreeModelColumn> iso; + Gtk::TreeModelColumn> fnumber; + Gtk::TreeModelColumn> focallen; + Gtk::TreeModelColumn> shutterspeed; + Gtk::TreeModelColumn> expcomp; + Gtk::TreeModelColumn> make; + Gtk::TreeModelColumn> model; + 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_make(Gtk::CellRenderer* cell, + const Gtk::TreeModel::iterator& iter); + void render_model(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_entry(const DynamicProfileEntry &entry); + DynamicProfileEntry get_entry(); + + 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_make; + Gtk::Entry *make; + + Gtk::CheckButton *has_model; + Gtk::Entry *model; + + 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/preferences.cc b/rtgui/preferences.cc index 0de355bd2..e43138473 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 () { @@ -2020,6 +2029,7 @@ void Preferences::okPressed () options.copyFrom (&moptions); options.filterOutParsedExtensions(); Options::save (); + dynProfilePanel->save(); hide (); } @@ -2181,6 +2191,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 9f32b735f..31b490ce1 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -627,7 +627,6 @@ void ProfileStoreComboBox::updateProfileList () // special case for the Internal default entry addRow(profileStore.getInternalDefaultPSE()); } - addRow(profileStore.getInternalDynamicPSE()); // releasing the profilestore's entry list mutex profileStore.releaseFileList(); From 6ae57cd556accd50c65ba3037eedbf769dcc208e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 09:11:37 +0100 Subject: [PATCH 04/19] dynamic profile: merge "make" and "model" into "camera", and allow to use regexps --- rtdata/languages/default | 10 ++++++ rtgui/dynamicprofile.cc | 30 +++++++++++++----- rtgui/dynamicprofile.h | 16 ++++------ rtgui/dynamicprofilepanel.cc | 59 +++++++++++------------------------- rtgui/dynamicprofilepanel.h | 27 ++++++----------- 5 files changed, 64 insertions(+), 78 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index 5bcfc2892..dbfe533bd 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,11 @@ 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 diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index aa3255467..c15799fc1 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -19,6 +19,7 @@ #include "dynamicprofile.h" #include +#include using namespace rtengine; using namespace rtengine::procparams; @@ -36,6 +37,22 @@ const double EXPCOMP_MAX = 20.0; } // namespace +bool DynamicProfileEntry::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(); + } +} + + DynamicProfileEntry::DynamicProfileEntry(): serial_number(0), iso(0, ISO_MAX), @@ -60,8 +77,7 @@ bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) && focallen(im->getFocalLen()) && shutterspeed(im->getShutterSpeed()) && expcomp(im->getExpComp()) - && make(im->getMake()) - && model(im->getModel()) + && camera(im->getCamera()) && lens(im->getLens())); } @@ -99,7 +115,7 @@ void get_double_range(DynamicProfileEntry::Range &dest, } -void get_optional(DynamicProfileEntry::Optional &dest, +void get_optional(DynamicProfileEntry::Optional &dest, const Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key) { @@ -132,7 +148,7 @@ void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group, void set_optional(Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key, - const DynamicProfileEntry::Optional &val) + const DynamicProfileEntry::Optional &val) { kf.set_boolean(group, key + "_enabled", val.enabled); kf.set_string(group, key + "_value", val.value); @@ -175,8 +191,7 @@ bool loadDynamicProfileEntries(std::vector &out) get_double_range(entry.focallen, kf, group, "focallen"); get_double_range(entry.shutterspeed, kf, group, "shutterspeed"); get_double_range(entry.expcomp, kf, group, "expcomp"); - get_optional(entry.make, kf, group, "make"); - get_optional(entry.model, kf, group, "model"); + get_optional(entry.camera, kf, group, "camera"); get_optional(entry.lens, kf, group, "lens"); try { entry.profilepath = kf.get_string(group, "profilepath"); @@ -202,8 +217,7 @@ bool storeDynamicProfileEntries(const std::vector &entries) set_double_range(kf, group, "focallen", entry.focallen); set_double_range(kf, group, "shutterspeed", entry.shutterspeed); set_double_range(kf, group, "expcomp", entry.expcomp); - set_optional(kf, group, "make", entry.make); - set_optional(kf, group, "model", entry.model); + set_optional(kf, group, "camera", entry.camera); set_optional(kf, group, "lens", entry.lens); kf.set_string(group, "profilepath", entry.profilepath); } diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h index a70a0bff7..0847cd8ef 100644 --- a/rtgui/dynamicprofile.h +++ b/rtgui/dynamicprofile.h @@ -38,16 +38,13 @@ public: } }; - template struct Optional { - T value; + Glib::ustring value; bool enabled; - explicit Optional(T v=T(), bool e=false): value(v), enabled(e) {} + explicit Optional(const Glib::ustring v="", bool e=false): + value(v), enabled(e) {} - bool operator()(const T &val) const - { - return !enabled || value == val; - } + bool operator()(const Glib::ustring &val) const; }; DynamicProfileEntry(); @@ -60,9 +57,8 @@ public: Range focallen; Range shutterspeed; Range expcomp; - Optional make; - Optional model; - Optional lens; + Optional camera; + Optional lens; Glib::ustring profilepath; }; diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index bfe311efb..3307cbcf3 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -68,9 +68,8 @@ DynamicProfilePanel::EditDialog::EditDialog(const Glib::ustring &title, hb->pack_start(*profilepath_, true, true, 2); get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4); - add_optional(M("DYNPROFILEEDITOR_CAMERA_MAKE"), has_make, make); - add_optional(M("DYNPROFILEEDITOR_CAMERA_MODEL"), has_model, model); - add_optional(M("EXIFFILTER_LENS"), has_lens, lens); + 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_); @@ -105,14 +104,11 @@ void DynamicProfilePanel::EditDialog::set_entry( expcomp_min_->set_value(entry.expcomp.min); expcomp_max_->set_value(entry.expcomp.max); - has_make->set_active(entry.make.enabled); - make->set_text(entry.make.value); + has_camera_->set_active(entry.camera.enabled); + camera_->set_text(entry.camera.value); - has_model->set_active(entry.model.enabled); - model->set_text(entry.model.value); - - has_lens->set_active(entry.lens.enabled); - lens->set_text(entry.lens.value); + has_lens_->set_active(entry.lens.enabled); + lens_->set_text(entry.lens.value); profilepath_->updateProfileList(); if (!profilepath_->setActiveRowFromFullPath(entry.profilepath)) { @@ -139,14 +135,11 @@ DynamicProfileEntry DynamicProfilePanel::EditDialog::get_entry() ret.expcomp.min = expcomp_min_->get_value(); ret.expcomp.max = expcomp_max_->get_value(); - ret.make.enabled = has_make->get_active(); - ret.make.value = make->get_text(); + ret.camera.enabled = has_camera_->get_active(); + ret.camera.value = camera_->get_text(); - ret.model.enabled = has_model->get_active(); - ret.model.value = model->get_text(); - - ret.lens.enabled = has_lens->get_active(); - ret.lens.value = lens->get_text(); + ret.lens.enabled = has_lens_->get_active(); + ret.lens.value = lens_->get_text(); ret.profilepath = profilepath_->getFullPathFromActiveRow(); @@ -273,21 +266,12 @@ DynamicProfilePanel::DynamicProfilePanel(): } cell = Gtk::manage(new Gtk::CellRendererText()); cols_count = treeview_.append_column( - M("DYNPROFILEEDITOR_CAMERA_MAKE"), *cell); + 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_make)); - } - cell = Gtk::manage(new Gtk::CellRendererText()); - cols_count = treeview_.append_column( - M("DYNPROFILEEDITOR_CAMERA_MODEL"), *cell); - col = treeview_.get_column(cols_count - 1); - if (col) { - col->set_cell_data_func( - *cell, sigc::mem_fun( - *this, &DynamicProfilePanel::render_model)); + *this, &DynamicProfilePanel::render_camera)); } cell = Gtk::manage(new Gtk::CellRendererText()); cols_count = treeview_.append_column(M("EXIFFILTER_LENS"), *cell); @@ -358,8 +342,7 @@ void DynamicProfilePanel::update_entry(Gtk::TreeModel::Row row, row[columns_.focallen] = entry.focallen; row[columns_.shutterspeed] = entry.shutterspeed; row[columns_.expcomp] = entry.expcomp; - row[columns_.make] = entry.make; - row[columns_.model] = entry.model; + row[columns_.camera] = entry.camera; row[columns_.lens] = entry.lens; row[columns_.profilepath] = entry.profilepath; } @@ -381,8 +364,7 @@ DynamicProfileEntry DynamicProfilePanel::to_entry(Gtk::TreeModel::Row row, ret.focallen = row[columns_.focallen]; ret.shutterspeed = row[columns_.shutterspeed]; ret.expcomp = row[columns_.expcomp]; - ret.make = row[columns_.make]; - ret.model = row[columns_.model]; + ret.camera = row[columns_.camera]; ret.lens = row[columns_.lens]; ret.profilepath = row[columns_.profilepath]; return ret; @@ -445,24 +427,17 @@ void DynamicProfilePanel::render_expcomp( #define RENDER_OPTIONAL_(name) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ - DynamicProfileEntry::Optional o = row[columns_. name]; \ + DynamicProfileEntry::Optional o = row[columns_. name]; \ if (o.enabled) { \ ct->property_text() = o.value; \ } else { \ ct->property_text() = ""; \ } -void DynamicProfilePanel::render_make( +void DynamicProfilePanel::render_camera( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_OPTIONAL_(make); -} - - -void DynamicProfilePanel::render_model( - Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) -{ - RENDER_OPTIONAL_(model); + RENDER_OPTIONAL_(camera); } diff --git a/rtgui/dynamicprofilepanel.h b/rtgui/dynamicprofilepanel.h index c24f9bfcb..12b68fadc 100644 --- a/rtgui/dynamicprofilepanel.h +++ b/rtgui/dynamicprofilepanel.h @@ -34,7 +34,6 @@ private: void add_entry(const DynamicProfileEntry &entry); DynamicProfileEntry to_entry(Gtk::TreeModel::Row row, int serial=0); - //Signal handlers: void on_button_quit(); void on_button_up(); void on_button_down(); @@ -42,7 +41,6 @@ private: void on_button_edit(); void on_button_delete(); - //Tree model columns: class DynamicProfileColumns: public Gtk::TreeModel::ColumnRecord { public: DynamicProfileColumns() @@ -52,8 +50,7 @@ private: add(focallen); add(shutterspeed); add(expcomp); - add(make); - add(model); + add(camera); add(lens); add(profilepath); } @@ -63,9 +60,8 @@ private: Gtk::TreeModelColumn> focallen; Gtk::TreeModelColumn> shutterspeed; Gtk::TreeModelColumn> expcomp; - Gtk::TreeModelColumn> make; - Gtk::TreeModelColumn> model; - Gtk::TreeModelColumn> lens; + Gtk::TreeModelColumn camera; + Gtk::TreeModelColumn lens; Gtk::TreeModelColumn profilepath; }; @@ -80,10 +76,8 @@ private: const Gtk::TreeModel::iterator& iter); void render_expcomp(Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); - void render_make(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_model(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, @@ -117,14 +111,11 @@ private: Gtk::SpinButton *expcomp_min_; Gtk::SpinButton *expcomp_max_; - Gtk::CheckButton *has_make; - Gtk::Entry *make; - - Gtk::CheckButton *has_model; - Gtk::Entry *model; + Gtk::CheckButton *has_camera_; + Gtk::Entry *camera_; - Gtk::CheckButton *has_lens; - Gtk::Entry *lens; + Gtk::CheckButton *has_lens_; + Gtk::Entry *lens_; ProfileStoreComboBox *profilepath_; }; From eeb2da2613fab4798ac8df7762c32726d4545fed Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 10:00:19 +0100 Subject: [PATCH 05/19] fixed bugs in loading dynamic profiles --- rtgui/dynamicprofile.cc | 9 +++++---- rtgui/dynamicprofilepanel.cc | 8 +++++++- rtgui/preferences.cc | 2 ++ rtgui/thumbnail.cc | 8 ++++++-- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index c15799fc1..0f09e8a5f 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -18,6 +18,7 @@ */ #include "dynamicprofile.h" +#include "profilestore.h" #include #include @@ -235,13 +236,13 @@ PartialProfile *loadDynamicProfile(const ImageMetaData *im) if (entry.matches(im)) { printf("found matching profile %s\n", entry.profilepath.c_str()); - PartialProfile p(true, true); - if (!p.load(options.findProfilePath(entry.profilepath))) { - p.applyTo(ret->pparams); + const PartialProfile *p = + profileStore.getProfile(entry.profilepath); + if (p != nullptr) { + p->applyTo(ret->pparams); } else { printf("ERROR loading matching profile\n"); } - p.deleteInstance(); } } } diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 3307cbcf3..61614b90c 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -19,6 +19,7 @@ #include "dynamicprofilepanel.h" #include "multilangmgr.h" +#include "profilestore.h" #include #include @@ -377,7 +378,12 @@ void DynamicProfilePanel::render_profilepath( auto row = *iter; Gtk::CellRendererText *ct = static_cast(cell); auto value = row[columns_.profilepath]; - ct->property_text() = value; + auto pse = profileStore.findEntryFromFullPath(value); + if (pse != nullptr) { + ct->property_text() = pse->label; + } else { + ct->property_text() = value; + } } diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index e43138473..ed613c61f 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -438,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) ); diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index b560c5f7b..60da23d01 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -227,13 +227,17 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu imageMetaData = rtengine::ImageMetaData::fromFile (fname, nullptr); } PartialProfile *pp = loadDynamicProfile(imageMetaData); + int err = 0; if (options.paramsLoadLocation == PLL_Input) { - pp->pparams->save(fname + paramFileExtension); + err = pp->pparams->save(fname + paramFileExtension); } else { - pp->pparams->save(getCacheFileName ("profiles", paramFileExtension)); + err = pp->pparams->save(getCacheFileName ("profiles", paramFileExtension)); } pp->deleteInstance(); delete pp; + if (!err) { + loadProcParams(); + } } else if (defProf != DEFPROFILE_DYNAMIC && !options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { // First generate the communication file, with general values and EXIF metadata rtengine::ImageMetaData* imageMetaData; From 01eb437b9002ea0831eaacd4ae39239ce887fd8f Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 11:00:32 +0100 Subject: [PATCH 06/19] some tweaks to the GUI of the dynamic profile editor --- rtdata/languages/default | 11 ++++++----- rtgui/dynamicprofilepanel.cc | 19 +------------------ 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/rtdata/languages/default b/rtdata/languages/default index dbfe533bd..31b14aa2f 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -2039,11 +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_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_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/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 61614b90c..1340e0b31 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -33,24 +33,6 @@ Glib::ustring to_str(V n) return buf.str(); } - -int to_int(const Glib::ustring &s) -{ - std::istringstream buf(s); - int r = -1; - buf >> r; - return r; -} - - -double to_double(const Glib::ustring &s) -{ - std::istringstream buf(s); - double r = 0.0; - buf >> r; - return r; -} - } // namespace @@ -207,6 +189,7 @@ void DynamicProfilePanel::EditDialog::add_optional(const Glib::ustring &name, 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")); } From f67d903550d4493972a4581bcb40380816821261 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 12:38:34 +0100 Subject: [PATCH 07/19] some tweaks to the UI of the dynamic profile editor --- rtgui/dynamicprofilepanel.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 1340e0b31..843b8fd72 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -207,10 +207,9 @@ DynamicProfilePanel::DynamicProfilePanel(): { add(vbox_); - //Add the TreeView, inside a ScrolledWindow, with the button underneath: + treeview_.set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_VERTICAL); scrolledwindow_.add(treeview_); - //Only show the scrollbars when they are necessary: scrolledwindow_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); vbox_.pack_start(scrolledwindow_); @@ -234,11 +233,9 @@ DynamicProfilePanel::DynamicProfilePanel(): button_delete_.signal_clicked().connect( sigc::mem_fun(*this, &DynamicProfilePanel::on_button_delete)); - //Create the Tree model: treemodel_ = Gtk::ListStore::create(columns_); treeview_.set_model(treemodel_); - //Add the TreeView's view columns: auto cell = Gtk::manage(new Gtk::CellRendererText()); int cols_count = treeview_.append_column( M("DYNPROFILEEDITOR_PROFILE"), *cell); @@ -374,8 +371,13 @@ void DynamicProfilePanel::render_profilepath( auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ DynamicProfileEntry::Range r = row[columns_. name]; \ - auto value = to_str(r.min) + " - " + to_str(r.max); \ - ct->property_text() = value; + DynamicProfileEntry dflt; \ + if (r.min > dflt.name.min || r.max < dflt.name.max) { \ + auto value = to_str(r.min) + " - " + to_str(r.max); \ + ct->property_text() = value; \ + } else { \ + ct->property_text() = ""; \ + } void DynamicProfilePanel::render_iso( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) From b3f75398c5723a86343a9fc26074b3ca50a2c6cf Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 3 Mar 2017 16:27:13 +0100 Subject: [PATCH 08/19] increased the number of decimals for shutterspeed in the dynamic profile editor --- rtgui/dynamicprofile.cc | 3 +-- rtgui/dynamicprofilepanel.cc | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 0f09e8a5f..af829b024 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -30,7 +30,6 @@ namespace { const int ISO_MAX = 512000; const double FNUMBER_MAX = 100.0; const double FOCALLEN_MAX = 10000.0; -const double SHUTTERSPEED_MIN = 1.0/10000.0; const double SHUTTERSPEED_MAX = 1000.0; const double EXPCOMP_MIN = -20.0; const double EXPCOMP_MAX = 20.0; @@ -59,7 +58,7 @@ DynamicProfileEntry::DynamicProfileEntry(): iso(0, ISO_MAX), fnumber(0, FNUMBER_MAX), focallen(0, FOCALLEN_MAX), - shutterspeed(SHUTTERSPEED_MIN, SHUTTERSPEED_MAX), + shutterspeed(0, SHUTTERSPEED_MAX), expcomp(EXPCOMP_MIN, EXPCOMP_MAX) { } diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 843b8fd72..1f3059a2c 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -158,6 +158,8 @@ void DynamicProfilePanel::EditDialog::set_ranges() DOIT_(shutterspeed); DOIT_(expcomp); #undef DOIT_ + shutterspeed_min_->set_digits(4); + shutterspeed_max_->set_digits(4); profilepath_->setInternalEntry(); } From 0720659627bc453a0fb9ad9adf59e872321a490b Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 4 Mar 2017 13:48:19 +0100 Subject: [PATCH 09/19] fixed reading and displaying of floating-point values in dynamic profile rules --- rtgui/dynamicprofile.cc | 4 ++-- rtgui/dynamicprofilepanel.cc | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index af829b024..749c9b02e 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -104,8 +104,8 @@ void get_double_range(DynamicProfileEntry::Range &dest, const Glib::ustring &key) { try { - int min = kf.get_double(group, key + "_min"); - int max = kf.get_double(group, key + "_max"); + 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; diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 1f3059a2c..0ac82f7ae 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -26,10 +26,10 @@ namespace { template -Glib::ustring to_str(V n) +Glib::ustring to_str(V n, int precision=1) { std::ostringstream buf; - buf << std::setprecision(1) << std::fixed << n; + buf << std::setprecision(precision) << std::fixed << n; return buf.str(); } @@ -369,13 +369,13 @@ void DynamicProfilePanel::render_profilepath( } -#define RENDER_RANGE_(tp, name) \ +#define RENDER_RANGE_(tp, name, prec) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ DynamicProfileEntry::Range r = row[columns_. name]; \ DynamicProfileEntry dflt; \ if (r.min > dflt.name.min || r.max < dflt.name.max) { \ - auto value = to_str(r.min) + " - " + to_str(r.max); \ + auto value = to_str(r.min, prec) + " - " + to_str(r.max, prec); \ ct->property_text() = value; \ } else { \ ct->property_text() = ""; \ @@ -384,35 +384,35 @@ void DynamicProfilePanel::render_profilepath( void DynamicProfilePanel::render_iso( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(int, iso); + RENDER_RANGE_(int, iso, 0); } void DynamicProfilePanel::render_fnumber( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, fnumber); + RENDER_RANGE_(double, fnumber, 1); } void DynamicProfilePanel::render_focallen( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, focallen); + RENDER_RANGE_(double, focallen, 1); } void DynamicProfilePanel::render_shutterspeed( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, shutterspeed); + RENDER_RANGE_(double, shutterspeed, 4); } void DynamicProfilePanel::render_expcomp( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, expcomp); + RENDER_RANGE_(double, expcomp, 1); } #undef RENDER_RANGE_ From bc5a6fc7c350c9b0a7c5e4b4b3baf3a8d4c7519d Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 4 Mar 2017 14:22:52 +0100 Subject: [PATCH 10/19] renamed DynamicProfileEntry to DynamicProfileRule --- rtgui/dynamicprofile.cc | 86 ++++++++++++------------- rtgui/dynamicprofile.h | 12 ++-- rtgui/dynamicprofilepanel.cc | 118 +++++++++++++++++------------------ rtgui/dynamicprofilepanel.h | 26 ++++---- 4 files changed, 121 insertions(+), 121 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 749c9b02e..d1a5a0177 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -37,7 +37,7 @@ const double EXPCOMP_MAX = 20.0; } // namespace -bool DynamicProfileEntry::Optional::operator()(const Glib::ustring &val) const +bool DynamicProfileRule::Optional::operator()(const Glib::ustring &val) const { if (!enabled) { return true; @@ -53,7 +53,7 @@ bool DynamicProfileEntry::Optional::operator()(const Glib::ustring &val) const } -DynamicProfileEntry::DynamicProfileEntry(): +DynamicProfileRule::DynamicProfileRule(): serial_number(0), iso(0, ISO_MAX), fnumber(0, FNUMBER_MAX), @@ -64,13 +64,13 @@ DynamicProfileEntry::DynamicProfileEntry(): } -bool DynamicProfileEntry::operator<(const DynamicProfileEntry &other) const +bool DynamicProfileRule::operator<(const DynamicProfileRule &other) const { return serial_number < other.serial_number; } -bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) +bool DynamicProfileRule::matches(const rtengine::ImageMetaData *im) { return (iso(im->getISOSpeed()) && fnumber(im->getFNumber()) @@ -83,7 +83,7 @@ bool DynamicProfileEntry::matches(const rtengine::ImageMetaData *im) namespace { -void get_int_range(DynamicProfileEntry::Range &dest, +void get_int_range(DynamicProfileRule::Range &dest, const Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key) { @@ -99,7 +99,7 @@ void get_int_range(DynamicProfileEntry::Range &dest, } -void get_double_range(DynamicProfileEntry::Range &dest, +void get_double_range(DynamicProfileRule::Range &dest, const Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key) { @@ -115,7 +115,7 @@ void get_double_range(DynamicProfileEntry::Range &dest, } -void get_optional(DynamicProfileEntry::Optional &dest, +void get_optional(DynamicProfileRule::Optional &dest, const Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key) { @@ -132,7 +132,7 @@ void get_optional(DynamicProfileEntry::Optional &dest, void set_int_range(Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key, - const DynamicProfileEntry::Range &val) + const DynamicProfileRule::Range &val) { kf.set_integer(group, key + "_min", val.min); kf.set_integer(group, key + "_max", val.max); @@ -140,7 +140,7 @@ void set_int_range(Glib::KeyFile &kf, const Glib::ustring &group, void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key, - const DynamicProfileEntry::Range &val) + const DynamicProfileRule::Range &val) { kf.set_double(group, key + "_min", val.min); kf.set_double(group, key + "_max", val.max); @@ -148,7 +148,7 @@ void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group, void set_optional(Glib::KeyFile &kf, const Glib::ustring &group, const Glib::ustring &key, - const DynamicProfileEntry::Optional &val) + const DynamicProfileRule::Optional &val) { kf.set_boolean(group, key + "_enabled", val.enabled); kf.set_string(group, key + "_value", val.value); @@ -157,7 +157,7 @@ void set_optional(Glib::KeyFile &kf, const Glib::ustring &group, } // namespace -bool loadDynamicProfileEntries(std::vector &out) +bool loadDynamicProfileRules(std::vector &out) { out.clear(); Glib::KeyFile kf; @@ -172,29 +172,29 @@ bool loadDynamicProfileEntries(std::vector &out) printf("loading dynamic profiles...\n"); auto groups = kf.get_groups(); for (auto group : groups) { - // groups are of the form "entry N", where N is a positive integer - if (group.find("entry ") != 0) { + // 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() + 6); + std::istringstream buf(group.c_str() + 5); int serial = 0; if (!(buf >> serial) || !buf.eof()) { return false; } - printf(" loading entry %d\n", serial); + printf(" loading rule %d\n", serial); - out.emplace_back(DynamicProfileEntry()); - DynamicProfileEntry &entry = out.back(); - entry.serial_number = serial; - get_int_range(entry.iso, kf, group, "iso"); - get_double_range(entry.fnumber, kf, group, "fnumber"); - get_double_range(entry.focallen, kf, group, "focallen"); - get_double_range(entry.shutterspeed, kf, group, "shutterspeed"); - get_double_range(entry.expcomp, kf, group, "expcomp"); - get_optional(entry.camera, kf, group, "camera"); - get_optional(entry.lens, kf, group, "lens"); + 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 { - entry.profilepath = kf.get_string(group, "profilepath"); + rule.profilepath = kf.get_string(group, "profilepath"); } catch (Glib::KeyFileError &) { out.pop_back(); } @@ -204,22 +204,22 @@ bool loadDynamicProfileEntries(std::vector &out) } -bool storeDynamicProfileEntries(const std::vector &entries) +bool storeDynamicProfileRules(const std::vector &rules) { printf("saving dynamic profiles...\n"); Glib::KeyFile kf; - for (auto &entry : entries) { + for (auto &rule : rules) { std::ostringstream buf; - buf << "entry " << entry.serial_number; + buf << "rule " << rule.serial_number; Glib::ustring group = buf.str(); - set_int_range(kf, group, "iso", entry.iso); - set_double_range(kf, group, "fnumber", entry.fnumber); - set_double_range(kf, group, "focallen", entry.focallen); - set_double_range(kf, group, "shutterspeed", entry.shutterspeed); - set_double_range(kf, group, "expcomp", entry.expcomp); - set_optional(kf, group, "camera", entry.camera); - set_optional(kf, group, "lens", entry.lens); - kf.set_string(group, "profilepath", entry.profilepath); + 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")); @@ -229,14 +229,14 @@ bool storeDynamicProfileEntries(const std::vector &entries) PartialProfile *loadDynamicProfile(const ImageMetaData *im) { PartialProfile *ret = new PartialProfile(true, true); - std::vector entries; - if (loadDynamicProfileEntries(entries)) { - for (auto &entry : entries) { - if (entry.matches(im)) { + std::vector rules; + if (loadDynamicProfileRules(rules)) { + for (auto &rule : rules) { + if (rule.matches(im)) { printf("found matching profile %s\n", - entry.profilepath.c_str()); + rule.profilepath.c_str()); const PartialProfile *p = - profileStore.getProfile(entry.profilepath); + profileStore.getProfile(rule.profilepath); if (p != nullptr) { p->applyTo(ret->pparams); } else { diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h index 0847cd8ef..c662a51f7 100644 --- a/rtgui/dynamicprofile.h +++ b/rtgui/dynamicprofile.h @@ -24,7 +24,7 @@ #include "options.h" -class DynamicProfileEntry { +class DynamicProfileRule { public: template struct Range { @@ -47,9 +47,9 @@ public: bool operator()(const Glib::ustring &val) const; }; - DynamicProfileEntry(); + DynamicProfileRule(); bool matches(const rtengine::ImageMetaData *im); - bool operator<(const DynamicProfileEntry &other) const; + bool operator<(const DynamicProfileRule &other) const; int serial_number; Range iso; @@ -63,9 +63,9 @@ public: }; -bool loadDynamicProfileEntries(std::vector &out); -bool storeDynamicProfileEntries( - const std::vector &entries); +bool loadDynamicProfileRules(std::vector &out); +bool storeDynamicProfileRules( + const std::vector &rules); rtengine::procparams::PartialProfile *loadDynamicProfile( const rtengine::ImageMetaData *im); diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 0ac82f7ae..2d8ba638a 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -69,40 +69,40 @@ DynamicProfilePanel::EditDialog::EditDialog(const Glib::ustring &title, } -void DynamicProfilePanel::EditDialog::set_entry( - const DynamicProfileEntry &entry) +void DynamicProfilePanel::EditDialog::set_rule( + const DynamicProfileRule &rule) { - iso_min_->set_value(entry.iso.min); - iso_max_->set_value(entry.iso.max); + iso_min_->set_value(rule.iso.min); + iso_max_->set_value(rule.iso.max); - fnumber_min_->set_value(entry.fnumber.min); - fnumber_max_->set_value(entry.fnumber.max); + fnumber_min_->set_value(rule.fnumber.min); + fnumber_max_->set_value(rule.fnumber.max); - focallen_min_->set_value(entry.focallen.min); - focallen_max_->set_value(entry.focallen.max); + focallen_min_->set_value(rule.focallen.min); + focallen_max_->set_value(rule.focallen.max); - shutterspeed_min_->set_value(entry.shutterspeed.min); - shutterspeed_max_->set_value(entry.shutterspeed.max); + shutterspeed_min_->set_value(rule.shutterspeed.min); + shutterspeed_max_->set_value(rule.shutterspeed.max); - expcomp_min_->set_value(entry.expcomp.min); - expcomp_max_->set_value(entry.expcomp.max); + expcomp_min_->set_value(rule.expcomp.min); + expcomp_max_->set_value(rule.expcomp.max); - has_camera_->set_active(entry.camera.enabled); - camera_->set_text(entry.camera.value); + has_camera_->set_active(rule.camera.enabled); + camera_->set_text(rule.camera.value); - has_lens_->set_active(entry.lens.enabled); - lens_->set_text(entry.lens.value); + has_lens_->set_active(rule.lens.enabled); + lens_->set_text(rule.lens.value); profilepath_->updateProfileList(); - if (!profilepath_->setActiveRowFromFullPath(entry.profilepath)) { + if (!profilepath_->setActiveRowFromFullPath(rule.profilepath)) { profilepath_->setInternalEntry(); } } -DynamicProfileEntry DynamicProfilePanel::EditDialog::get_entry() +DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule() { - DynamicProfileEntry ret; + DynamicProfileRule ret; ret.iso.min = iso_min_->get_value_as_int(); ret.iso.max = iso_max_->get_value_as_int(); @@ -131,27 +131,27 @@ DynamicProfileEntry DynamicProfilePanel::EditDialog::get_entry() void DynamicProfilePanel::EditDialog::set_ranges() { - DynamicProfileEntry default_entry; + 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_entry.iso.min, default_entry.iso.max); - iso_max_->set_range(default_entry.iso.min, default_entry.iso.max); - iso_min_->set_value(default_entry.iso.min); - iso_max_->set_value(default_entry.iso.max); + 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_entry. name .min, \ - default_entry. name .max); \ - name ## _max_->set_range(default_entry. name .min, \ - default_entry. name .max); \ - name ## _min_->set_value(default_entry. name .min); \ - name ## _max_->set_value(default_entry. name .max) + 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); @@ -308,39 +308,39 @@ DynamicProfilePanel::DynamicProfilePanel(): show_all_children(); - std::vector entries; - if (loadDynamicProfileEntries(entries)) { - for (auto &e : entries) { - add_entry(e); + std::vector rules; + if (loadDynamicProfileRules(rules)) { + for (auto &r : rules) { + add_rule(r); } } } -void DynamicProfilePanel::update_entry(Gtk::TreeModel::Row row, - const DynamicProfileEntry &entry) +void DynamicProfilePanel::update_rule(Gtk::TreeModel::Row row, + const DynamicProfileRule &rule) { - row[columns_.iso] = entry.iso; - row[columns_.fnumber] = entry.fnumber; - row[columns_.focallen] = entry.focallen; - row[columns_.shutterspeed] = entry.shutterspeed; - row[columns_.expcomp] = entry.expcomp; - row[columns_.camera] = entry.camera; - row[columns_.lens] = entry.lens; - row[columns_.profilepath] = entry.profilepath; + 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_entry(const DynamicProfileEntry &entry) +void DynamicProfilePanel::add_rule(const DynamicProfileRule &rule) { auto row = *(treemodel_->append()); - update_entry(row, entry); + update_rule(row, rule); } -DynamicProfileEntry DynamicProfilePanel::to_entry(Gtk::TreeModel::Row row, +DynamicProfileRule DynamicProfilePanel::to_rule(Gtk::TreeModel::Row row, int serial) { - DynamicProfileEntry ret; + DynamicProfileRule ret; ret.serial_number = serial; ret.iso = row[columns_.iso]; ret.fnumber = row[columns_.fnumber]; @@ -372,8 +372,8 @@ void DynamicProfilePanel::render_profilepath( #define RENDER_RANGE_(tp, name, prec) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ - DynamicProfileEntry::Range r = row[columns_. name]; \ - DynamicProfileEntry dflt; \ + DynamicProfileRule::Range r = row[columns_. name]; \ + DynamicProfileRule dflt; \ if (r.min > dflt.name.min || r.max < dflt.name.max) { \ auto value = to_str(r.min, prec) + " - " + to_str(r.max, prec); \ ct->property_text() = value; \ @@ -420,7 +420,7 @@ void DynamicProfilePanel::render_expcomp( #define RENDER_OPTIONAL_(name) \ auto row = *iter; \ Gtk::CellRendererText *ct = static_cast(cell); \ - DynamicProfileEntry::Optional o = row[columns_. name]; \ + DynamicProfileRule::Optional o = row[columns_. name]; \ if (o.enabled) { \ ct->property_text() = o.value; \ } else { \ @@ -488,8 +488,8 @@ void DynamicProfilePanel::on_button_new() static_cast(*get_toplevel())); int status = d.run(); if (status == 1) { - DynamicProfileEntry entry = d.get_entry(); - add_entry(entry); + DynamicProfileRule rule = d.get_rule(); + add_rule(rule); } } @@ -504,24 +504,24 @@ void DynamicProfilePanel::on_button_edit() static_cast(*get_toplevel())); auto it = s->get_selected(); Gtk::TreeModel::Row row = *(s->get_selected()); - d.set_entry(to_entry(row)); + d.set_rule(to_rule(row)); int status = d.run(); if (status == 1) { - update_entry(row, d.get_entry()); + update_rule(row, d.get_rule()); } } void DynamicProfilePanel::save() { - std::vector entries; + std::vector rules; int serial = 1; for (auto row : treemodel_->children()) { - entries.emplace_back(to_entry(row, serial++)); + rules.emplace_back(to_rule(row, serial++)); } - if (!storeDynamicProfileEntries(entries)) { + if (!storeDynamicProfileRules(rules)) { printf("Error in saving dynamic profile rules\n"); } else { - printf("Saved %d dynamic profile rules\n", int(entries.size())); + printf("Saved %d dynamic profile rules\n", int(rules.size())); } } diff --git a/rtgui/dynamicprofilepanel.h b/rtgui/dynamicprofilepanel.h index 12b68fadc..72ff95b9a 100644 --- a/rtgui/dynamicprofilepanel.h +++ b/rtgui/dynamicprofilepanel.h @@ -29,10 +29,10 @@ public: void save(); private: - void update_entry(Gtk::TreeModel::Row row, - const DynamicProfileEntry &entry); - void add_entry(const DynamicProfileEntry &entry); - DynamicProfileEntry to_entry(Gtk::TreeModel::Row row, int serial=0); + 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(); @@ -55,13 +55,13 @@ private: 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> iso; + Gtk::TreeModelColumn> fnumber; + Gtk::TreeModelColumn> focallen; + Gtk::TreeModelColumn> shutterspeed; + Gtk::TreeModelColumn> expcomp; + Gtk::TreeModelColumn camera; + Gtk::TreeModelColumn lens; Gtk::TreeModelColumn profilepath; }; @@ -86,8 +86,8 @@ private: class EditDialog: public Gtk::Dialog { public: EditDialog(const Glib::ustring &title, Gtk::Window &parent); - void set_entry(const DynamicProfileEntry &entry); - DynamicProfileEntry get_entry(); + void set_rule(const DynamicProfileRule &rule); + DynamicProfileRule get_rule(); private: void set_ranges(); From dd4fd82582d9688488d4038f7bcce16b53b1fa52 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sat, 4 Mar 2017 14:16:26 +0100 Subject: [PATCH 11/19] Cache DynamicProfileRuleS in the profileStore for reduced I/O --- rtgui/dynamicprofile.cc | 23 ++++++++++------------- rtgui/dynamicprofilepanel.cc | 11 +++++------ rtgui/profilestore.cc | 1 + rtgui/profilestore.h | 11 ++++++++++- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index d1a5a0177..27d4f0178 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -229,19 +229,16 @@ bool storeDynamicProfileRules(const std::vector &rules) PartialProfile *loadDynamicProfile(const ImageMetaData *im) { PartialProfile *ret = new PartialProfile(true, true); - std::vector rules; - if (loadDynamicProfileRules(rules)) { - for (auto &rule : rules) { - if (rule.matches(im)) { - 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\n"); - } + for (auto &rule : profileStore.getDynamicProfileRules()) { + if (rule.matches(im)) { + 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\n"); } } } diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 2d8ba638a..bd7665c36 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -308,11 +308,8 @@ DynamicProfilePanel::DynamicProfilePanel(): show_all_children(); - std::vector rules; - if (loadDynamicProfileRules(rules)) { - for (auto &r : rules) { - add_rule(r); - } + for (auto &r : profileStore.getDynamicProfileRules()) { + add_rule(r); } } @@ -514,13 +511,15 @@ void DynamicProfilePanel::on_button_edit() void DynamicProfilePanel::save() { - std::vector rules; + auto &rules = profileStore.getDynamicProfileRules(); + rules.clear(); 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"); + rules.clear(); } else { printf("Saved %d dynamic profile rules\n", int(rules.size())); } diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 31b490ce1..7eaf50b77 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -42,6 +42,7 @@ bool ProfileStore::init () storeState = STORESTATE_BEINGINITIALIZED; parseMutex = new MyMutex(); _parseProfiles (); + loadDynamicProfileRules(dynamicRules); storeState = STORESTATE_INITIALIZED; } diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h index 5e5591e15..022652287 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -29,6 +29,7 @@ #include "threadutils.h" #include "paramsedited.h" #include "guiutils.h" +#include "dynamicprofile.h" /** @brief This will implement callback functions for the ProfileStore @@ -161,6 +162,9 @@ private: /** List of the client of this store */ std::list listeners; + /** cache for dynamic profile rules */ + std::vector 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/ @@ -203,7 +207,12 @@ public: { return internalDynamicEntry; } - + + std::vector &getDynamicProfileRules() + { + return dynamicRules; + } + void addListener(ProfileStoreListener *listener); void removeListener(ProfileStoreListener *listener); From d40d806224e33e206dc9b97e4e8c3e1dcd593119 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 09:52:32 +0100 Subject: [PATCH 12/19] slightly more informative error log message --- rtgui/dynamicprofile.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 27d4f0178..86b8126e8 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -238,7 +238,8 @@ PartialProfile *loadDynamicProfile(const ImageMetaData *im) if (p != nullptr) { p->applyTo(ret->pparams); } else { - printf("ERROR loading matching profile\n"); + printf("ERROR loading matching profile from: %s\n", + rule.profilepath.c_str()); } } } From f8bf8d18dc128c06449cabd1d3f60a880a11f985 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 11:07:07 +0100 Subject: [PATCH 13/19] minor cosmetic tweak to the way dynamic profile rules are shown --- rtgui/dynamicprofilepanel.cc | 44 ++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index bd7665c36..f148dab2e 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -20,21 +20,10 @@ #include "dynamicprofilepanel.h" #include "multilangmgr.h" #include "profilestore.h" +#include "../rtengine/rtengine.h" #include #include -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 - //----------------------------------------------------------------------------- // DynamicProfilePanel::EditDialog @@ -366,50 +355,67 @@ void DynamicProfilePanel::render_profilepath( } -#define RENDER_RANGE_(tp, name, prec) \ +#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 = to_str(r.min, prec) + " - " + to_str(r.max, prec); \ + 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, 0); + RENDER_RANGE_(int, iso, to_str); } void DynamicProfilePanel::render_fnumber( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, fnumber, 1); + 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, 1); + RENDER_RANGE_(double, focallen, to_str); } void DynamicProfilePanel::render_shutterspeed( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, shutterspeed, 4); + RENDER_RANGE_(double, shutterspeed, + rtengine::ImageMetaData::shutterToString); } void DynamicProfilePanel::render_expcomp( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, expcomp, 1); + RENDER_RANGE_(double, expcomp, to_str); } #undef RENDER_RANGE_ From 43e32e81ef596560842d673a84cbcd0538ae190b Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 18:10:42 +0100 Subject: [PATCH 14/19] some code style improvements --- rtgui/dynamicprofile.cc | 2 +- rtgui/dynamicprofile.h | 2 +- rtgui/dynamicprofilepanel.cc | 5 ++--- rtgui/profilestore.h | 7 ++++++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 86b8126e8..69bda1d89 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -70,7 +70,7 @@ bool DynamicProfileRule::operator<(const DynamicProfileRule &other) const } -bool DynamicProfileRule::matches(const rtengine::ImageMetaData *im) +bool DynamicProfileRule::matches(const rtengine::ImageMetaData *im) const { return (iso(im->getISOSpeed()) && fnumber(im->getFNumber()) diff --git a/rtgui/dynamicprofile.h b/rtgui/dynamicprofile.h index c662a51f7..4c5e552e4 100644 --- a/rtgui/dynamicprofile.h +++ b/rtgui/dynamicprofile.h @@ -48,7 +48,7 @@ public: }; DynamicProfileRule(); - bool matches(const rtengine::ImageMetaData *im); + bool matches(const rtengine::ImageMetaData *im) const; bool operator<(const DynamicProfileRule &other) const; int serial_number; diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index f148dab2e..d06f9be4f 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -517,16 +517,15 @@ void DynamicProfilePanel::on_button_edit() void DynamicProfilePanel::save() { - auto &rules = profileStore.getDynamicProfileRules(); - rules.clear(); + 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"); - rules.clear(); } else { + profileStore.setDynamicProfileRules(rules); printf("Saved %d dynamic profile rules\n", int(rules.size())); } } diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h index 022652287..f97cd02e1 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -208,11 +208,16 @@ public: return internalDynamicEntry; } - std::vector &getDynamicProfileRules() + const std::vector &getDynamicProfileRules() const { return dynamicRules; } + void setDynamicProfileRules(const std::vector &r) + { + dynamicRules = r; + } + void addListener(ProfileStoreListener *listener); void removeListener(ProfileStoreListener *listener); From 48ac4e3112c40e00ee09be4f117f564b7b2df51e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 22:45:13 +0100 Subject: [PATCH 15/19] added workaround for #3727 (by heckflosse) --- rtgui/profilestore.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 7eaf50b77..364ae5c6c 100644 --- a/rtgui/profilestore.cc +++ b/rtgui/profilestore.cc @@ -577,12 +577,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); From f7f4575c54a97034186cc7380d6e7dbd5284972e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 5 Mar 2017 22:59:44 +0100 Subject: [PATCH 16/19] allow to run the custom profile builder (if set) after the creation of the dynamic profile --- rtgui/thumbnail.cc | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index 60da23d01..b9fb5ed2e 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -217,8 +217,16 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu const CacheImageData* cfs = getCacheImageData(); Glib::ustring defaultPparamsPath = options.findProfilePath(defProf); + bool create = (!hasProcParams() || forceCPB); - if (defProf == DEFPROFILE_DYNAMIC && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { + Glib::ustring outFName; + if (options.paramsLoadLocation == PLL_Input) { + outFName = fname + paramFileExtension; + } else { + outFName = 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); @@ -227,18 +235,15 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu imageMetaData = rtengine::ImageMetaData::fromFile (fname, nullptr); } PartialProfile *pp = loadDynamicProfile(imageMetaData); - int err = 0; - if (options.paramsLoadLocation == PLL_Input) { - err = pp->pparams->save(fname + paramFileExtension); - } else { - err = pp->pparams->save(getCacheFileName ("profiles", paramFileExtension)); - } + int err = pp->pparams->save(outFName); pp->deleteInstance(); delete pp; if (!err) { loadProcParams(); } - } else if (defProf != DEFPROFILE_DYNAMIC && !options.CPBPath.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) { + } + + if (!options.CPBPath.empty() && !defaultPparamsPath.empty() && create && cfs && cfs->exifValid) { // First generate the communication file, with general values and EXIF metadata rtengine::ImageMetaData* imageMetaData; @@ -254,14 +259,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, From a1a042e9eaae14f5450beea4b079e2c340831996 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 6 Mar 2017 09:00:13 +0100 Subject: [PATCH 17/19] code style cleanups (after floessie's feedback) --- rtgui/profilestore.cc | 19 +++++++++++++++++-- rtgui/profilestore.h | 17 ++++++----------- rtgui/thumbnail.cc | 12 +++++------- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc index 364ae5c6c..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), internalDynamicEntry(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,7 +43,7 @@ bool ProfileStore::init () storeState = STORESTATE_BEINGINITIALIZED; parseMutex = new MyMutex(); _parseProfiles (); - loadDynamicProfileRules(dynamicRules); + loadDynamicProfileRules(*dynamicRules); storeState = STORESTATE_INITIALIZED; } @@ -506,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) {} diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h index f97cd02e1..ad569c180 100644 --- a/rtgui/profilestore.h +++ b/rtgui/profilestore.h @@ -29,9 +29,11 @@ #include "threadutils.h" #include "paramsedited.h" #include "guiutils.h" -#include "dynamicprofile.h" +// forward decl +class DynamicProfileRule; + /** @brief This will implement callback functions for the ProfileStore * */ @@ -163,7 +165,7 @@ private: std::list listeners; /** cache for dynamic profile rules */ - std::vector dynamicRules; + std::unique_ptr> dynamicRules; /** @brief Method to recursively parse a profile folder with a level depth arbitrarily limited to 3 * @@ -208,15 +210,8 @@ public: return internalDynamicEntry; } - const std::vector &getDynamicProfileRules() const - { - return dynamicRules; - } - - void setDynamicProfileRules(const std::vector &r) - { - dynamicRules = r; - } + 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 b9fb5ed2e..62f543d4a 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -217,14 +217,12 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu const CacheImageData* cfs = getCacheImageData(); Glib::ustring defaultPparamsPath = options.findProfilePath(defProf); - bool create = (!hasProcParams() || forceCPB); + const bool create = (!hasProcParams() || forceCPB); - Glib::ustring outFName; - if (options.paramsLoadLocation == PLL_Input) { - outFName = fname + paramFileExtension; - } else { - outFName = getCacheFileName("profiles", paramFileExtension); - } + const Glib::ustring outFName = + (options.paramsLoadLocation == PLL_Input) ? + fname + paramFileExtension : + getCacheFileName("profiles", paramFileExtension); if (defProf == DEFPROFILE_DYNAMIC && create && cfs && cfs->exifValid) { rtengine::ImageMetaData* imageMetaData; From e8d4378659b21e4165b255e403b9bca1a65701bb Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 6 Mar 2017 15:02:39 +0100 Subject: [PATCH 18/19] print loading/saving dynamic profile messages only in verbose mode --- rtgui/dynamicprofile.cc | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc index 69bda1d89..4439a1562 100644 --- a/rtgui/dynamicprofile.cc +++ b/rtgui/dynamicprofile.cc @@ -169,7 +169,9 @@ bool loadDynamicProfileRules(std::vector &out) } catch (Glib::Error &e) { return false; } - printf("loading dynamic profiles...\n"); + 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 @@ -181,7 +183,9 @@ bool loadDynamicProfileRules(std::vector &out) if (!(buf >> serial) || !buf.eof()) { return false; } - printf(" loading rule %d\n", serial); + if (options.rtSettings.verbose) { + printf(" loading rule %d\n", serial); + } out.emplace_back(DynamicProfileRule()); DynamicProfileRule &rule = out.back(); @@ -206,7 +210,9 @@ bool loadDynamicProfileRules(std::vector &out) bool storeDynamicProfileRules(const std::vector &rules) { - printf("saving dynamic profiles...\n"); + if (options.rtSettings.verbose) { + printf("saving dynamic profiles...\n"); + } Glib::KeyFile kf; for (auto &rule : rules) { std::ostringstream buf; @@ -231,8 +237,10 @@ PartialProfile *loadDynamicProfile(const ImageMetaData *im) PartialProfile *ret = new PartialProfile(true, true); for (auto &rule : profileStore.getDynamicProfileRules()) { if (rule.matches(im)) { - printf("found matching profile %s\n", - rule.profilepath.c_str()); + if (options.rtSettings.verbose) { + printf("found matching profile %s\n", + rule.profilepath.c_str()); + } const PartialProfile *p = profileStore.getProfile(rule.profilepath); if (p != nullptr) { From d40a0c750d184a71f269dc18a4c354e654de5693 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 6 Mar 2017 17:56:10 +0100 Subject: [PATCH 19/19] print loading/saving dynamic profile messages only in verbose mode (forgot one from previous commit) --- rtgui/dynamicprofilepanel.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index d06f9be4f..e5f9ae1cc 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -526,6 +526,8 @@ void DynamicProfilePanel::save() printf("Error in saving dynamic profile rules\n"); } else { profileStore.setDynamicProfileRules(rules); - printf("Saved %d dynamic profile rules\n", int(rules.size())); + if (options.rtSettings.verbose) { + printf("Saved %d dynamic profile rules\n", int(rules.size())); + } } }