diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt index 74abb1d59..e50c7b3e5 100644 --- a/rtengine/CMakeLists.txt +++ b/rtengine/CMakeLists.txt @@ -50,6 +50,7 @@ set(RTENGINESOURCEFILES dfmanager.cc diagonalcurves.cc dirpyr_equalizer.cc + dynamicprofile.cc expo_before_b.cc fast_demo.cc ffmanager.cc @@ -99,6 +100,7 @@ set(RTENGINESOURCEFILES previewimage.cc processingjob.cc procparams.cc + profilestore.cc rawimage.cc rawimagesource.cc refreshmap.cc diff --git a/rtengine/dcrop.cc b/rtengine/dcrop.cc index 84629e771..cc2f402c9 100644 --- a/rtengine/dcrop.cc +++ b/rtengine/dcrop.cc @@ -26,8 +26,7 @@ #include #include #include -#include "../rtgui/cachemanager.h" -#include "../rtgui/cacheimagedata.h" +#include "../rtgui/md5helper.h" //#include // "ceil" rounding @@ -838,11 +837,7 @@ void Crop::update (int todo) if (needslocal ) { // if (tyty ) { - CacheManager* cachemgr; - - CacheImageData cfs; - cfs.md5 = cachemgr->getMD5 (parent->imgsrc->getFileName()); - std::string mdfive = cfs.md5; + std::string mdfive = getMD5 (parent->imgsrc->getFileName()); Glib::ustring pop = options.cacheBaseDir + "/mip/"; diff --git a/rtengine/dynamicprofile.cc b/rtengine/dynamicprofile.cc new file mode 100644 index 000000000..5ac408da5 --- /dev/null +++ b/rtengine/dynamicprofile.cc @@ -0,0 +1,261 @@ +/* -*- 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 "../rtengine/dynamicprofile.h" + +#include +#include + +using namespace rtengine; +using namespace rtengine::procparams; + +namespace +{ + +const int ISO_MAX = 512000; +const double FNUMBER_MAX = 100.0; +const double FOCALLEN_MAX = 10000.0; +const double SHUTTERSPEED_MAX = 1000.0; +const double EXPCOMP_MIN = -20.0; +const double EXPCOMP_MAX = 20.0; + +} // namespace + +DynamicProfileRules dynamicProfileRules; + +bool DynamicProfileRule::Optional::operator() (const Glib::ustring &val) const +{ + if (!enabled) { + return true; + } + + if (value.find ("re:") == 0) { + // this is a regexp + return Glib::Regex::match_simple (value.substr (3), val, Glib::REGEX_CASELESS); + } else { + // normal string comparison + return value.casefold() == val.casefold(); + } +} + + +DynamicProfileRule::DynamicProfileRule(): + serial_number (0), + iso (0, ISO_MAX), + fnumber (0, FNUMBER_MAX), + focallen (0, FOCALLEN_MAX), + shutterspeed (0, SHUTTERSPEED_MAX), + expcomp (EXPCOMP_MIN, EXPCOMP_MAX) +{ +} + + +bool DynamicProfileRule::operator< (const DynamicProfileRule &other) const +{ + return serial_number < other.serial_number; +} + + +bool DynamicProfileRule::matches (const rtengine::ImageMetaData *im) const +{ + return (iso (im->getISOSpeed()) + && fnumber (im->getFNumber()) + && focallen (im->getFocalLen()) + && shutterspeed (im->getShutterSpeed()) + && expcomp (im->getExpComp()) + && camera (im->getCamera()) + && lens (im->getLens())); +} + +namespace +{ + +void get_int_range (DynamicProfileRule::Range &dest, + const Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key) +{ + try { + int min = kf.get_integer (group, key + "_min"); + int max = kf.get_integer (group, key + "_max"); + + if (min <= max) { + dest.min = min; + dest.max = max; + } + } catch (Glib::KeyFileError &e) { + } +} + + +void get_double_range (DynamicProfileRule::Range &dest, + const Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key) +{ + try { + double min = kf.get_double (group, key + "_min"); + double max = kf.get_double (group, key + "_max"); + + if (min <= max) { + dest.min = min; + dest.max = max; + } + } catch (Glib::KeyFileError &e) { + } +} + + +void get_optional (DynamicProfileRule::Optional &dest, + const Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key) +{ + try { + bool e = kf.get_boolean (group, key + "_enabled"); + + if (e) { + Glib::ustring s = kf.get_string (group, key + "_value"); + dest.enabled = e; + dest.value = s; + } + } catch (Glib::KeyFileError &) { + } +} + +void set_int_range (Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key, + const DynamicProfileRule::Range &val) +{ + kf.set_integer (group, key + "_min", val.min); + kf.set_integer (group, key + "_max", val.max); +} + +void set_double_range (Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key, + const DynamicProfileRule::Range &val) +{ + kf.set_double (group, key + "_min", val.min); + kf.set_double (group, key + "_max", val.max); +} + +void set_optional (Glib::KeyFile &kf, const Glib::ustring &group, + const Glib::ustring &key, + const DynamicProfileRule::Optional &val) +{ + kf.set_boolean (group, key + "_enabled", val.enabled); + kf.set_string (group, key + "_value", val.value); +} + +} // namespace + +bool DynamicProfileRules::loadRules() +{ + dynamicRules.clear(); + Glib::KeyFile kf; + + try { + if (!kf.load_from_file (Glib::build_filename (Options::rtdir, "dynamicprofile.cfg"))) { + return false; + } + } catch (Glib::Error &e) { + return false; + } + + if (options.rtSettings.verbose) { + printf ("loading dynamic profiles...\n"); + } + + auto groups = kf.get_groups(); + + for (auto group : groups) { + // groups are of the form "rule N", where N is a positive integer + if (group.find ("rule ") != 0) { + return false; + } + + std::istringstream buf (group.c_str() + 5); + int serial = 0; + + if (! (buf >> serial) || !buf.eof()) { + return false; + } + + if (options.rtSettings.verbose) { + printf (" loading rule %d\n", serial); + } + + dynamicRules.emplace_back (DynamicProfileRule()); + DynamicProfileRule &rule = dynamicRules.back(); + rule.serial_number = serial; + get_int_range (rule.iso, kf, group, "iso"); + get_double_range (rule.fnumber, kf, group, "fnumber"); + get_double_range (rule.focallen, kf, group, "focallen"); + get_double_range (rule.shutterspeed, kf, group, "shutterspeed"); + get_double_range (rule.expcomp, kf, group, "expcomp"); + get_optional (rule.camera, kf, group, "camera"); + get_optional (rule.lens, kf, group, "lens"); + + try { + rule.profilepath = kf.get_string (group, "profilepath"); + } catch (Glib::KeyFileError &) { + dynamicRules.pop_back(); + } + } + + std::sort (dynamicRules.begin(), dynamicRules.end()); + rulesLoaded = true; + return true; +} + +bool DynamicProfileRules::storeRules() +{ + if (options.rtSettings.verbose) { + printf ("saving dynamic profiles...\n"); + } + + Glib::KeyFile kf; + + for (auto &rule : dynamicRules) { + std::ostringstream buf; + buf << "rule " << rule.serial_number; + Glib::ustring group = buf.str(); + set_int_range (kf, group, "iso", rule.iso); + set_double_range (kf, group, "fnumber", rule.fnumber); + set_double_range (kf, group, "focallen", rule.focallen); + set_double_range (kf, group, "shutterspeed", rule.shutterspeed); + set_double_range (kf, group, "expcomp", rule.expcomp); + set_optional (kf, group, "camera", rule.camera); + set_optional (kf, group, "lens", rule.lens); + kf.set_string (group, "profilepath", rule.profilepath); + } + + return kf.save_to_file (Glib::build_filename (Options::rtdir, "dynamicprofile.cfg")); +} + +const std::vector &DynamicProfileRules::getRules() +{ + if (!rulesLoaded) { + loadRules(); + } + + return dynamicRules; +} + +void DynamicProfileRules::setRules (const std::vector &r) +{ + dynamicRules = r; +} diff --git a/rtgui/dynamicprofile.h b/rtengine/dynamicprofile.h similarity index 63% rename from rtgui/dynamicprofile.h rename to rtengine/dynamicprofile.h index 4c5e552e4..cfe46d9ba 100644 --- a/rtgui/dynamicprofile.h +++ b/rtengine/dynamicprofile.h @@ -21,18 +21,18 @@ #include #include -#include "options.h" +#include "../rtgui/options.h" - -class DynamicProfileRule { +class DynamicProfileRule +{ public: template struct Range { T min; T max; - explicit Range(T l=T(), T u=T()): min(l), max(u) {} + explicit Range (T l = T(), T u = T()): min (l), max (u) {} - bool operator()(T val) const + bool operator() (T val) const { return val >= min && val <= max; } @@ -41,15 +41,15 @@ public: struct Optional { Glib::ustring value; bool enabled; - explicit Optional(const Glib::ustring v="", bool e=false): - value(v), enabled(e) {} + explicit Optional (const Glib::ustring v = "", bool e = false): + value (v), enabled (e) {} - bool operator()(const Glib::ustring &val) const; + bool operator() (const Glib::ustring &val) const; }; - + DynamicProfileRule(); - bool matches(const rtengine::ImageMetaData *im) const; - bool operator<(const DynamicProfileRule &other) const; + bool matches (const rtengine::ImageMetaData *im) const; + bool operator< (const DynamicProfileRule &other) const; int serial_number; Range iso; @@ -62,13 +62,18 @@ public: Glib::ustring profilepath; }; +class DynamicProfileRules +{ +protected: + /** cache for dynamic profile rules */ + std::vector dynamicRules; + bool rulesLoaded; -bool loadDynamicProfileRules(std::vector &out); -bool storeDynamicProfileRules( - const std::vector &rules); - -rtengine::procparams::PartialProfile *loadDynamicProfile( - const rtengine::ImageMetaData *im); - +public: + bool loadRules(); + bool storeRules(); + const std::vector &getRules(); + void setRules (const std::vector &r); +}; #endif // _DYNAMICPROFILE_H_ diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index e1f49b6cd..66d10ea5d 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -26,8 +26,7 @@ #include #include #include -#include "../rtgui/cachemanager.h" -#include "../rtgui/cacheimagedata.h" +#include "../rtgui/md5helper.h" #include "iccstore.h" #ifdef _OPENMP @@ -736,11 +735,7 @@ void ImProcCoordinator::updatePreviewImage (int todo, Crop* cropCall) */ bool isascii = true; - CacheManager* cachemgr; - - CacheImageData cfs; - cfs.md5 = cachemgr->getMD5 (imgsrc->getFileName()); - std::string mdfive = cfs.md5; + std::string mdfive = getMD5 (imgsrc->getFileName()); Glib::ustring datainterm = imgsrc->getFileName() + ".ii";//extansion ii arbitrary to test if mip file is possible diff --git a/rtengine/init.cc b/rtengine/init.cc index 4c66cea68..2d157c762 100644 --- a/rtengine/init.cc +++ b/rtengine/init.cc @@ -16,6 +16,7 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ +#include "../rtgui/profilestorecombobox.h" #include "rtengine.h" #include "iccstore.h" #include "dcp.h" @@ -27,7 +28,7 @@ #include "dfmanager.h" #include "ffmanager.h" #include "rtthumbnail.h" -#include "../rtgui/profilestore.h" +#include "profilestore.h" #include "../rtgui/threadutils.h" namespace rtengine @@ -40,6 +41,7 @@ MyMutex* lcmsMutex = nullptr; int init (const Settings* s, Glib::ustring baseDir, Glib::ustring userSettingsDir, bool loadAll) { settings = s; + ProfileStore::getInstance()->init (loadAll); ICCStore::getInstance()->init (s->iccDirectory, Glib::build_filename (baseDir, "iccprofiles"), loadAll); DCPStore::getInstance()->init (Glib::build_filename (baseDir, "dcpprofiles"), loadAll); diff --git a/rtengine/profilestore.cc b/rtengine/profilestore.cc new file mode 100644 index 000000000..984ab9423 --- /dev/null +++ b/rtengine/profilestore.cc @@ -0,0 +1,535 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * 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 "profilestore.h" + +#include "dynamicprofile.h" +#include "../rtgui/options.h" +#include "../rtgui/multilangmgr.h" + +using namespace rtengine; +using namespace rtengine::procparams; + +ProfileStore::ProfileStore () : storeState (STORESTATE_NOTINITIALIZED), internalDefaultProfile (nullptr), internalDefaultEntry (nullptr), internalDynamicEntry (nullptr), loadAll (true) +{ + internalDefaultProfile = new AutoPartialProfile(); + internalDefaultProfile->set (true); +} + +ProfileStore* ProfileStore::getInstance() +{ + static ProfileStore instance; + return &instance; +} + + +bool ProfileStore::init (bool loadAll) +{ + if (storeState == STORESTATE_DELETED) { + return false; + } + + this->loadAll = loadAll; + + if (storeState == STORESTATE_NOTINITIALIZED && loadAll) { + storeState = STORESTATE_BEINGINITIALIZED; + _parseProfiles (); + storeState = STORESTATE_INITIALIZED; + } + + return storeState == STORESTATE_INITIALIZED; +} + +ProfileStore::~ProfileStore () +{ + if (storeState == STORESTATE_NOTINITIALIZED) { + return; + } + + // This lock prevent object's suppression while scanning the directories + storeState = STORESTATE_DELETED; + + { + MyMutex::MyLock lock (parseMutex); + + clearProfileList (); + partProfiles.clear (); + clearFileList(); + delete internalDefaultProfile; + delete internalDefaultEntry; + delete internalDynamicEntry; + } +} + +/* + * Public method to parse the profiles' directories + * Since there's a race condition in the multithreaded environment on this object, + * parseProfiles may need to ask for initialization of this object, and then will + * ask a mutex lock on it, has it been initialized by this call or not + * + * This method will scan the directory tree again and update the profile list. When finished, + * the listeners will be called in order to update with the new list + */ +void ProfileStore::parseProfiles () +{ + + for (auto listener : listeners) { + listener->storeCurrentValue(); + } + + init (true); // safe even if already initialized + + for (auto listener : listeners) { + listener->updateProfileList(); + listener->restoreValue(); + } +} + +void ProfileStore::_parseProfiles () +{ + // clear loaded profiles + folders.clear(); + clearFileList(); + clearProfileList (); + + folders.push_back ("<<< ROOT >>>"); // Fake path, so parentFolderId == 0 will be used to attach a ProfileStoreEntry to the root container, not sub-menu + + Glib::ustring p1 = options.getUserProfilePath(); + Glib::ustring p2 = options.getGlobalProfilePath(); + bool displayLevel0 = options.useBundledProfiles && !p1.empty() && !p2.empty() && p1 != p2; + + Glib::ustring virtualPath ("${U}"); + Glib::ustring currDir ("${U}"); + parseDir (p1, virtualPath, currDir, 0, 0, displayLevel0); + + if (displayLevel0) { + virtualPath = "${G}"; + currDir = "${G}"; + parseDir (p2, virtualPath, currDir, 0, 0, displayLevel0); + } + + // sort profiles + std::sort (entries.begin(), entries.end(), SortProfiles() ); + + // entries and partProfiles are empty, but the entry and profiles already exist (they have survived to clearFileList and clearProfileList) + if (!internalDefaultEntry) { + internalDefaultEntry = new ProfileStoreEntry (Glib::ustring ("(") + M ("PROFILEPANEL_PINTERNAL") + Glib::ustring (")"), PSET_FILE, 0, 0); + } + + 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) { + options.setDefProfRawMissing (true); + + if (options.rtSettings.verbose) { + printf ("WARNING: Default profile \"%s\" for raw images not found!\n", options.defProfRaw.c_str()); + } + } + + if (findEntryFromFullPathU (options.defProfImg) == nullptr) { + options.setDefProfImgMissing (true); + + if (options.rtSettings.verbose) { + printf ("WARNING: Default profile \"%s\" for standard images not found!\n", options.defProfImg.c_str()); + } + } +} + +/// @return Returns true if some files has been found (directories are ignored) +bool ProfileStore::parseDir (Glib::ustring& realPath, Glib::ustring& virtualPath, Glib::ustring& currDir, unsigned int parentId, unsigned char level, bool displayLevel0) +{ + bool fileFound = false; + + // reload the available profiles from the profile dir + if (!realPath.empty() && Glib::file_test (realPath, Glib::FILE_TEST_EXISTS) && Glib::file_test (realPath, Glib::FILE_TEST_IS_DIR)) { + unsigned int folder = 0; // folder's own Id + + // add this entry to the folder list + folders.push_back (virtualPath); + folder = (unsigned int) (folders.size()) - 1; + + if (level > 0 || displayLevel0) { + // replace the virtual folder name by a localized text + if (currDir == "${U}") { + currDir = M ("PROFILEPANEL_MYPROFILES"); + } else if (currDir == "${G}") { + currDir = M ("PROFILEPANEL_GLOBALPROFILES"); + } + + // add this localized text to the file list + entries.push_back ( new ProfileStoreEntry (currDir, PSET_FOLDER, parentId, folder) ); + } + + // walking through the directory + Glib::Dir* dir = nullptr; + dir = new Glib::Dir (realPath); + + for (Glib::DirIterator i = dir->begin(); i != dir->end(); ++i) { + currDir = *i; + + if (currDir == "." || currDir == "..") { + continue; + } + + Glib::ustring fname = Glib::build_filename (realPath, currDir); + + if (Glib::file_test (fname, Glib::FILE_TEST_IS_DIR)) { + Glib::ustring vp (Glib::build_filename (virtualPath, currDir)); + Glib::ustring rp (Glib::build_filename (realPath, currDir)); + fileFound = parseDir (rp, vp, currDir, folder, level + 1, 0); + } else { + size_t lastdot = currDir.find_last_of ('.'); + + if (lastdot != Glib::ustring::npos && lastdot == currDir.length() - 4 && currDir.substr (lastdot).casefold() == paramFileExtension) { + // file found + if ( options.rtSettings.verbose ) { + printf ("Processing file %s...", fname.c_str()); + } + + Glib::ustring name = currDir.substr (0, lastdot); + + // create the partial profile + AutoPartialProfile *pProf = new AutoPartialProfile(); + int res = pProf->load (fname); + + if (!res && pProf->pparams->ppVersion >= 220) { + fileFound = true; + + if ( options.rtSettings.verbose ) { + printf ("OK\n"); + } + + // adding this file to the list + ProfileStoreEntry* filePSE = new ProfileStoreEntry (name, PSET_FILE, folder, 0); + entries.push_back (filePSE); + + // map the partial profile + partProfiles[filePSE] = pProf; + //partProfiles.insert( std::pair (filePSE, pProf) ); + } else if ( options.rtSettings.verbose ) { + printf ("failed!\n"); + } + } + } + } + + delete dir; + } + + if (!fileFound && (level > 0 || displayLevel0)) { + // no files found in this level, we delete the subdirectory entry + folders.pop_back(); + entries.pop_back(); + } + + return fileFound; +} + +int ProfileStore::findFolderId (const Glib::ustring &path) +{ + // initialization must have been done when calling this + for (std::vector::iterator i = folders.begin(); i != folders.end(); ++i) { + if (*i == path) { + return i - folders.begin(); + } + } + + return -1; +} + +/** @brief Return the ProfileStoreEntry object that match the given file and path + * + * @param fullPath Path of the file; the filename may end by the standard extension, + * but have to begin with a virtual location ( ${G} or ${U} ) + * Will return null on invalid path or if the entry can't be found + */ +const ProfileStoreEntry* ProfileStore::findEntryFromFullPathU (Glib::ustring path) +{ + if (path.empty()) { + return nullptr; + } + + if (storeState == STORESTATE_NOTINITIALIZED) { + parseProfiles(); + } + + if (path == DEFPROFILE_INTERNAL || path == DEFPROFILE_DYNAMIC) { + return internalDefaultEntry; + } + + // consistently apply casefold() to make sure dot position is correct + const Glib::ustring casefolded_path = path.casefold(); + const Glib::ustring::size_type lastdot_pos = casefolded_path.find_last_of ('.'); + + if ( + lastdot_pos != Glib::ustring::npos + && lastdot_pos <= casefolded_path.size() - 4 + && !casefolded_path.compare (lastdot_pos, 4, paramFileExtension) + ) { + // removing the extension + // now use dot position without casefold() + path = path.substr (0, path.find_last_of ('.')); + } + + // dir separator may come from options file and may be \ or /, we convert them to G_DIR_SEPARATOR_S + if (path.size() > 4 && (path[4] == '/' || path[4] == '\\')) { + path = path.substr (0, 4) + G_DIR_SEPARATOR_S + path.substr (5); + } + + // removing the filename + Glib::ustring fName = Glib::path_get_basename (path); + + if (!fName.empty()) { + path = path.substr (0, path.length() - fName.length()); + } else { + // path is malformed, returning NULL; + return nullptr; + } + + path = Glib::path_get_dirname (path); + + // 1. find the path in the folder list + int parentFolderId = findFolderId (path); + + if (parentFolderId == -1) { + return nullptr; + } + + // 2. find the entry that match the given filename and parentFolderId + if (parentFolderId >= 0) { + for (auto entry : entries) { + if (entry->parentFolderId == parentFolderId && entry->label == fName) { + return entry; + } + } + } + + return nullptr; +} + +/** Protected version of findEntryFromFullPathU */ +const ProfileStoreEntry* ProfileStore::findEntryFromFullPath (Glib::ustring path) +{ + MyMutex::MyLock lock (parseMutex); + return findEntryFromFullPathU (path); +} + +const PartialProfile* ProfileStore::getProfile (Glib::ustring path) +{ + + if (storeState == STORESTATE_NOTINITIALIZED) { + parseProfiles(); + } + + const ProfileStoreEntry *pse = findEntryFromFullPath (path); + + if (!pse) { + return nullptr; + } + + return getProfile (pse); +} + +const PartialProfile* ProfileStore::getProfile (const ProfileStoreEntry* entry) +{ + + if (storeState == STORESTATE_NOTINITIALIZED) { + parseProfiles(); + } + + MyMutex::MyLock lock (parseMutex); + + if (entry == internalDefaultEntry) { + return internalDefaultProfile; + } + + std::map::iterator iter = partProfiles.find (entry); + + if (iter != partProfiles.end()) { + return iter->second; + } else { + // This shouldn't happen! +#ifndef NDEBUG + printf ("WARNING! Profile not found!\n"); +#endif + return nullptr; + } +} + +/** @brief Get a pointer to the profile's vector list + * + * This method grants you unique access to the vector list through Mutex locking. + * When you're done with the file list, you MUST call the releaseFileList method to release the lock. + */ +const std::vector* ProfileStore::getFileList () +{ + + if (storeState == STORESTATE_NOTINITIALIZED) { + parseProfiles(); + } + + parseMutex.lock(); + + return &entries; +} + +void ProfileStore::releaseFileList() +{ + parseMutex.unlock(); +} + +/* + * Send back a pointer to the default procparams for raw or standard images. + * If the profile doesn't already exist in the profile list, + * it will add it with default internal values, so this method never fails + */ +const ProcParams* ProfileStore::getDefaultProcParams (bool isRaw) +{ + + //Note: the mutex is locked in getProfile, called below + // eventual initialization is done there too + + const PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg); + + if (!pProf) { + pProf = internalDefaultProfile; + } + + return pProf->pparams; +} + +/* + * Send back a pointer to the default partial profile for raw or standard images. + * If it doesn't already exist in the profile list, it will add it with default internal values, + * so this method will never fails + */ +const PartialProfile* ProfileStore::getDefaultPartialProfile (bool isRaw) +{ + + //Note: the mutex is locked in getProfile, called below + // eventual initialization is done there too + + const PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg); + + if (!pProf) { + pProf = internalDefaultProfile; + } + + return pProf; +} + +const Glib::ustring ProfileStore::getPathFromId (int folderId) +{ + // initialization must have been done when calling this + return folders.at (folderId); +} + + +void ProfileStore::clearFileList() +{ + for (auto entry : entries) { + if (entry != internalDefaultEntry) { + delete entry; + } + } + + entries.clear(); +} + +void ProfileStore::clearProfileList() +{ + for (auto partProfile : partProfiles) { + if (partProfile.second != internalDefaultProfile) { + delete partProfile.second; + } + } + + partProfiles.clear(); +} + +void ProfileStore::addListener (ProfileStoreListener *listener) +{ + listeners.push_back (listener); +} + +void ProfileStore::removeListener (ProfileStoreListener *listener) +{ + listeners.remove (listener); +} + +void ProfileStore::dumpFolderList() +{ + printf ("Folder list:\n------------\n"); + + for (unsigned int i = 0; i < folders.size(); i++) { + printf (" #%3ud - %s\n", i, folders.at (i).c_str()); + } + + printf ("\n"); +} + +PartialProfile *ProfileStore::loadDynamicProfile (const ImageMetaData *im) +{ + if (storeState == STORESTATE_NOTINITIALIZED) { + parseProfiles(); + } + + PartialProfile *ret = new PartialProfile (true, true); + + if (!rulesLoaded) { + loadRules(); + } + + for (auto rule : dynamicRules) { + if (rule.matches (im)) { + if (options.rtSettings.verbose) { + printf ("found matching profile %s\n", rule.profilepath.c_str()); + } + + const PartialProfile *p = getProfile (rule.profilepath); + + if (p != nullptr) { + p->applyTo (ret->pparams); + } else { + printf ("ERROR loading matching profile from: %s\n", rule.profilepath.c_str()); + } + } + } + + return ret; +} + +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) {} + +void ProfileStoreEntry::setValues (Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder) +{ + this->label = label; + this->type = type; + parentFolderId = parentFolder; + folderId = folder; +} + diff --git a/rtgui/profilestore.h b/rtengine/profilestore.h similarity index 67% rename from rtgui/profilestore.h rename to rtengine/profilestore.h index ad569c180..c6627b4c8 100644 --- a/rtgui/profilestore.h +++ b/rtengine/profilestore.h @@ -23,16 +23,14 @@ #include #include -#include "../rtengine/rtengine.h" -#include "../rtengine/noncopyable.h" - -#include "threadutils.h" -#include "paramsedited.h" -#include "guiutils.h" +#include "rtengine.h" +#include "noncopyable.h" +#include "dynamicprofile.h" // forward decl class DynamicProfileRule; +class DynamicProfileRules; /** @brief This will implement callback functions for the ProfileStore * @@ -83,7 +81,7 @@ public: * @param parentFolder index of the elements's path in the folder list * @param folder index of the folder's own path in the folder list */ - ProfileStoreEntry(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder); + ProfileStoreEntry (Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder); /** @brief Set the values of the object after its instantiation * @param label Label to be used in menu or combobox; also used as profile's filename @@ -91,31 +89,7 @@ public: * @param parentFolder index of the elements's path in the folder list * @param folder index of the folder's own path in the folder list */ - void setValues(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder); -}; - - -/** - * @brief subclass of Gtk::Label with extra fields for Combobox and Menu, to link with a ProfileStoreEntry - */ -class ProfileStoreLabel : public Gtk::Label -{ - -public: - const ProfileStoreEntry *entry; - -#ifndef NDEBUG - ProfileStoreLabel() : Gtk::Label("*** error ***"), entry(nullptr) {} -#else - ProfileStoreLabel() : Gtk::Label(""), entry(NULL) {} -#endif - - /** @brief Create a new ProfileStoreLabel - * - * @param entry Pointer to the ProfileStoreEntry object, be it a directory or a file - */ - explicit ProfileStoreLabel(const ProfileStoreEntry *entry); - ProfileStoreLabel (const ProfileStoreLabel &other); + void setValues (Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder); }; @@ -124,12 +98,12 @@ public: * This store can be queried by the GUI to display a Tree of the profiles available * in the user's and system's profile directory and subdirectories. */ -class ProfileStore : - public rtengine::NonCopyable +class ProfileStore : public rtengine::NonCopyable, public DynamicProfileRules { typedef enum { STORESTATE_NOTINITIALIZED, + STORESTATE_LIGHTWEIGHT, STORESTATE_BEINGINITIALIZED, STORESTATE_INITIALIZED, STORESTATE_DELETED @@ -137,13 +111,13 @@ class ProfileStore : private: struct SortProfiles { - bool operator ()(const ProfileStoreEntry* const a1, const ProfileStoreEntry* const a2) + bool operator () (const ProfileStoreEntry* const a1, const ProfileStoreEntry* const a2) { return a1->parentFolderId == a2->parentFolderId ? a1->label < a2->label : a1->parentFolderId < a2->parentFolderId; } }; - MyMutex *parseMutex; + MyMutex parseMutex; StoreState storeState; rtengine::procparams::AutoPartialProfile *internalDefaultProfile; ProfileStoreEntry *internalDefaultEntry; @@ -164,8 +138,8 @@ private: /** List of the client of this store */ std::list listeners; - /** cache for dynamic profile rules */ - std::unique_ptr> dynamicRules; + /** whereas we have to load all profile at init time or one by one upon request */ + bool loadAll; /** @brief Method to recursively parse a profile folder with a level depth arbitrarily limited to 3 * @@ -182,24 +156,26 @@ private: void _parseProfiles (); void clearFileList (); void clearProfileList (); - const ProfileStoreEntry* findEntryFromFullPathU(Glib::ustring path); + const ProfileStoreEntry* findEntryFromFullPathU (Glib::ustring path); public: ProfileStore(); ~ProfileStore(); - bool init (); + static ProfileStore* getInstance(); + + bool init (bool loadAll = true); void parseProfiles (); - int findFolderId(const Glib::ustring &path); - const ProfileStoreEntry* findEntryFromFullPath(Glib::ustring path); + int findFolderId (const Glib::ustring &path); + const ProfileStoreEntry* findEntryFromFullPath (Glib::ustring path); const rtengine::procparams::PartialProfile* getProfile (Glib::ustring path); const rtengine::procparams::PartialProfile* getProfile (const ProfileStoreEntry* entry); const std::vector* getFileList (); void releaseFileList (); const rtengine::procparams::ProcParams* getDefaultProcParams (bool isRaw); const rtengine::procparams::PartialProfile* getDefaultPartialProfile (bool isRaw); - const Glib::ustring getPathFromId(int folderId); + const Glib::ustring getPathFromId (int folderId); const ProfileStoreEntry* getInternalDefaultPSE() { return internalDefaultEntry; @@ -210,53 +186,12 @@ public: return internalDynamicEntry; } - const std::vector &getDynamicProfileRules() const; - void setDynamicProfileRules(const std::vector &r); + void addListener (ProfileStoreListener *listener); + void removeListener (ProfileStoreListener *listener); - void addListener(ProfileStoreListener *listener); - void removeListener(ProfileStoreListener *listener); + rtengine::procparams::PartialProfile* loadDynamicProfile (const rtengine::ImageMetaData *im); void dumpFolderList(); - }; -class ProfileStoreComboBox : public MyComboBox -{ - -protected: - class MethodColumns : public Gtk::TreeModel::ColumnRecord - { - public: - Gtk::TreeModelColumn label; - Gtk::TreeModelColumn profileStoreEntry; - MethodColumns() - { - add(label); - add(profileStoreEntry); - } - }; - - Glib::RefPtr refTreeModel; - MethodColumns methodColumns; - void refreshProfileList_ (Gtk::TreeModel::Row *parentRow, int parentFolderId, bool initial, const std::vector *entryList); - Gtk::TreeIter findRowFromEntry_ (Gtk::TreeModel::Children childs, const ProfileStoreEntry *pse); - Gtk::TreeIter findRowFromFullPath_(Gtk::TreeModel::Children childs, int parentFolderId, Glib::ustring &name); - -public: - ProfileStoreComboBox(); - void updateProfileList(); - Glib::ustring getCurrentLabel(); - const ProfileStoreEntry* getSelectedEntry(); - Gtk::TreeIter findRowFromEntry (const ProfileStoreEntry *pse); - Gtk::TreeIter findRowFromFullPath (Glib::ustring path); - Glib::ustring getFullPathFromActiveRow (); - bool setActiveRowFromFullPath (Glib::ustring oldPath); - bool setActiveRowFromEntry (const ProfileStoreEntry *pse); - bool setInternalEntry (); - Gtk::TreeIter getRowFromLabel(Glib::ustring name); - Gtk::TreeIter addRow(const ProfileStoreEntry *profileStoreEntry); -}; - -extern ProfileStore profileStore; - #endif diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index f28065d09..61e95c1a9 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -32,8 +32,7 @@ #include #include #include -#include "../rtgui/cachemanager.h" -#include "../rtgui/cacheimagedata.h" +#include "../rtgui/md5helper.h" #undef THREAD_PRIORITY_NORMAL @@ -1025,11 +1024,8 @@ private: if (params.locallab.enabled) { MyTime t1, t2; t1.set(); - CacheManager* cachemgr; - CacheImageData cfs; - cfs.md5 = cachemgr->getMD5 (imgsrc->getFileName()); - std::string mdfive = cfs.md5; + std::string mdfive = getMD5 (imgsrc->getFileName()); Glib::ustring pop = options.cacheBaseDir + "/mip/"; @@ -2137,11 +2133,12 @@ private: params.wavelet.strength *= scale_factor; params.dirpyrDenoise.luma *= scale_factor; - params.dirpyrDenoise.Ldetail += (100 - params.dirpyrDenoise.Ldetail) * scale_factor; - //params.dirpyrDenoise.smethod = "shal"; - for (auto &p : params.dirpyrDenoise.lcurve) { - p *= scale_factor; + //params.dirpyrDenoise.Ldetail += (100 - params.dirpyrDenoise.Ldetail) * scale_factor; + auto &lcurve = params.dirpyrDenoise.lcurve; + for (size_t i = 2; i < lcurve.size(); i += 4) { + lcurve[i] *= min(scale_factor * 2, 1.0); } + noiseLCurve.Set(lcurve); const char *medmethods[] = { "soft", "33", "55soft", "55", "77", "99" }; if (params.dirpyrDenoise.median) { auto &key = params.dirpyrDenoise.methodmed == "RGB" ? params.dirpyrDenoise.rgbmethod : params.dirpyrDenoise.medmethod; diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 9514ab6c4..6111b0ca7 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -46,7 +46,6 @@ set(NONCLISOURCEFILES dirpyrdenoise.cc dirpyrequalizer.cc distortion.cc - dynamicprofile.cc dynamicprofilepanel.cc edit.cc editorpanel.cc @@ -108,7 +107,7 @@ set(NONCLISOURCEFILES previewmodepanel.cc previewwindow.cc profilepanel.cc - profilestore.cc + profilestorecombobox.cc prsharpening.cc rawcacorrection.cc rawexposure.cc @@ -220,15 +219,15 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/config.h.in" "${CMAKE_CURRENT_BINARY # Create new executables targets add_executable(rth ${EXTRA_SRC_NONCLI} ${NONCLISOURCEFILES}) -#add_executable(rth-cli ${EXTRA_SRC_CLI} ${CLISOURCEFILES}) +add_executable(rth-cli ${EXTRA_SRC_CLI} ${CLISOURCEFILES}) # Add dependencies to executables targets add_dependencies(rth UpdateInfo) -#add_dependencies(rth-cli UpdateInfo) +add_dependencies(rth-cli UpdateInfo) # Set executables targets properties, i.e. output filename and compile flags set_target_properties(rth PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS}" OUTPUT_NAME rawtherapee) -#set_target_properties(rth-cli PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS}" OUTPUT_NAME rawtherapee-cli) +set_target_properties(rth-cli PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS}" OUTPUT_NAME rawtherapee-cli) # Add linked libraries dependencies to executables targets target_link_libraries(rth rtengine @@ -252,25 +251,25 @@ target_link_libraries(rth rtengine ${ZLIB_LIBRARIES} ) -#target_link_libraries(rth-cli rtengine -# ${CAIROMM_LIBRARIES} -# ${EXPAT_LIBRARIES} -# ${EXTRA_LIB_RTGUI} -# ${FFTW3F_LIBRARIES} -# ${GIOMM_LIBRARIES} -# ${GIO_LIBRARIES} -# ${GLIB2_LIBRARIES} -# ${GLIBMM_LIBRARIES} -# ${GOBJECT_LIBRARIES} -# ${GTHREAD_LIBRARIES} -# ${IPTCDATA_LIBRARIES} -# ${JPEG_LIBRARIES} -# ${LCMS_LIBRARIES} -# ${PNG_LIBRARIES} -# ${TIFF_LIBRARIES} -# ${ZLIB_LIBRARIES} -# ) +target_link_libraries(rth-cli rtengine + ${CAIROMM_LIBRARIES} + ${EXPAT_LIBRARIES} + ${EXTRA_LIB_RTGUI} + ${FFTW3F_LIBRARIES} + ${GIOMM_LIBRARIES} + ${GIO_LIBRARIES} + ${GLIB2_LIBRARIES} + ${GLIBMM_LIBRARIES} + ${GOBJECT_LIBRARIES} + ${GTHREAD_LIBRARIES} + ${IPTCDATA_LIBRARIES} + ${JPEG_LIBRARIES} + ${LCMS_LIBRARIES} + ${PNG_LIBRARIES} + ${TIFF_LIBRARIES} + ${ZLIB_LIBRARIES} + ) # Install executables install(TARGETS rth DESTINATION ${BINDIR}) -#install(TARGETS rth-cli DESTINATION ${BINDIR}) +install(TARGETS rth-cli DESTINATION ${BINDIR}) diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc index eee97c2ad..d5a728a97 100644 --- a/rtgui/bayerprocess.cc +++ b/rtgui/bayerprocess.cc @@ -945,21 +945,53 @@ void BayerProcess::pixelShiftMotionMethodChanged () void BayerProcess::FrameCountChanged(int n, int frameNum) { - GThreadLock lock; - imageNumber->block (true); + struct Data { + BayerProcess *me; + int n; + int frameNum; + }; + const auto func = [](gpointer data) -> gboolean { + Data *d = static_cast(data); + BayerProcess *me = d->me; + me->imageNumber->block (true); + int n = d->n; + int frameNum = d->frameNum; - imageNumber->remove_all(); - imageNumber->append("1"); - for(int i = 2; i <= std::min(n, 4); ++i) { - std::ostringstream entry; - entry << i; - imageNumber->append(entry.str()); - } - imageNumber->set_active(std::min(frameNum, n - 1)); - if(n == 1) { - imageNumberBox->hide(); - } else { - imageNumberBox->show(); - } - imageNumber->block (false); + me->imageNumber->remove_all(); + me->imageNumber->append("1"); + for(int i = 2; i <= std::min(n, 4); ++i) { + std::ostringstream entry; + entry << i; + me->imageNumber->append(entry.str()); + } + me->imageNumber->set_active(std::min(frameNum, n - 1)); + if(n == 1) { + me->imageNumberBox->hide(); + } else { + me->imageNumberBox->show(); + } + me->imageNumber->block (false); + delete d; + return FALSE; + }; + + idle_register.add(func, new Data { this, n, frameNum }); + + // GThreadLock lock; + // imageNumber->block (true); + + // imageNumber->remove_all(); + // imageNumber->append("1"); + // for(int i = 2; i <= std::min(n, 4); ++i) { + // std::ostringstream entry; + // entry << i; + // imageNumber->append(entry.str()); + // } + // imageNumber->set_active(std::min(frameNum, n - 1)); + // if(n == 1) { + // imageNumberBox->hide(); + // } else { + // imageNumberBox->show(); + // } + // imageNumber->block (false); } diff --git a/rtgui/bayerprocess.h b/rtgui/bayerprocess.h index 3d15802a5..6d9dd6062 100644 --- a/rtgui/bayerprocess.h +++ b/rtgui/bayerprocess.h @@ -74,6 +74,8 @@ protected: Adjuster* pixelShiftRedBlueWeight; #endif int oldMethod; + + IdleRegister idle_register; public: BayerProcess (); diff --git a/rtgui/cachemanager.cc b/rtgui/cachemanager.cc index 2eb8a7ba0..94093d2f4 100644 --- a/rtgui/cachemanager.cc +++ b/rtgui/cachemanager.cc @@ -32,7 +32,7 @@ #include "options.h" #include "procparamchangers.h" #include "thumbnail.h" - +#include "md5helper.h" namespace { @@ -314,44 +314,6 @@ void CacheManager::deleteFiles (const Glib::ustring& fname, const std::string& m } } -std::string CacheManager::getMD5 (const Glib::ustring& fname) -{ - - auto file = Gio::File::create_for_path (fname); - - if (file && file->query_exists ()) { - -#ifdef WIN32 - - std::unique_ptr wfname (reinterpret_cast (g_utf8_to_utf16 (fname.c_str (), -1, NULL, NULL, NULL)), g_free); - - WIN32_FILE_ATTRIBUTE_DATA fileAttr; - - if (GetFileAttributesExW (wfname.get (), GetFileExInfoStandard, &fileAttr)) { - // We use name, size and creation time to identify a file. - const auto identifier = Glib::ustring::compose ("%1-%2-%3-%4", fileAttr.nFileSizeLow, fileAttr.ftCreationTime.dwHighDateTime, fileAttr.ftCreationTime.dwLowDateTime, fname); - return Glib::Checksum::compute_checksum (Glib::Checksum::CHECKSUM_MD5, identifier); - } - -#else - - try { - - if (auto info = file->query_info ()) { - // We only use name and size to identify a file. - const auto identifier = Glib::ustring::compose ("%1%2", fname, info->get_size ()); - return Glib::Checksum::compute_checksum (Glib::Checksum::CHECKSUM_MD5, identifier); - } - - } catch (Gio::Error&) {} - -#endif - - } - - return {}; -} - Glib::ustring CacheManager::getCacheFileName (const Glib::ustring& subDir, const Glib::ustring& fname, const Glib::ustring& fext, diff --git a/rtgui/cachemanager.h b/rtgui/cachemanager.h index 7d63608ff..ac40be64b 100644 --- a/rtgui/cachemanager.h +++ b/rtgui/cachemanager.h @@ -62,8 +62,6 @@ public: void clearmip () const; void clearFromCache (const Glib::ustring& fname, bool purge) const; - static std::string getMD5 (const Glib::ustring& fname); - Glib::ustring getCacheFileName (const Glib::ustring& subDir, const Glib::ustring& fname, const Glib::ustring& fext, diff --git a/rtgui/dynamicprofile.cc b/rtgui/dynamicprofile.cc deleted file mode 100644 index 4439a1562..000000000 --- a/rtgui/dynamicprofile.cc +++ /dev/null @@ -1,255 +0,0 @@ -/* -*- C++ -*- - * This file is part of RawTherapee. - * - * Copyright (c) 2017 Alberto Griggio - * - * RawTherapee is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * RawTherapee is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with RawTherapee. If not, see . - */ - -#include "dynamicprofile.h" -#include "profilestore.h" -#include -#include - -using namespace rtengine; -using namespace rtengine::procparams; - -namespace { - -const int ISO_MAX = 512000; -const double FNUMBER_MAX = 100.0; -const double FOCALLEN_MAX = 10000.0; -const double SHUTTERSPEED_MAX = 1000.0; -const double EXPCOMP_MIN = -20.0; -const double EXPCOMP_MAX = 20.0; - -} // namespace - - -bool DynamicProfileRule::Optional::operator()(const Glib::ustring &val) const -{ - if (!enabled) { - return true; - } - if (value.find("re:") == 0) { - // this is a regexp - return Glib::Regex::match_simple(value.substr(3), val, - Glib::REGEX_CASELESS); - } else { - // normal string comparison - return value.casefold() == val.casefold(); - } -} - - -DynamicProfileRule::DynamicProfileRule(): - serial_number(0), - iso(0, ISO_MAX), - fnumber(0, FNUMBER_MAX), - focallen(0, FOCALLEN_MAX), - shutterspeed(0, SHUTTERSPEED_MAX), - expcomp(EXPCOMP_MIN, EXPCOMP_MAX) -{ -} - - -bool DynamicProfileRule::operator<(const DynamicProfileRule &other) const -{ - return serial_number < other.serial_number; -} - - -bool DynamicProfileRule::matches(const rtengine::ImageMetaData *im) const -{ - return (iso(im->getISOSpeed()) - && fnumber(im->getFNumber()) - && focallen(im->getFocalLen()) - && shutterspeed(im->getShutterSpeed()) - && expcomp(im->getExpComp()) - && camera(im->getCamera()) - && lens(im->getLens())); -} - -namespace { - -void get_int_range(DynamicProfileRule::Range &dest, - const Glib::KeyFile &kf, const Glib::ustring &group, - const Glib::ustring &key) -{ - try { - int min = kf.get_integer(group, key + "_min"); - int max = kf.get_integer(group, key + "_max"); - if (min <= max) { - dest.min = min; - dest.max = max; - } - } catch (Glib::KeyFileError &e) { - } -} - - -void get_double_range(DynamicProfileRule::Range &dest, - const Glib::KeyFile &kf, const Glib::ustring &group, - const Glib::ustring &key) -{ - try { - double min = kf.get_double(group, key + "_min"); - double max = kf.get_double(group, key + "_max"); - if (min <= max) { - dest.min = min; - dest.max = max; - } - } catch (Glib::KeyFileError &e) { - } -} - - -void get_optional(DynamicProfileRule::Optional &dest, - const Glib::KeyFile &kf, const Glib::ustring &group, - const Glib::ustring &key) -{ - try { - bool e = kf.get_boolean(group, key + "_enabled"); - if (e) { - Glib::ustring s = kf.get_string(group, key + "_value"); - dest.enabled = e; - dest.value = s; - } - } catch (Glib::KeyFileError &) { - } -} - -void set_int_range(Glib::KeyFile &kf, const Glib::ustring &group, - const Glib::ustring &key, - const DynamicProfileRule::Range &val) -{ - kf.set_integer(group, key + "_min", val.min); - kf.set_integer(group, key + "_max", val.max); -} - -void set_double_range(Glib::KeyFile &kf, const Glib::ustring &group, - const Glib::ustring &key, - const DynamicProfileRule::Range &val) -{ - kf.set_double(group, key + "_min", val.min); - kf.set_double(group, key + "_max", val.max); -} - -void set_optional(Glib::KeyFile &kf, const Glib::ustring &group, - const Glib::ustring &key, - const DynamicProfileRule::Optional &val) -{ - kf.set_boolean(group, key + "_enabled", val.enabled); - kf.set_string(group, key + "_value", val.value); -} - -} // namespace - - -bool loadDynamicProfileRules(std::vector &out) -{ - out.clear(); - Glib::KeyFile kf; - try { - if (!kf.load_from_file( - Glib::build_filename(Options::rtdir, "dynamicprofile.cfg"))) { - return false; - } - } catch (Glib::Error &e) { - return false; - } - if (options.rtSettings.verbose) { - printf("loading dynamic profiles...\n"); - } - auto groups = kf.get_groups(); - for (auto group : groups) { - // groups are of the form "rule N", where N is a positive integer - if (group.find("rule ") != 0) { - return false; - } - std::istringstream buf(group.c_str() + 5); - int serial = 0; - if (!(buf >> serial) || !buf.eof()) { - return false; - } - if (options.rtSettings.verbose) { - printf(" loading rule %d\n", serial); - } - - out.emplace_back(DynamicProfileRule()); - DynamicProfileRule &rule = out.back(); - rule.serial_number = serial; - get_int_range(rule.iso, kf, group, "iso"); - get_double_range(rule.fnumber, kf, group, "fnumber"); - get_double_range(rule.focallen, kf, group, "focallen"); - get_double_range(rule.shutterspeed, kf, group, "shutterspeed"); - get_double_range(rule.expcomp, kf, group, "expcomp"); - get_optional(rule.camera, kf, group, "camera"); - get_optional(rule.lens, kf, group, "lens"); - try { - rule.profilepath = kf.get_string(group, "profilepath"); - } catch (Glib::KeyFileError &) { - out.pop_back(); - } - } - std::sort(out.begin(), out.end()); - return true; -} - - -bool storeDynamicProfileRules(const std::vector &rules) -{ - if (options.rtSettings.verbose) { - printf("saving dynamic profiles...\n"); - } - Glib::KeyFile kf; - for (auto &rule : rules) { - std::ostringstream buf; - buf << "rule " << rule.serial_number; - Glib::ustring group = buf.str(); - set_int_range(kf, group, "iso", rule.iso); - set_double_range(kf, group, "fnumber", rule.fnumber); - set_double_range(kf, group, "focallen", rule.focallen); - set_double_range(kf, group, "shutterspeed", rule.shutterspeed); - set_double_range(kf, group, "expcomp", rule.expcomp); - set_optional(kf, group, "camera", rule.camera); - set_optional(kf, group, "lens", rule.lens); - kf.set_string(group, "profilepath", rule.profilepath); - } - return kf.save_to_file( - Glib::build_filename(Options::rtdir, "dynamicprofile.cfg")); -} - - -PartialProfile *loadDynamicProfile(const ImageMetaData *im) -{ - PartialProfile *ret = new PartialProfile(true, true); - for (auto &rule : profileStore.getDynamicProfileRules()) { - if (rule.matches(im)) { - if (options.rtSettings.verbose) { - printf("found matching profile %s\n", - rule.profilepath.c_str()); - } - const PartialProfile *p = - profileStore.getProfile(rule.profilepath); - if (p != nullptr) { - p->applyTo(ret->pparams); - } else { - printf("ERROR loading matching profile from: %s\n", - rule.profilepath.c_str()); - } - } - } - return ret; -} diff --git a/rtgui/dynamicprofilepanel.cc b/rtgui/dynamicprofilepanel.cc index 172c06f47..db3a19ed0 100644 --- a/rtgui/dynamicprofilepanel.cc +++ b/rtgui/dynamicprofilepanel.cc @@ -19,8 +19,9 @@ #include "dynamicprofilepanel.h" #include "multilangmgr.h" -#include "profilestore.h" +#include "../rtengine/profilestore.h" #include "../rtengine/rtengine.h" +#include "../rtengine/dynamicprofile.h" #include #include @@ -29,65 +30,63 @@ // DynamicProfilePanel::EditDialog //----------------------------------------------------------------------------- -DynamicProfilePanel::EditDialog::EditDialog(const Glib::ustring &title, - Gtk::Window &parent): - Gtk::Dialog(title, parent) +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); + profilepath_ = Gtk::manage (new ProfileStoreComboBox()); + Gtk::HBox *hb = Gtk::manage (new Gtk::HBox()); + hb->pack_start (*Gtk::manage (new Gtk::Label (M ("DYNPROFILEEDITOR_PROFILE"))), false, false, 4); + hb->pack_start (*profilepath_, true, true, 2); + get_content_area()->pack_start (*hb, Gtk::PACK_SHRINK, 4); - add_optional(M("EXIFFILTER_CAMERA"), has_camera_, camera_); - add_optional(M("EXIFFILTER_LENS"), has_lens_, lens_); - - add_range(M("EXIFFILTER_ISO"), iso_min_, iso_max_); - add_range(M("EXIFFILTER_APERTURE"), fnumber_min_, fnumber_max_); - add_range(M("EXIFFILTER_FOCALLEN"), focallen_min_, focallen_max_); - add_range(M("EXIFFILTER_SHUTTER"), shutterspeed_min_, shutterspeed_max_); - add_range(M("EXIFFILTER_EXPOSURECOMPENSATION"), expcomp_min_, expcomp_max_); + add_optional (M ("EXIFFILTER_CAMERA"), has_camera_, camera_); + add_optional (M ("EXIFFILTER_LENS"), has_lens_, lens_); - add_button(M("GENERAL_OK"), 1); - add_button(M("GENERAL_CANCEL"), 2); + add_range (M ("EXIFFILTER_ISO"), iso_min_, iso_max_); + add_range (M ("EXIFFILTER_APERTURE"), fnumber_min_, fnumber_max_); + add_range (M ("EXIFFILTER_FOCALLEN"), focallen_min_, focallen_max_); + add_range (M ("EXIFFILTER_SHUTTER"), shutterspeed_min_, shutterspeed_max_); + add_range (M ("EXIFFILTER_EXPOSURECOMPENSATION"), expcomp_min_, expcomp_max_); + + add_button (M ("GENERAL_OK"), 1); + add_button (M ("GENERAL_CANCEL"), 2); set_ranges(); - + show_all_children(); } -void DynamicProfilePanel::EditDialog::set_rule( +void DynamicProfilePanel::EditDialog::set_rule ( const DynamicProfileRule &rule) { - iso_min_->set_value(rule.iso.min); - iso_max_->set_value(rule.iso.max); + iso_min_->set_value (rule.iso.min); + iso_max_->set_value (rule.iso.max); - fnumber_min_->set_value(rule.fnumber.min); - fnumber_max_->set_value(rule.fnumber.max); + fnumber_min_->set_value (rule.fnumber.min); + fnumber_max_->set_value (rule.fnumber.max); - focallen_min_->set_value(rule.focallen.min); - focallen_max_->set_value(rule.focallen.max); + focallen_min_->set_value (rule.focallen.min); + focallen_max_->set_value (rule.focallen.max); - shutterspeed_min_->set_value(rule.shutterspeed.min); - shutterspeed_max_->set_value(rule.shutterspeed.max); + shutterspeed_min_->set_value (rule.shutterspeed.min); + shutterspeed_max_->set_value (rule.shutterspeed.max); - expcomp_min_->set_value(rule.expcomp.min); - expcomp_max_->set_value(rule.expcomp.max); + expcomp_min_->set_value (rule.expcomp.min); + expcomp_max_->set_value (rule.expcomp.max); - has_camera_->set_active(rule.camera.enabled); - camera_->set_text(rule.camera.value); + has_camera_->set_active (rule.camera.enabled); + camera_->set_text (rule.camera.value); - has_lens_->set_active(rule.lens.enabled); - lens_->set_text(rule.lens.value); + has_lens_->set_active (rule.lens.enabled); + lens_->set_text (rule.lens.value); profilepath_->updateProfileList(); - if (!profilepath_->setActiveRowFromFullPath(rule.profilepath)) { + + if (!profilepath_->setActiveRowFromFullPath (rule.profilepath)) { profilepath_->setInternalEntry(); } } - DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule() { @@ -97,7 +96,7 @@ DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule() 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(); @@ -112,7 +111,7 @@ DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule() ret.lens.enabled = has_lens_->get_active(); ret.lens.value = lens_->get_text(); - + ret.profilepath = profilepath_->getFullPathFromActiveRow(); return ret; @@ -121,20 +120,20 @@ DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule() void DynamicProfilePanel::EditDialog::set_ranges() { DynamicProfileRule default_rule; - iso_min_->set_digits(0); - iso_max_->set_digits(0); - iso_min_->set_increments(1, 10); - iso_max_->set_increments(1, 10); - iso_min_->set_range(default_rule.iso.min, default_rule.iso.max); - iso_max_->set_range(default_rule.iso.min, default_rule.iso.max); - iso_min_->set_value(default_rule.iso.min); - iso_max_->set_value(default_rule.iso.max); + iso_min_->set_digits (0); + iso_max_->set_digits (0); + iso_min_->set_increments (1, 10); + iso_max_->set_increments (1, 10); + iso_min_->set_range (default_rule.iso.min, default_rule.iso.max); + iso_max_->set_range (default_rule.iso.min, default_rule.iso.max); + iso_min_->set_value (default_rule.iso.min); + iso_max_->set_value (default_rule.iso.max); -#define DOIT_(name) \ - name ## _min_->set_digits(1); \ - name ## _max_->set_digits(1); \ - name ## _min_->set_increments(0.1, 1); \ - name ## _max_->set_increments(0.1, 1); \ +#define DOIT_(name) \ + name ## _min_->set_digits(1); \ + name ## _max_->set_digits(1); \ + name ## _min_->set_increments(0.1, 1); \ + name ## _max_->set_increments(0.1, 1); \ name ## _min_->set_range(default_rule. name .min, \ default_rule. name .max); \ name ## _max_->set_range(default_rule. name .min, \ @@ -142,45 +141,42 @@ void DynamicProfilePanel::EditDialog::set_ranges() name ## _min_->set_value(default_rule. name .min); \ name ## _max_->set_value(default_rule. name .max) - DOIT_(fnumber); - DOIT_(focallen); - DOIT_(shutterspeed); - DOIT_(expcomp); + DOIT_ (fnumber); + DOIT_ (focallen); + DOIT_ (shutterspeed); + DOIT_ (expcomp); #undef DOIT_ - shutterspeed_min_->set_digits(4); - shutterspeed_max_->set_digits(4); + shutterspeed_min_->set_digits (4); + shutterspeed_max_->set_digits (4); profilepath_->setInternalEntry(); } -void DynamicProfilePanel::EditDialog::add_range(const Glib::ustring &name, +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); + 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) +void DynamicProfilePanel::EditDialog::add_optional (const Glib::ustring &name, Gtk::CheckButton *&check, Gtk::Entry *&field) { - check = Gtk::manage (new Gtk::CheckButton(name)); - Gtk::HBox *hb = Gtk::manage(new Gtk::HBox()); - hb->pack_start(*check, Gtk::PACK_SHRINK, 4); - field = Gtk::manage(new Gtk::Entry()); - hb->pack_start(*field, true, true, 2); - get_content_area()->pack_start(*hb, Gtk::PACK_SHRINK, 4); - field->set_tooltip_text(M("DYNPROFILEEDITOR_ENTRY_TOOLTIP")); + check = Gtk::manage (new Gtk::CheckButton (name)); + Gtk::HBox *hb = Gtk::manage (new Gtk::HBox()); + hb->pack_start (*check, Gtk::PACK_SHRINK, 4); + field = Gtk::manage (new Gtk::Entry()); + hb->pack_start (*field, true, true, 2); + get_content_area()->pack_start (*hb, Gtk::PACK_SHRINK, 4); + field->set_tooltip_text (M ("DYNPROFILEEDITOR_ENTRY_TOOLTIP")); } @@ -189,121 +185,135 @@ void DynamicProfilePanel::EditDialog::add_optional(const Glib::ustring &name, //----------------------------------------------------------------------------- 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")) + 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 (vbox_); - treeview_.set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_VERTICAL); - scrolledwindow_.add(treeview_); + treeview_.set_grid_lines (Gtk::TREE_VIEW_GRID_LINES_VERTICAL); + scrolledwindow_.add (treeview_); - scrolledwindow_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + scrolledwindow_.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - vbox_.pack_start(scrolledwindow_); - vbox_.pack_start(buttonbox_, Gtk::PACK_SHRINK); + 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)); + buttonbox_.pack_start (button_new_, Gtk::PACK_SHRINK); + buttonbox_.pack_start (button_edit_, Gtk::PACK_SHRINK); + buttonbox_.pack_start (button_delete_, Gtk::PACK_SHRINK); + buttonbox_.pack_start (button_up_, Gtk::PACK_SHRINK); + buttonbox_.pack_start (button_down_, Gtk::PACK_SHRINK); + buttonbox_.set_border_width (5); + buttonbox_.set_layout (Gtk::BUTTONBOX_END); + button_up_.signal_clicked().connect ( + sigc::mem_fun (*this, &DynamicProfilePanel::on_button_up)); + button_down_.signal_clicked().connect ( + sigc::mem_fun (*this, &DynamicProfilePanel::on_button_down)); + button_new_.signal_clicked().connect ( + sigc::mem_fun (*this, &DynamicProfilePanel::on_button_new)); + button_edit_.signal_clicked().connect ( + sigc::mem_fun (*this, &DynamicProfilePanel::on_button_edit)); + button_delete_.signal_clicked().connect ( + sigc::mem_fun (*this, &DynamicProfilePanel::on_button_delete)); - treemodel_ = Gtk::ListStore::create(columns_); - treeview_.set_model(treemodel_); + treemodel_ = Gtk::ListStore::create (columns_); + treeview_.set_model (treemodel_); + + auto cell = Gtk::manage (new Gtk::CellRendererText()); + int cols_count = treeview_.append_column ( M ("DYNPROFILEEDITOR_PROFILE"), *cell); + auto col = treeview_.get_column (cols_count - 1); - 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( + col->set_cell_data_func ( + *cell, sigc::mem_fun ( *this, &DynamicProfilePanel::render_profilepath)); } - cell = Gtk::manage(new Gtk::CellRendererText()); - cols_count = treeview_.append_column( - M("EXIFFILTER_CAMERA"), *cell); - col = treeview_.get_column(cols_count - 1); + + cell = Gtk::manage (new Gtk::CellRendererText()); + cols_count = treeview_.append_column ( + M ("EXIFFILTER_CAMERA"), *cell); + col = treeview_.get_column (cols_count - 1); + if (col) { - col->set_cell_data_func( - *cell, sigc::mem_fun( + col->set_cell_data_func ( + *cell, sigc::mem_fun ( *this, &DynamicProfilePanel::render_camera)); } - cell = Gtk::manage(new Gtk::CellRendererText()); - cols_count = treeview_.append_column(M("EXIFFILTER_LENS"), *cell); - col = treeview_.get_column(cols_count - 1); + + 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( + 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); + + 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( + 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); + + 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( + 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); + + 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( + 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); + + 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( + 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); + + 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( + col->set_cell_data_func ( + *cell, sigc::mem_fun ( *this, &DynamicProfilePanel::render_expcomp)); } - + show_all_children(); - for (auto &r : profileStore.getDynamicProfileRules()) { - add_rule(r); + for (auto &r : ProfileStore::getInstance()->getRules()) { + add_rule (r); } } -void DynamicProfilePanel::update_rule(Gtk::TreeModel::Row row, +void DynamicProfilePanel::update_rule (Gtk::TreeModel::Row row, const DynamicProfileRule &rule) { row[columns_.iso] = rule.iso; @@ -316,15 +326,15 @@ void DynamicProfilePanel::update_rule(Gtk::TreeModel::Row row, row[columns_.profilepath] = rule.profilepath; } -void DynamicProfilePanel::add_rule(const DynamicProfileRule &rule) +void DynamicProfilePanel::add_rule (const DynamicProfileRule &rule) { - auto row = *(treemodel_->append()); - update_rule(row, rule); + auto row = * (treemodel_->append()); + update_rule (row, rule); } -DynamicProfileRule DynamicProfilePanel::to_rule(Gtk::TreeModel::Row row, - int serial) +DynamicProfileRule DynamicProfilePanel::to_rule (Gtk::TreeModel::Row row, + int serial) { DynamicProfileRule ret; ret.serial_number = serial; @@ -340,13 +350,14 @@ DynamicProfileRule DynamicProfilePanel::to_rule(Gtk::TreeModel::Row row, } -void DynamicProfilePanel::render_profilepath( +void DynamicProfilePanel::render_profilepath ( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { auto row = *iter; - Gtk::CellRendererText *ct = static_cast(cell); + Gtk::CellRendererText *ct = static_cast (cell); auto value = row[columns_.profilepath]; - auto pse = profileStore.findEntryFromFullPath(value); + auto pse = ProfileStore::getInstance()->findEntryFromFullPath (value); + if (pse != nullptr) { ct->property_text() = pse->label; } else { @@ -368,54 +379,56 @@ void DynamicProfilePanel::render_profilepath( } -namespace { +namespace +{ template -Glib::ustring to_str(V n, int precision=1) +Glib::ustring to_str (V n, int precision = 1) { std::ostringstream buf; - buf << std::setprecision(precision) << std::fixed << n; + buf << std::setprecision (precision) << std::fixed << n; return buf.str(); } } // namespace -void DynamicProfilePanel::render_iso( +void DynamicProfilePanel::render_iso ( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(int, iso, to_str); + RENDER_RANGE_ (int, iso, to_str); } -void DynamicProfilePanel::render_fnumber( +void DynamicProfilePanel::render_fnumber ( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, fnumber, - [](double f) - { return std::string("f/") + - rtengine::ImageMetaData::apertureToString(f); }); + RENDER_RANGE_ (double, fnumber, + [] (double f) { + return std::string ("f/") + + rtengine::ImageMetaData::apertureToString (f); + }); } -void DynamicProfilePanel::render_focallen( +void DynamicProfilePanel::render_focallen ( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, focallen, to_str); + RENDER_RANGE_ (double, focallen, to_str); } -void DynamicProfilePanel::render_shutterspeed( +void DynamicProfilePanel::render_shutterspeed ( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, shutterspeed, - rtengine::ImageMetaData::shutterToString); + RENDER_RANGE_ (double, shutterspeed, + rtengine::ImageMetaData::shutterToString); } -void DynamicProfilePanel::render_expcomp( +void DynamicProfilePanel::render_expcomp ( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_RANGE_(double, expcomp, to_str); + RENDER_RANGE_ (double, expcomp, to_str); } #undef RENDER_RANGE_ @@ -430,17 +443,17 @@ void DynamicProfilePanel::render_expcomp( ct->property_text() = ""; \ } -void DynamicProfilePanel::render_camera( +void DynamicProfilePanel::render_camera ( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_OPTIONAL_(camera); + RENDER_OPTIONAL_ (camera); } -void DynamicProfilePanel::render_lens( +void DynamicProfilePanel::render_lens ( Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter) { - RENDER_OPTIONAL_(lens); + RENDER_OPTIONAL_ (lens); } #undef RENDER_OPTIONAL_ @@ -448,28 +461,34 @@ void DynamicProfilePanel::render_lens( 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); + 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); + treemodel_->iter_swap (it, it2); } } @@ -477,22 +496,25 @@ void DynamicProfilePanel::on_button_down() void DynamicProfilePanel::on_button_delete() { auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { return; } + auto it = s->get_selected(); - treemodel_->erase(it); + treemodel_->erase (it); } void DynamicProfilePanel::on_button_new() { - EditDialog d(M("DYNPROFILEEDITOR_NEW_RULE"), - static_cast(*get_toplevel())); + EditDialog d (M ("DYNPROFILEEDITOR_NEW_RULE"), + static_cast (*get_toplevel())); int status = d.run(); + if (status == 1) { DynamicProfileRule rule = d.get_rule(); - add_rule(rule); + add_rule (rule); } } @@ -500,16 +522,19 @@ void DynamicProfilePanel::on_button_new() void DynamicProfilePanel::on_button_edit() { auto s = treeview_.get_selection(); + if (!s->count_selected_rows()) { return; } - EditDialog d(M("DYNPROFILEEDITOR_EDIT_RULE"), - static_cast(*get_toplevel())); - Gtk::TreeModel::Row row = *(s->get_selected()); - d.set_rule(to_rule(row)); + + EditDialog d (M ("DYNPROFILEEDITOR_EDIT_RULE"), + static_cast (*get_toplevel())); + Gtk::TreeModel::Row row = * (s->get_selected()); + d.set_rule (to_rule (row)); int status = d.run(); + if (status == 1) { - update_rule(row, d.get_rule()); + update_rule (row, d.get_rule()); } } @@ -518,15 +543,16 @@ void DynamicProfilePanel::save() { std::vector rules; int serial = 1; + for (auto row : treemodel_->children()) { - rules.emplace_back(to_rule(row, serial++)); + rules.emplace_back (to_rule (row, serial++)); } - if (!storeDynamicProfileRules(rules)) { - printf("Error in saving dynamic profile rules\n"); - } else { - profileStore.setDynamicProfileRules(rules); - if (options.rtSettings.verbose) { - printf("Saved %d dynamic profile rules\n", int(rules.size())); - } + + ProfileStore::getInstance()->setRules (rules); + + if (!ProfileStore::getInstance()->storeRules()) { + printf ("Error in saving dynamic profile rules\n"); + } else if (options.rtSettings.verbose) { + printf ("Saved %d dynamic profile rules\n", int (rules.size())); } } diff --git a/rtgui/dynamicprofilepanel.h b/rtgui/dynamicprofilepanel.h index 72ff95b9a..dca62f1e6 100644 --- a/rtgui/dynamicprofilepanel.h +++ b/rtgui/dynamicprofilepanel.h @@ -20,20 +20,20 @@ #define _DYNAMICPROFILEPANEL_H_ #include -#include "dynamicprofile.h" -#include "profilestore.h" +#include "../rtengine/dynamicprofile.h" +#include "profilestorecombobox.h" -class DynamicProfilePanel: public Gtk::VBox { +class DynamicProfilePanel: public Gtk::VBox +{ public: DynamicProfilePanel(); void save(); private: - void update_rule(Gtk::TreeModel::Row row, - const DynamicProfileRule &rule); - void add_rule(const DynamicProfileRule &rule); - DynamicProfileRule to_rule(Gtk::TreeModel::Row row, int serial=0); - + void update_rule (Gtk::TreeModel::Row row, const DynamicProfileRule &rule); + void add_rule (const DynamicProfileRule &rule); + DynamicProfileRule to_rule (Gtk::TreeModel::Row row, int serial = 0); + void on_button_quit(); void on_button_up(); void on_button_down(); @@ -41,18 +41,19 @@ private: void on_button_edit(); void on_button_delete(); - class DynamicProfileColumns: public Gtk::TreeModel::ColumnRecord { + class DynamicProfileColumns: public Gtk::TreeModel::ColumnRecord + { public: DynamicProfileColumns() { - add(iso); - add(fnumber); - add(focallen); - add(shutterspeed); - add(expcomp); - add(camera); - add(lens); - add(profilepath); + add (iso); + add (fnumber); + add (focallen); + add (shutterspeed); + add (expcomp); + add (camera); + add (lens); + add (profilepath); } Gtk::TreeModelColumn> iso; @@ -66,35 +67,26 @@ private: }; // cell renderers - void render_iso(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_fnumber(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_focallen(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_shutterspeed(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_expcomp(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_camera(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_lens(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); - void render_profilepath(Gtk::CellRenderer* cell, - const Gtk::TreeModel::iterator& iter); + void render_iso (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_fnumber (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_focallen (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_shutterspeed (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_expcomp (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_camera (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_lens (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); + void render_profilepath (Gtk::CellRenderer* cell, const Gtk::TreeModel::iterator& iter); - class EditDialog: public Gtk::Dialog { + class EditDialog: public Gtk::Dialog + { public: - EditDialog(const Glib::ustring &title, Gtk::Window &parent); - void set_rule(const DynamicProfileRule &rule); + EditDialog (const Glib::ustring &title, Gtk::Window &parent); + void set_rule (const DynamicProfileRule &rule); DynamicProfileRule get_rule(); private: void set_ranges(); - void add_range(const Glib::ustring &name, - Gtk::SpinButton *&from, Gtk::SpinButton *&to); - void add_optional(const Glib::ustring &name, - Gtk::CheckButton *&check, Gtk::Entry *&field); + 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_; diff --git a/rtgui/filebrowser.cc b/rtgui/filebrowser.cc index d96a89d09..dd37a740b 100644 --- a/rtgui/filebrowser.cc +++ b/rtgui/filebrowser.cc @@ -134,7 +134,7 @@ FileBrowser::FileBrowser () fbih->destroyed = false; fbih->pending = 0; - profileStore.addListener(this); + ProfileStore::getInstance()->addListener(this); int p = 0; pmenu = new Gtk::Menu (); @@ -457,7 +457,7 @@ FileBrowser::~FileBrowser () { idle_register.destroy(); - profileStore.removeListener(this); + ProfileStore::getInstance()->removeListener(this); delete pmenu; delete pmenuColorLabels; delete[] amiExtProg; @@ -1346,7 +1346,7 @@ void FileBrowser::applyMenuItemActivated (ProfileStoreLabel *label) { MYREADERLOCK(l, entryRW); - const rtengine::procparams::PartialProfile* partProfile = profileStore.getProfile (label->entry); + const rtengine::procparams::PartialProfile* partProfile = ProfileStore::getInstance()->getProfile (label->entry); if (partProfile->pparams && !selected.empty()) { if (bppcl) { @@ -1376,7 +1376,7 @@ void FileBrowser::applyPartialMenuItemActivated (ProfileStoreLabel *label) } } - const rtengine::procparams::PartialProfile* srcProfiles = profileStore.getProfile (label->entry); + const rtengine::procparams::PartialProfile* srcProfiles = ProfileStore::getInstance()->getProfile (label->entry); if (srcProfiles->pparams) { @@ -1982,7 +1982,7 @@ void FileBrowser::updateProfileList () // submenu applmenu int p = 0; - const std::vector *profEntries = profileStore.getFileList(); // lock and get a pointer to the profiles' list + const std::vector *profEntries = ProfileStore::getInstance()->getFileList(); // lock and get a pointer to the profiles' list std::map subMenuList; // store the Gtk::Menu that Gtk::MenuItem will have to be attached to @@ -2067,7 +2067,7 @@ void FileBrowser::updateProfileList () applypartprof->set_submenu (*(subMenuList.at(0))); } - profileStore.releaseFileList(); + ProfileStore::getInstance()->releaseFileList(); subMenuList.clear(); } diff --git a/rtgui/filebrowser.h b/rtgui/filebrowser.h index faede2d8c..4bad832eb 100644 --- a/rtgui/filebrowser.h +++ b/rtgui/filebrowser.h @@ -29,7 +29,7 @@ #include "partialpastedlg.h" #include "exportpanel.h" #include "extprog.h" -#include "profilestore.h" +#include "profilestorecombobox.h" class ProfileStoreLabel; class FileBrowser; diff --git a/rtgui/guiutils.cc b/rtgui/guiutils.cc index 8e4ae3613..91284e983 100644 --- a/rtgui/guiutils.cc +++ b/rtgui/guiutils.cc @@ -45,12 +45,6 @@ IdleRegister::~IdleRegister() void IdleRegister::add(GSourceFunc function, gpointer data, gint priority) { - struct DataWrapper { - IdleRegister* const self; - GSourceFunc function; - gpointer data; - }; - const auto dispatch = [](gpointer data) -> gboolean { DataWrapper* const data_wrapper = static_cast(data); @@ -80,9 +74,11 @@ void IdleRegister::add(GSourceFunc function, gpointer data, gint priority) void IdleRegister::destroy() { mutex.lock(); - for (const auto id : ids) { + for (const auto& id : ids) { g_source_remove(id.second); + delete id.first; } + ids.clear(); mutex.unlock(); } diff --git a/rtgui/guiutils.h b/rtgui/guiutils.h index b2ebaa80a..a6ec916f0 100644 --- a/rtgui/guiutils.h +++ b/rtgui/guiutils.h @@ -54,7 +54,13 @@ public: void destroy(); private: - std::map ids; + struct DataWrapper { + IdleRegister* const self; + GSourceFunc function; + gpointer data; + }; + + std::map ids; MyMutex mutex; }; diff --git a/rtgui/main-cli.cc b/rtgui/main-cli.cc index 3a601e18e..91dfc9204 100644 --- a/rtgui/main-cli.cc +++ b/rtgui/main-cli.cc @@ -49,6 +49,9 @@ #include "conio.h" #endif +// Set this to 1 to make RT work when started with Eclipse and arguments, at least on Windows platform +#define ECLIPSE_ARGS 0 + extern Options options; // stores path to data files @@ -175,7 +178,11 @@ int main(int argc, char **argv) SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); if (argc > 1 || options.rtSettings.verbose) { - if (options.rtSettings.verbose || ( !Glib::file_test (fname_to_utf8 (argv[1]), Glib::FILE_TEST_EXISTS ) && !Glib::file_test (fname_to_utf8 (argv[1]), Glib::FILE_TEST_IS_DIR))) { + Glib::ustring fname(fname_to_utf8 (argv[1])); +#if ECLIPSE_ARGS + fname = fname.substr(1, fname.length()-2); +#endif + if (options.rtSettings.verbose || ( !Glib::file_test (fname, Glib::FILE_TEST_EXISTS ) && !Glib::file_test (fname, Glib::FILE_TEST_IS_DIR))) { bool stdoutRedirectedtoFile = (GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)) == 0x0001); bool stderrRedirectedtoFile = (GetFileType(GetStdHandle(STD_ERROR_HANDLE)) == 0x0001); @@ -191,8 +198,7 @@ int main(int argc, char **argv) break; } - if(Console) { - AllocConsole(); + if(Console && AllocConsole()) { AttachConsole( GetCurrentProcessId() ) ; // Don't allow CTRL-C in console to terminate RT SetConsoleCtrlHandler( NULL, true ); @@ -268,8 +274,12 @@ void deleteProcParams(std::vector &pparam bool dontLoadCache( int argc, char **argv ) { - for( int iArg = 1; iArg < argc; iArg++) { - if( argv[iArg][0] == '-' && argv[iArg][1] == 'q' ) { + for (int iArg = 1; iArg < argc; iArg++) { + Glib::ustring currParam(argv[iArg]); +#if ECLIPSE_ARGS + currParam = currParam.substr(1, currParam.length()-2); +#endif + if( currParam.at(0) == '-' && currParam.at(1) == 'q' ) { return true; } } @@ -288,6 +298,7 @@ int processLineParams( int argc, char **argv ) bool sideProcParams = false; bool copyParamsFile = false; bool skipIfNoSidecar = false; + bool allExtensions = false; bool useDefault = false; unsigned int sideCarFilePos = 0; int compression = 92; @@ -297,16 +308,22 @@ int processLineParams( int argc, char **argv ) unsigned errors = 0; for( int iArg = 1; iArg < argc; iArg++) { - if( argv[iArg][0] == '-' ) { - switch( argv[iArg][1] ) { + Glib::ustring currParam(argv[iArg]); +#if ECLIPSE_ARGS + currParam = currParam.substr(1, currParam.length()-2); +#endif + if( currParam.at(0) == '-' ) { + switch( currParam.at(1) ) { case 'O': copyParamsFile = true; case 'o': // outputfile or dir if( iArg + 1 < argc ) { iArg++; - outputPath = fname_to_utf8 (argv[iArg]); - + outputPath = Glib::ustring(fname_to_utf8(argv[iArg])); +#if ECLIPSE_ARGS + outputPath = outputPath.substr(1, outputPath.length()-2); +#endif if( Glib::file_test (outputPath, Glib::FILE_TEST_IS_DIR)) { outputDirectory = true; } @@ -319,7 +336,10 @@ int processLineParams( int argc, char **argv ) // RT stop if any of them can't be loaded for any reason. if( iArg + 1 < argc ) { iArg++; - Glib::ustring fname = fname_to_utf8 (argv[iArg]); + Glib::ustring fname(fname_to_utf8(argv[iArg])); +#if ECLIPSE_ARGS + fname = fname.substr(1, fname.length()-2); +#endif if (fname.at(0) == '-') { std::cerr << "Error: filename missing next to the -p switch" << std::endl; @@ -359,16 +379,20 @@ int processLineParams( int argc, char **argv ) overwriteFiles = true; break; + case 'a': + allExtensions = true; + break; + case 'j': - if (strlen(argv[iArg]) > 2 && argv[iArg][2] == 's') { - if (strlen(argv[iArg]) == 3) { + if (currParam.length() > 2 && currParam.at(2) == 's') { + if (currParam.length() == 3) { std::cerr << "Error: the -js switch requires a mandatory value!" << std::endl; deleteProcParams(processingParams); return -3; } // looking for the subsampling parameter - sscanf(&argv[iArg][3], "%d", &subsampling); + subsampling = atoi(currParam.substr(3).c_str()); if (subsampling < 1 || subsampling > 3) { std::cerr << "Error: the value accompanying the -js switch has to be in the [1-3] range!" << std::endl; @@ -377,7 +401,7 @@ int processLineParams( int argc, char **argv ) } } else { outputType = "jpg"; - sscanf(&argv[iArg][2], "%d", &compression); + compression = atoi(currParam.substr(2).c_str()); if (compression < 0 || compression > 100) { std::cerr << "Error: the value accompanying the -j switch has to be in the [0-100] range!" << std::endl; @@ -389,7 +413,7 @@ int processLineParams( int argc, char **argv ) break; case 'b': - sscanf(&argv[iArg][2], "%d", &bits); + bits = atoi(currParam.substr(2).c_str()); if (bits != 8 && bits != 16) { std::cerr << "Error: specify -b8 for 8-bit or -b16 for 16-bit output." << std::endl; @@ -401,7 +425,7 @@ int processLineParams( int argc, char **argv ) case 't': outputType = "tif"; - compression = ((argv[iArg][2] != 'z') ? 0 : 1); + compression = ((currParam.at(2) != 'z') ? 0 : 1); break; case 'n': @@ -412,16 +436,35 @@ int processLineParams( int argc, char **argv ) case 'f': fast_export = true; break; - + case 'c': // MUST be last option while (iArg + 1 < argc) { iArg++; + Glib::ustring argument(fname_to_utf8(argv[iArg])); +#if ECLIPSE_ARGS + argument = argument.substr(1, argument.length()-2); +#endif - const auto argument = fname_to_utf8 (argv[iArg]); + if (!Glib::file_test (argument, Glib::FILE_TEST_EXISTS)) { + std::cout << "\"" << argument << "\" doesn't exist !" << std::endl; + continue; + } if (Glib::file_test (argument, Glib::FILE_TEST_IS_REGULAR)) { - inputFiles.emplace_back (argument); + bool notAll = allExtensions && !options.is_parse_extention (argument); + bool notRetained = !allExtensions && !options.has_retained_extention (argument); + if (notAll || notRetained) { + if (notAll) { + std::cout << "\"" << argument << "\" is not one of the file format to process: skipped" << std::endl; + } else if (notRetained) { + std::cout << "\"" << argument << "\" is not one of the retained file format to process: skipped" << std::endl; + } + } + else { + inputFiles.emplace_back (argument); + } continue; + } if (Glib::file_test (argument, Glib::FILE_TEST_IS_DIR)) { @@ -438,19 +481,28 @@ int processLineParams( int argc, char **argv ) while (auto file = enumerator->next_file ()) { const auto fileName = Glib::build_filename (argument, file->get_name ()); + bool isDir = Glib::file_test (fileName, Glib::FILE_TEST_IS_DIR); + bool notAll = allExtensions && !options.is_parse_extention (fileName); + bool notRetained = !allExtensions && !options.has_retained_extention (fileName); - if (Glib::file_test (fileName, Glib::FILE_TEST_IS_DIR)) { + if (isDir || notAll || notRetained) { + if (isDir) { + std::cout << "\"" << fileName << "\" is a directory: skipped" << std::endl; + } else if (notAll) { + std::cout << "\"" << fileName << "\" is not one of the file format to process: skipped" << std::endl; + } else if (notRetained) { + std::cout << "\"" << fileName << "\" is not one of the retained file format to process: skipped" << std::endl; + } continue; + } - // skip files without extension and sidecar files - auto lastdot = fileName.find_last_of('.'); - if (lastdot == Glib::ustring::npos) { - continue; - } - - if (fileName.substr (lastdot).compare (paramFileExtension) == 0) { - continue; + if (sideProcParams && skipIfNoSidecar) { + // look for the sidecar proc params + if (!Glib::file_test(fileName + paramFileExtension, Glib::FILE_TEST_EXISTS)) { + std::cout << "\"" << fileName << "\" has no side-car file: image skipped" << std::endl; + continue; + } } inputFiles.emplace_back (fileName); @@ -500,12 +552,16 @@ int processLineParams( int argc, char **argv ) std::cout << " " << Glib::path_get_basename(argv[0]) << " [-o |-O ] [-s|-S] [-p [-p ...] ] [-d] [ -j[1-100] [-js<1-3>] | [-b<8|16>] [-t[z] | [-n]] ] [-Y] [-f] -c " << std::endl; std::cout << std::endl; std::cout << " -q Quick Start mode : do not load cached files to speedup start time." << std::endl; - std::cout << " -c Specify one or more input files." << std::endl; + std::cout << " -c Specify one or more input files or directory." << std::endl; + std::cout << " When specifying directories, Rawtherapee will look for images files that comply with the" << std::endl; + std::cout << " selected extensions (see also '-a')." << std::endl; std::cout << " -c must be the last option." << std::endl; std::cout << " -o | Set output file or folder." << std::endl; std::cout << " Saves output file alongside input file if -o is not specified." << std::endl; std::cout << " -O | Set output file or folder and copy " << pparamsExt << " file into it." << std::endl; std::cout << " Saves output file alongside input file if -O is not specified." << std::endl; + std::cout << " -a stand for 'all'. When specifying a directory, process all images specified in the" << std::endl; + std::cout << " extension list from the options file, even those currently not seleted" << std::endl; std::cout << " -s Use the existing sidecar file to build the processing parameters," << std::endl; std::cout << " e.g. for photo.raw there should be a photo.raw." << pparamsExt << " file in the same folder." << std::endl; std::cout << " If the sidecar file does not exist, neutral values will be used." << std::endl; @@ -546,7 +602,10 @@ int processLineParams( int argc, char **argv ) } } } else { - argv1 = fname_to_utf8 (argv[iArg]); + argv1 = Glib::ustring(fname_to_utf8(argv[iArg])); +#if ECLIPSE_ARGS + argv1 = argv1.substr(1, argv1.length()-2); +#endif if( outputDirectory ) { options.savePathFolder = outputPath; @@ -587,7 +646,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() || (profPath != DEFPROFILE_DYNAMIC && 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, Glib::path_get_basename(options.defProfRaw) + paramFileExtension)))) { std::cerr << "Error: default raw processing profile not found" << std::endl; rawParams->deleteInstance(); delete rawParams; @@ -598,7 +657,7 @@ int processLineParams( int argc, char **argv ) imgParams = new rtengine::procparams::PartialProfile(true); profPath = options.findProfilePath(options.defProfImg); - 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)))) { + if (options.is_defProfImgMissing() || profPath.empty() || (profPath != DEFPROFILE_DYNAMIC && imgParams->load(profPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(profPath, Glib::path_get_basename(options.defProfImg) + paramFileExtension)))) { std::cerr << "Error: default non-raw processing profile not found" << std::endl; imgParams->deleteInstance(); delete imgParams; @@ -635,7 +694,7 @@ int processLineParams( int argc, char **argv ) } else if( outputDirectory ) { Glib::ustring s = Glib::path_get_basename( inputFile ); Glib::ustring::size_type ext = s.find_last_of('.'); - outputFile = outputPath + "/" + s.substr(0, ext) + "." + outputType; + outputFile = Glib::build_filename(outputPath, s.substr(0, ext) + "." + outputType); } else { Glib::ustring s = outputPath; Glib::ustring::size_type ext = s.find_last_of('.'); @@ -673,7 +732,7 @@ int processLineParams( int argc, char **argv ) if (options.defProfRaw == DEFPROFILE_DYNAMIC) { rawParams->deleteInstance(); delete rawParams; - rawParams = loadDynamicProfile(ii->getMetaData()); + rawParams = ProfileStore::getInstance()->loadDynamicProfile(ii->getMetaData()); } std::cout << " Merging default raw processing profile" << std::endl; rawParams->applyTo(¤tParams); @@ -681,7 +740,7 @@ int processLineParams( int argc, char **argv ) if (options.defProfImg == DEFPROFILE_DYNAMIC) { imgParams->deleteInstance(); delete imgParams; - imgParams = loadDynamicProfile(ii->getMetaData()); + imgParams = ProfileStore::getInstance()->loadDynamicProfile(ii->getMetaData()); } std::cout << " Merging default non-raw processing profile" << std::endl; imgParams->applyTo(¤tParams); diff --git a/rtgui/main.cc b/rtgui/main.cc index 644e4bb98..c7d9d4134 100644 --- a/rtgui/main.cc +++ b/rtgui/main.cc @@ -38,7 +38,7 @@ #include "rtimage.h" #include "version.h" #include "extprog.h" -#include "dynamicprofile.h" +#include "../rtengine/dynamicprofile.h" #ifndef WIN32 #include @@ -50,6 +50,9 @@ #include "conio.h" #endif +// Set this to 1 to make RT work when started with Eclipse and arguments, at least on Windows platform +#define ECLIPSE_ARGS 0 + extern Options options; // stores path to data files @@ -177,7 +180,6 @@ int main (int argc, char **argv) return -2; } - profileStore.init (); extProgStore->init(); SoundManager::init(); @@ -420,37 +422,41 @@ int processLineParams ( int argc, char **argv ) { unsigned errors = 0; - for ( int iArg = 1; iArg < argc; iArg++) { - if ( argv[iArg][0] == '-' ) { - switch ( argv[iArg][1] ) { + for( int iArg = 1; iArg < argc; iArg++) { + Glib::ustring currParam(argv[iArg]); +#if ECLIPSE_ARGS + currParam = currParam.substr(1, currParam.length()-2); +#endif + if( currParam.at(0) == '-' ) { + switch( currParam.at(1) ) { #ifdef WIN32 case 'w': // This case is handled outside this function break; #endif - case 'h': - case '?': - default: { - Glib::ustring pparamsExt = paramFileExtension.substr (1); - std::cout << " An advanced, cross-platform program for developing raw photos." << std::endl; - std::cout << std::endl; - std::cout << " Website: http://www.rawtherapee.com/" << std::endl; - std::cout << " Documentation: http://rawpedia.rawtherapee.com/" << std::endl; - std::cout << " Forum: https://discuss.pixls.us/c/software/rawtherapee" << std::endl; - std::cout << " Code and bug reports: https://github.com/Beep6581/RawTherapee" << std::endl; - std::cout << std::endl; - std::cout << "Symbols:" << std::endl; - std::cout << " indicate parameters you can change." << std::endl; - std::cout << " [Square brackets] mean the parameter is optional." << std::endl; - std::cout << " The pipe symbol | indicates a choice of one or the other." << std::endl; - std::cout << " The dash symbol - denotes a range of possible values from one to the other." << std::endl; - std::cout << std::endl; - std::cout << "Usage:" << std::endl; - std::cout << " " << Glib::path_get_basename (argv[0]) << " Start File Browser inside folder." << std::endl; - std::cout << " " << Glib::path_get_basename (argv[0]) << " Start Image Editor with file." << std::endl; - std::cout << std::endl; - std::cout << "Options:" << std::endl; + case 'h': + case '?': + default: { + Glib::ustring pparamsExt = paramFileExtension.substr(1); + std::cout << " An advanced, cross-platform program for developing raw photos." << std::endl; + std::cout << std::endl; + std::cout << " Website: http://www.rawtherapee.com/" << std::endl; + std::cout << " Documentation: http://rawpedia.rawtherapee.com/" << std::endl; + std::cout << " Forum: https://discuss.pixls.us/c/software/rawtherapee" << std::endl; + std::cout << " Code and bug reports: https://github.com/Beep6581/RawTherapee" << std::endl; + std::cout << std::endl; + std::cout << "Symbols:" << std::endl; + std::cout << " indicate parameters you can change." << std::endl; + //std::cout << " [Square brackets] mean the parameter is optional." << std::endl; + //std::cout << " The pipe symbol | indicates a choice of one or the other." << std::endl; + //std::cout << " The dash symbol - denotes a range of possible values from one to the other." << std::endl; + std::cout << std::endl; + std::cout << "Usage:" << std::endl; + std::cout << " " << Glib::path_get_basename(argv[0]) << " Start File Browser inside folder." << std::endl; + std::cout << " " << Glib::path_get_basename(argv[0]) << " Start Image Editor with file." << std::endl; + std::cout << std::endl; + std::cout << "Options:" << std::endl; #ifdef WIN32 std::cout << " -w Do not open the Windows console" << std::endl; #endif @@ -459,7 +465,10 @@ int processLineParams ( int argc, char **argv ) } } } else { - argv1 = fname_to_utf8 (argv[iArg]); + argv1 = Glib::ustring(fname_to_utf8(argv[iArg])); +#if ECLIPSE_ARGS + argv1 = argv1.substr(1, argv1.length()-2); +#endif break; } } diff --git a/rtgui/md5helper.h b/rtgui/md5helper.h new file mode 100644 index 000000000..17ee70f3a --- /dev/null +++ b/rtgui/md5helper.h @@ -0,0 +1,69 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-201 Gabor Horvath + * + * 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 +#include + +#include +#include + +#ifdef WIN32 +#include +#endif + +namespace { +std::string getMD5 (const Glib::ustring& fname) +{ + + auto file = Gio::File::create_for_path (fname); + + if (file && file->query_exists ()) { + +#ifdef WIN32 + + std::unique_ptr wfname (reinterpret_cast (g_utf8_to_utf16 (fname.c_str (), -1, NULL, NULL, NULL)), g_free); + + WIN32_FILE_ATTRIBUTE_DATA fileAttr; + + if (GetFileAttributesExW (wfname.get (), GetFileExInfoStandard, &fileAttr)) { + // We use name, size and creation time to identify a file. + const auto identifier = Glib::ustring::compose ("%1-%2-%3-%4", fileAttr.nFileSizeLow, fileAttr.ftCreationTime.dwHighDateTime, fileAttr.ftCreationTime.dwLowDateTime, fname); + return Glib::Checksum::compute_checksum (Glib::Checksum::CHECKSUM_MD5, identifier); + } + +#else + + try { + + if (auto info = file->query_info ()) { + // We only use name and size to identify a file. + const auto identifier = Glib::ustring::compose ("%1%2", fname, info->get_size ()); + return Glib::Checksum::compute_checksum (Glib::Checksum::CHECKSUM_MD5, identifier); + } + + } catch (Gio::Error&) {} + +#endif + + } + + return {}; +} + +} diff --git a/rtgui/options.cc b/rtgui/options.cc index 3021aa3b3..195514258 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -231,7 +231,7 @@ Glib::ustring Options::getPreferredProfilePath() /** @brief Get the absolute path of the given filename or the "Neutral" special value * *@param profName path + filename of the procparam to look for. A filename without path can be provided for backward compatibility. - * In this case, this parameter will be update with the new format. + * In this case, this parameter will be updated with the new format. *@return Send back the absolute path of the given filename or "Neutral" if "Neutral" has been set to profName. Implementor will have * to test for this particular value. If the absolute path is invalid (e.g. the file doesn't exist), it will return an empty string. */ @@ -2429,6 +2429,27 @@ void Options::save () } } +/* + * return true if ext is a parsed extension (retained or not) + */ +bool Options::is_parse_extention (Glib::ustring fname) +{ + Glib::ustring ext = getExtension (fname).lowercase(); + + if (!ext.empty()) { + // there is an extension to the filename + + // look out if it has one of the listed extensions (selected or not) + for (unsigned int i = 0; i < parseExtensions.size(); i++) { + if (ext == parseExtensions[i]) { + return true; + } + } + } + + return false; +} + /* * return true if fname ends with one of the retained image file extensions */ diff --git a/rtgui/options.h b/rtgui/options.h index 1103877d9..6a5f2db8a 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -337,7 +337,8 @@ public: { return globalProfilePath; } - Glib::ustring findProfilePath (Glib::ustring &profName); + Glib::ustring findProfilePath(Glib::ustring &profName); + bool is_parse_extention (Glib::ustring fname); bool has_retained_extention (Glib::ustring fname); bool is_extention_enabled (Glib::ustring ext); bool is_defProfRawMissing() diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index c9d87d973..68de43d91 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -87,20 +87,20 @@ Preferences::Preferences (RTWindow *rtwindow) get_action_area()->pack_end (*ok); get_action_area()->pack_end (*cancel); - 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")); - nb->append_page (*getPerformancePanel(), M ("PREFERENCES_TAB_PERFORMANCE")); + 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")); + nb->append_page (*getPerformancePanel(), M("PREFERENCES_TAB_PERFORMANCE")); // Sounds only on Windows and Linux #if defined(WIN32) || defined(__linux__) - nb->append_page (*getSoundPanel(), M ("PREFERENCES_TAB_SOUND")); + nb->append_page (*getSoundPanel(), M("PREFERENCES_TAB_SOUND")); #endif nb->set_current_page (0); - profileStore.addListener (this); + ProfileStore::getInstance()->addListener(this); fillPreferences (); @@ -111,8 +111,8 @@ Preferences::Preferences (RTWindow *rtwindow) Preferences::~Preferences () { - profileStore.removeListener (this); - get_size (options.preferencesWidth, options.preferencesHeight); + ProfileStore::getInstance()->removeListener(this); + get_size(options.preferencesWidth, options.preferencesHeight); } int Preferences::getThemeRowNumber (Glib::ustring& longThemeFName) @@ -465,16 +465,17 @@ 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)); + const ProfileStoreEntry* dynpse = ProfileStore::getInstance()->getInternalDynamicPSE(); + rprofiles->addRow(dynpse); + 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) ); + iprofiles->addRow(dynpse); + 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) ); Gtk::Table* defpt = Gtk::manage (new Gtk::Table (2, 2)); defpt->attach (*drlab, 0, 1, 0, 1, Gtk::FILL, Gtk::SHRINK, 2, 2); defpt->attach (*rprofiles, 1, 2, 0, 1, Gtk::EXPAND | Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 2, 2); @@ -2302,7 +2303,7 @@ void Preferences::bundledProfilesChanged () options.useBundledProfiles = useBundledProfiles->get_active (); // rescan the file's tree - profileStore.parseProfiles(); // This will call Preferences::updateProfileList in return + ProfileStore::getInstance()->parseProfiles(); // This will call Preferences::updateProfileList in return // restoring back the old value options.useBundledProfiles = currValue; @@ -2338,8 +2339,9 @@ void Preferences::updateProfileList() { rprofiles->updateProfileList(); iprofiles->updateProfileList(); - rprofiles->addRow (profileStore.getInternalDynamicPSE()); - iprofiles->addRow (profileStore.getInternalDynamicPSE()); + const ProfileStoreEntry* dynpse = ProfileStore::getInstance()->getInternalDynamicPSE(); + rprofiles->addRow(dynpse); + iprofiles->addRow(dynpse); } void Preferences::restoreValue() diff --git a/rtgui/profilepanel.cc b/rtgui/profilepanel.cc index 1e71c23e0..7947eebf3 100644 --- a/rtgui/profilepanel.cc +++ b/rtgui/profilepanel.cc @@ -18,9 +18,9 @@ */ #include "profilepanel.h" #include "options.h" -#include "profilestore.h" #include "clipboard.h" #include "multilangmgr.h" +#include "profilestorecombobox.h" #include "rtimage.h" using namespace rtengine; @@ -99,7 +99,7 @@ ProfilePanel::ProfilePanel () : storedPProfile(nullptr), lastFilename(""), image lastsaved = nullptr; dontupdate = false; - profileStore.addListener(this); + ProfileStore::getInstance()->addListener(this); changeconn = profiles->signal_changed().connect( sigc::mem_fun(*this, &ProfilePanel::selection_changed) ); @@ -114,7 +114,7 @@ ProfilePanel::ProfilePanel () : storedPProfile(nullptr), lastFilename(""), image ProfilePanel::~ProfilePanel () { - profileStore.removeListener(this); + ProfileStore::getInstance()->removeListener(this); if (custom) { custom->deleteInstance(); @@ -188,7 +188,7 @@ void ProfilePanel::storeCurrentValue () const ProfileStoreEntry *entry = profiles->getSelectedEntry(); const PartialProfile *currProfile; - if (entry && (currProfile = profileStore.getProfile(entry)) != nullptr) { + if (entry && (currProfile = ProfileStore::getInstance()->getProfile(entry)) != nullptr) { // now storedPProfile has the current entry's values storedPProfile = new PartialProfile(currProfile->pparams, currProfile->pedited, true); } else { @@ -318,7 +318,7 @@ void ProfilePanel::save_clicked (GdkEventButton* event) toSave = lastsaved; } else { const ProfileStoreEntry* entry = profiles->getSelectedEntry(); - toSave = entry ? profileStore.getProfile (profiles->getSelectedEntry()) : nullptr; + toSave = entry ? ProfileStore::getInstance()->getProfile (profiles->getSelectedEntry()) : nullptr; } if (toSave) { @@ -343,7 +343,7 @@ void ProfilePanel::save_clicked (GdkEventButton* event) } else { done = true; bool ccPrevState = changeconn.block(true); - profileStore.parseProfiles(); + ProfileStore::getInstance()->parseProfiles(); changeconn.block (ccPrevState); } } else { @@ -355,7 +355,7 @@ void ProfilePanel::save_clicked (GdkEventButton* event) } else { done = true; bool ccPrevState = changeconn.block(true); - profileStore.parseProfiles(); + ProfileStore::getInstance()->parseProfiles(); changeconn.block (ccPrevState); } } @@ -388,7 +388,7 @@ void ProfilePanel::copy_clicked (GdkEventButton* event) toSave = lastsaved; } else { const ProfileStoreEntry* entry = profiles->getSelectedEntry(); - toSave = entry ? profileStore.getProfile (entry) : nullptr; + toSave = entry ? ProfileStore::getInstance()->getProfile (entry) : nullptr; } // toSave has to be a complete procparams @@ -559,7 +559,7 @@ void ProfilePanel::paste_clicked (GdkEventButton* event) const ProfileStoreEntry* entry = profiles->getSelectedEntry(); if (entry) { - const PartialProfile* partProfile = profileStore.getProfile (entry); + const PartialProfile* partProfile = ProfileStore::getInstance()->getProfile (entry); *custom->pparams = *partProfile->pparams; } } @@ -576,7 +576,7 @@ void ProfilePanel::paste_clicked (GdkEventButton* event) const ProfileStoreEntry* entry = profiles->getSelectedEntry(); if (entry) { - const PartialProfile* partProfile = profileStore.getProfile (entry); + const PartialProfile* partProfile = ProfileStore::getInstance()->getProfile (entry); *custom->pparams = *partProfile->pparams; } } @@ -660,7 +660,7 @@ void ProfilePanel::selection_changed () currRow = profiles->get_active(); } - const PartialProfile* s = profileStore.getProfile (pse); + const PartialProfile* s = ProfileStore::getInstance()->getProfile (pse); if (s) { if (fillMode->get_active() && s->pedited) { @@ -745,12 +745,12 @@ void ProfilePanel::initProfile (const Glib::ustring& profileFullPath, ProcParams lasSavedEntry = getLastSavedRow(); } - if (!(pse = profileStore.findEntryFromFullPath(profileFullPath))) { + if (!(pse = ProfileStore::getInstance()->findEntryFromFullPath(profileFullPath))) { // entry not found, pse = the Internal ProfileStoreEntry - pse = profileStore.getInternalDefaultPSE(); + pse = ProfileStore::getInstance()->getInternalDefaultPSE(); } - defprofile = profileStore.getProfile (pse); + defprofile = ProfileStore::getInstance()->getProfile (pse); // selecting the "Internal" entry profiles->setInternalEntry (); diff --git a/rtgui/profilepanel.h b/rtgui/profilepanel.h index 8293c1803..72c9db36b 100644 --- a/rtgui/profilepanel.h +++ b/rtgui/profilepanel.h @@ -24,9 +24,9 @@ #include "../rtengine/rtengine.h" #include "pparamschangelistener.h" #include "profilechangelistener.h" -#include "profilestore.h" #include "partialpastedlg.h" #include "guiutils.h" +#include "profilestorecombobox.h" #include "rtimage.h" class ProfilePanel : public Gtk::Grid, public PParamsChangeListener, public ProfileStoreListener diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc deleted file mode 100644 index d7bf18ab5..000000000 --- a/rtgui/profilestore.cc +++ /dev/null @@ -1,862 +0,0 @@ -/* - * This file is part of RawTherapee. - * - * Copyright (c) 2004-2010 Gabor Horvath - * - * 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 "profilestore.h" -#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), dynamicRules(new std::vector()) -{ - internalDefaultProfile = new AutoPartialProfile(); - internalDefaultProfile->set(true); -} - -bool ProfileStore::init () -{ - if (storeState == STORESTATE_DELETED) { - return false; - } - - if (storeState == STORESTATE_NOTINITIALIZED) { - storeState = STORESTATE_BEINGINITIALIZED; - parseMutex = new MyMutex(); - _parseProfiles (); - loadDynamicProfileRules(*dynamicRules); - storeState = STORESTATE_INITIALIZED; - } - - return true; -} - -ProfileStore::~ProfileStore () -{ - - if (storeState == STORESTATE_NOTINITIALIZED) { - return; - } - - // This lock prevent object's suppression while scanning the directories - storeState = STORESTATE_DELETED; - MyMutex::MyLock lock(*parseMutex); - - clearProfileList (); - partProfiles.clear (); - clearFileList(); - delete internalDefaultProfile; - delete internalDefaultEntry; - delete internalDynamicEntry; - lock.release(); - delete parseMutex; - parseMutex = nullptr; -} - -/* - * Public method to parse the profiles' directories - * Since there's a race condition in the multithreaded environment on this object, - * parseProfiles may need to ask for initialization of this object, and then will - * ask a mutex lock on it, has it been initialized by this call or not - * - * This method will scan the directory tree again and update the profile list. When finished, - * the listeners will be called in order to update with the new list - */ -void ProfileStore::parseProfiles () -{ - - if (!init()) - // I don't even know if this situation can occur - { - return; - } - - for (auto listener : listeners) { - listener->storeCurrentValue(); - } - - { - MyMutex::MyLock lock(*parseMutex); - - _parseProfiles (); - } - - for (auto listener : listeners) { - listener->updateProfileList(); - listener->restoreValue(); - } -} - -void ProfileStore::_parseProfiles () -{ - - // Acquire the GUI, since the tree model can interact with combobox - GThreadLock threadLock; - - // clear loaded profiles - folders.clear(); - clearFileList(); - clearProfileList (); - - folders.push_back("<<< ROOT >>>"); // Fake path, so parentFolderId == 0 will be used to attach a ProfileStoreEntry to the root container, not sub-menu - - Glib::ustring p1 = options.getUserProfilePath(); - Glib::ustring p2 = options.getGlobalProfilePath(); - bool displayLevel0 = options.useBundledProfiles && !p1.empty() && !p2.empty() && p1 != p2; - - Glib::ustring virtualPath("${U}"); - Glib::ustring currDir("${U}"); - parseDir (p1, virtualPath, currDir, 0, 0, displayLevel0); - - if (displayLevel0) { - virtualPath = "${G}"; - currDir = "${G}"; - parseDir (p2, virtualPath, currDir, 0, 0, displayLevel0); - } - - // sort profiles - std::sort(entries.begin(), entries.end(), SortProfiles() ); - - // entries and partProfiles are empty, but the entry and profiles already exist (they have survived to clearFileList and clearProfileList) - if (!internalDefaultEntry) { - internalDefaultEntry = new ProfileStoreEntry(Glib::ustring("(") + M("PROFILEPANEL_PINTERNAL") + Glib::ustring(")"), PSET_FILE, 0, 0); - } - - 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) { - options.setDefProfRawMissing(true); - - if (options.rtSettings.verbose) { - printf("WARNING: Default profile \"%s\" for raw images not found!\n", options.defProfRaw.c_str()); - } - } - - if (findEntryFromFullPathU(options.defProfImg) == nullptr) { - options.setDefProfImgMissing(true); - - if (options.rtSettings.verbose) { - printf("WARNING: Default profile \"%s\" for standard images not found!\n", options.defProfImg.c_str()); - } - } -} - -/// @return Returns true if some files has been found (directories are ignored) -bool ProfileStore::parseDir (Glib::ustring& realPath, Glib::ustring& virtualPath, Glib::ustring& currDir, unsigned int parentId, unsigned char level, bool displayLevel0) -{ - - bool fileFound = false; - - // reload the available profiles from the profile dir - if (!realPath.empty() && Glib::file_test(realPath, Glib::FILE_TEST_EXISTS) && Glib::file_test (realPath, Glib::FILE_TEST_IS_DIR)) { - unsigned int folder = 0; // folder's own Id - - // add this entry to the folder list - folders.push_back(virtualPath); - folder = (unsigned int)(folders.size()) - 1; - - if (level > 0 || displayLevel0) { - // replace the virtual folder name by a localized text - if (currDir == "${U}") { - currDir = M("PROFILEPANEL_MYPROFILES"); - } else if (currDir == "${G}") { - currDir = M("PROFILEPANEL_GLOBALPROFILES"); - } - - // add this localized text to the file list - entries.push_back( new ProfileStoreEntry(currDir, PSET_FOLDER, parentId, folder) ); - } - - // walking through the directory - Glib::Dir* dir = nullptr; - dir = new Glib::Dir (realPath); - - for (Glib::DirIterator i = dir->begin(); i != dir->end(); ++i) { - currDir = *i; - - if (currDir == "." || currDir == "..") { - continue; - } - - Glib::ustring fname = Glib::build_filename(realPath, currDir); - - if (Glib::file_test (fname, Glib::FILE_TEST_IS_DIR)) { - Glib::ustring vp(Glib::build_filename(virtualPath, currDir)); - Glib::ustring rp(Glib::build_filename(realPath, currDir)); - fileFound = parseDir (rp, vp, currDir, folder, level + 1, 0); - } else { - size_t lastdot = currDir.find_last_of ('.'); - - if (lastdot != Glib::ustring::npos && lastdot == currDir.length() - 4 && currDir.substr(lastdot).casefold() == paramFileExtension) { - // file found - if( options.rtSettings.verbose ) { - printf ("Processing file %s...", fname.c_str()); - } - - Glib::ustring name = currDir.substr(0, lastdot); - - // create the partial profile - AutoPartialProfile *pProf = new AutoPartialProfile(); - int res = pProf->load (fname); - - if (!res && pProf->pparams->ppVersion >= 220) { - fileFound = true; - - if( options.rtSettings.verbose ) { - printf ("OK\n"); - } - - // adding this file to the list - ProfileStoreEntry* filePSE = new ProfileStoreEntry(name, PSET_FILE, folder, 0); - entries.push_back(filePSE); - - // map the partial profile - partProfiles[filePSE] = pProf; - //partProfiles.insert( std::pair (filePSE, pProf) ); - } else if( options.rtSettings.verbose ) { - printf ("failed!\n"); - } - } - } - } - - delete dir; - } - - if (!fileFound && (level > 0 || displayLevel0)) { - // no files found in this level, we delete the subdirectory entry - folders.pop_back(); - entries.pop_back(); - } - - return fileFound; -} - -int ProfileStore::findFolderId(const Glib::ustring &path) -{ - for (std::vector::iterator i = folders.begin(); i != folders.end(); ++i) { - if (*i == path) { - return i - folders.begin(); - } - } - - return -1; -} - -/** @brief Return the ProfileStoreEntry object that match the given file and path - * - * @param fullPath Path of the file; the filename may end by the standard extension, - * but have to begin with a virtual location ( ${G} or ${U} ) - * Will return null on invalid path or if the entry can't be found - */ -const ProfileStoreEntry* ProfileStore::findEntryFromFullPathU(Glib::ustring path) -{ - if (path.empty()) { - return nullptr; - } - - if (path == DEFPROFILE_INTERNAL || path == DEFPROFILE_DYNAMIC) { - return internalDefaultEntry; - } - - // consistently apply casefold() to make sure dot position is correct - const Glib::ustring casefolded_path = path.casefold(); - const Glib::ustring::size_type lastdot_pos = casefolded_path.find_last_of('.'); - - if ( - lastdot_pos != Glib::ustring::npos - && lastdot_pos <= casefolded_path.size() - 4 - && !casefolded_path.compare(lastdot_pos, 4, paramFileExtension) - ) { - // removing the extension - // now use dot position without casefold() - path = path.substr(0, path.find_last_of('.')); - } - - // dir separator may come from options file and may be \ or /, we convert them to G_DIR_SEPARATOR_S - if (path.size() > 4 && (path[4] == '/' || path[4] == '\\')) { - path = path.substr(0, 4) + G_DIR_SEPARATOR_S + path.substr(5); - } - - // removing the filename - Glib::ustring fName = Glib::path_get_basename(path); - - if (!fName.empty()) { - path = path.substr(0, path.length() - fName.length()); - } else { - // path is malformed, returning NULL; - return nullptr; - } - - path = Glib::path_get_dirname(path); - - // 1. find the path in the folder list - int parentFolderId = findFolderId(path); - - if (parentFolderId == -1) { - return nullptr; - } - - // 2. find the entry that match the given filename and parentFolderId - for (auto entry : entries) { - if (entry->parentFolderId == parentFolderId && entry->label == fName) { - return entry; - } - } - - return nullptr; -} - -/** Protected version of findEntryFromFullPathU */ -const ProfileStoreEntry* ProfileStore::findEntryFromFullPath(Glib::ustring path) -{ - MyMutex::MyLock lock(*parseMutex); - return findEntryFromFullPathU(path); -} - -const PartialProfile* ProfileStore::getProfile (Glib::ustring path) -{ - - if (!init()) - // I don't even know if this situation can occur - { - return nullptr; - } - - const ProfileStoreEntry *pse = findEntryFromFullPath(path); - - if (!pse) { - return nullptr; - } - - return getProfile(pse); -} - -const PartialProfile* ProfileStore::getProfile (const ProfileStoreEntry* entry) -{ - - if (!init()) - // I don't even know if this situation can occur - { - return nullptr; - } - - MyMutex::MyLock lock(*parseMutex); - - if (entry == internalDefaultEntry) { - return internalDefaultProfile; - } - - std::map::iterator iter = partProfiles.find(entry); - - if (iter != partProfiles.end()) { - return iter->second; - } else { - // This shouldn't happen! -#ifndef NDEBUG - printf("WARNING! Profile not found!\n"); -#endif - return nullptr; - } -} - -/** @brief Get a pointer to the profile's vector list - * - * This method grants you unique access to the vector list through Mutex locking. - * When you're done with the file list, you MUST call the releaseFileList method to release the lock. - */ -const std::vector* ProfileStore::getFileList () -{ - /*if (!init()) { - // I don't even know if this situation can occur - return NULL; - }*/ - - parseMutex->lock(); - - return &entries; -} - -void ProfileStore::releaseFileList() -{ - parseMutex->unlock(); -} - -/* - * Send back a pointer to the default procparams for raw or standard images. - * If the profile doesn't already exist in the profile list, - * it will add it with default internal values, so this method never fails - */ -const ProcParams* ProfileStore::getDefaultProcParams (bool isRaw) -{ - - if (!init()) - // I don't even know if this situation can occur - { - return nullptr; - } - - //Note: the mutex is locked in getProfile, called below - - const PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg); - - if (!pProf) { - pProf = internalDefaultProfile; - } - - return pProf->pparams; -} - -/* - * Send back a pointer to the default partial profile for raw or standard images. - * If it doesn't already exist in the profile list, it will add it with default internal values, - * so this method will never fails - */ -const PartialProfile* ProfileStore::getDefaultPartialProfile (bool isRaw) -{ - - if (!init()) - // I don't even know if this situation can occur - { - return nullptr; - } - - //Note: the mutex is locked in getProfile, called below - - const PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg); - - if (!pProf) { - pProf = internalDefaultProfile; - } - - return pProf; -} - -const Glib::ustring ProfileStore::getPathFromId(int folderId) -{ - return folders.at(folderId); -} - - -void ProfileStore::clearFileList() -{ - for (auto entry : entries) { - if (entry != internalDefaultEntry) { - delete entry; - } - } - - entries.clear(); -} - -void ProfileStore::clearProfileList() -{ - for (auto partProfile : partProfiles) { - if (partProfile.second != internalDefaultProfile) { - delete partProfile.second; - } - } - - partProfiles.clear(); -} - -void ProfileStore::addListener(ProfileStoreListener *listener) -{ - listeners.push_back(listener); -} - -void ProfileStore::removeListener(ProfileStoreListener *listener) -{ - listeners.remove(listener); -} - -void ProfileStore::dumpFolderList() -{ - printf("Folder list:\n------------\n"); - - for (unsigned int i = 0; i < folders.size(); i++) { - printf(" #%3ud - %s\n", i, folders.at(i).c_str()); - } - - 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) {} - -void ProfileStoreEntry::setValues(Glib::ustring label, PSEType type, unsigned short parentFolder, unsigned short folder) -{ - this->label = label; - this->type = type; - parentFolderId = parentFolder; - folderId = folder; -} - -ProfileStoreLabel::ProfileStoreLabel(const ProfileStoreEntry *entry) : Gtk::Label(entry->label), entry(entry) -{ - set_alignment(0, 0.5); - set_ellipsize(Pango::ELLIPSIZE_END); - show(); -} - -ProfileStoreComboBox::ProfileStoreComboBox () -{ - updateProfileList(); - setPreferredWidth(50, 120); -} - -Glib::ustring ProfileStoreComboBox::getCurrentLabel() -{ - Glib::ustring currLabel; - Gtk::TreeModel::iterator currRow = get_active(); - - if (currRow) { - const ProfileStoreEntry *currEntry = (*currRow)[methodColumns.profileStoreEntry]; - return currEntry->label; - } - - return currLabel; -} - -const ProfileStoreEntry* ProfileStoreComboBox::getSelectedEntry() -{ - Gtk::TreeModel::iterator currRow_ = get_active(); - Gtk::TreeModel::Row currRow = *currRow_; - - if (currRow) { - return currRow[methodColumns.profileStoreEntry]; - } else { - return nullptr; - } -} - -/** @brief Recursive method to update the combobox entries */ -void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow, int parentFolderId, bool initial, const std::vector *entryList) -{ - for (auto entry : *entryList) { - if (entry->parentFolderId == parentFolderId) { // filtering the entry of the same folder - if (entry->type == PSET_FOLDER) { - Glib::ustring folderPath( profileStore.getPathFromId(entry->folderId) ); - - if (options.useBundledProfiles || ((folderPath != "${G}" ) && (folderPath != "${U}" ))) { - // creating the new submenu - Gtk::TreeModel::Row newSubMenu; - - if (initial) { - newSubMenu = *(refTreeModel->append()); - } else { - newSubMenu = *(refTreeModel->append(parentRow->children())); - } - - // 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] = "-"; - menuHeader[methodColumns.profileStoreEntry] = entry; -#endif - refreshProfileList_ (&newSubMenu, entry->folderId, false, entryList); - } else { - refreshProfileList_ (parentRow, entry->folderId, true, entryList); - } - } else { - Gtk::TreeModel::Row newItem; - - // creating a menu entry - if (initial) { - newItem = *(refTreeModel->append()); - } else { - newItem = *(refTreeModel->append(parentRow->children())); - } - - newItem[methodColumns.label] = entry->label; - newItem[methodColumns.profileStoreEntry] = entry; - } - } - } -} -/** @brief Get the ProfileStore's entry list and recreate the combobox entries. - * If you want to update the ProfileStore list itself (rescan the dir tree), use the "ProfileStore::parseProfiles" method instead - * - * This method has to be called by the ProfileStoreListener having a ProfileStoreComboBox. - */ -void ProfileStoreComboBox::updateProfileList () -{ - // clear items - clear(); - refTreeModel.clear(); - // Create the Tree model - refTreeModel = Gtk::TreeStore::create(methodColumns); - // Assign the model to the Combobox - set_model(refTreeModel); - - // this will lock the profilestore's entry list too - const std::vector *entryList = profileStore.getFileList(); - - //profileStore.dumpFolderList(); - refreshProfileList_ (NULL, entryList->at(0)->parentFolderId, true, entryList); - - if (entryList->at(0)->parentFolderId != 0) { - // special case for the Internal default entry - addRow(profileStore.getInternalDefaultPSE()); - } - - // releasing the profilestore's entry list mutex - profileStore.releaseFileList(); - - pack_start(methodColumns.label, false); - - Gtk::CellRendererText* cellRenderer = dynamic_cast(get_first_cell()); - cellRenderer->property_ellipsize() = Pango::ELLIPSIZE_MIDDLE; - cellRenderer->property_ellipsize_set() = true; -} - -Gtk::TreeIter ProfileStoreComboBox::findRowFromEntry_ (Gtk::TreeModel::Children childs, const ProfileStoreEntry *pse) -{ - Gtk::TreeModel::Row row; - Gtk::TreeIter rowInSubLevel; - - for(Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { - row = *iter; - // Hombre: is there a smarter way of knowing if this row has childs? - const ProfileStoreEntry *pse_ = row[methodColumns.profileStoreEntry]; - - if (pse_->type == PSET_FOLDER) { - rowInSubLevel = findRowFromEntry_ (iter->children(), pse); - - if (rowInSubLevel) { - // entry found - return rowInSubLevel; - } - } else if (pse_ == pse) { - // entry found - return iter; - } - } - - return childs.end(); -} - -Gtk::TreeIter ProfileStoreComboBox::findRowFromEntry (const ProfileStoreEntry *pse) -{ - Gtk::TreeModel::Children childs = refTreeModel->children(); - - if (pse) { - Gtk::TreeIter row = findRowFromEntry_ (childs, pse); - return row; - } - - return childs.end(); -} - -Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath_ (Gtk::TreeModel::Children childs, int parentFolderId, Glib::ustring &name) -{ - Gtk::TreeModel::Row row; - Gtk::TreeIter rowInSubLevel; - - for(Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { - row = *iter; - // Hombre: is there a smarter way of knowing if this row has childs? - const ProfileStoreEntry *pse = row[methodColumns.profileStoreEntry]; - - if (pse->type == PSET_FOLDER) { - rowInSubLevel = findRowFromFullPath_ (iter->children(), parentFolderId, name); - - if (rowInSubLevel) { - // entry found - return rowInSubLevel; - } - } else if (parentFolderId == pse->parentFolderId && name == pse->label) { - // entry found - return iter; - } - } - - return childs.end(); -} - -Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath (Glib::ustring path) -{ - Gtk::TreeIter row; - - if (path.empty()) { - return row; - } - - if (path == DEFPROFILE_INTERNAL) { - row = findRowFromEntry(profileStore.getInternalDefaultPSE()); - return row; - } - - if (path == DEFPROFILE_DYNAMIC) { - row = findRowFromEntry(profileStore.getInternalDynamicPSE()); - return row; - } - - // removing the filename - Glib::ustring fName = Glib::path_get_basename(path); - - if (!fName.empty()) { - path = path.substr(0, path.length() - fName.length()); - } else { - // path is malformed; - return row; - } - - path = Glib::path_get_dirname(path); - int parentFolderId = profileStore.findFolderId(path); - - // 1. find the path in the folder list - if (parentFolderId != -1) { - row = findRowFromFullPath_ (refTreeModel->children(), parentFolderId, fName); - } - - return row; -} - -/** @brief Get the absolute full path of the active row entry. - * @return The absolute full path of the active row entry, or the "Internal" keyword, - * or an empty string if the ComboBox is in an invalid state - */ -Glib::ustring ProfileStoreComboBox::getFullPathFromActiveRow() -{ - Glib::ustring path; - Gtk::TreeModel::iterator currRowI = get_active(); - - if (!currRowI) { - return path; - } - - Gtk::TreeModel::Row currRow = *currRowI; - - if (currRow) { - - const ProfileStoreEntry *currEntry = currRow[methodColumns.profileStoreEntry]; - - if (!currEntry) { - return path; - } - - if (currEntry == profileStore.getInternalDefaultPSE()) { - return Glib::ustring(DEFPROFILE_INTERNAL); - } - - if (currEntry == profileStore.getInternalDynamicPSE()) { - return Glib::ustring(DEFPROFILE_DYNAMIC); - } - - path = Glib::build_filename(profileStore.getPathFromId(currEntry->parentFolderId), currEntry->label); - } - - return path; -} - -bool ProfileStoreComboBox::setActiveRowFromFullPath(Glib::ustring path) -{ - if (!path.empty()) { - Gtk::TreeIter row = findRowFromFullPath(path); - - if (row) { - set_active(row); - return true; - } - } - - return false; -} - -bool ProfileStoreComboBox::setActiveRowFromEntry(const ProfileStoreEntry *pse) -{ - if (pse) { - Gtk::TreeIter row = findRowFromEntry(pse); - - if (row) { - set_active(row); - return true; - } - } - - return false; -} - -bool ProfileStoreComboBox::setInternalEntry () -{ - return setActiveRowFromEntry(profileStore.getInternalDefaultPSE()); -} - -/** @brief Get the row from the first level of the tree that match the provided name */ -Gtk::TreeIter ProfileStoreComboBox::getRowFromLabel(Glib::ustring name) -{ - Gtk::TreeIter row; - Gtk::TreeModel::Children childs = refTreeModel->children(); - - if (!name.empty()) { - Gtk::TreeModel::Row currRow; - - for(Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { - currRow = *iter; - const ProfileStoreEntry *pse = currRow[methodColumns.profileStoreEntry]; - - if (pse->label == name) { - return currRow; - } - } - } - - return childs.end(); - //return refTreeModel->get_iter(""); // is this fast? We want to send back a null, anvalid or end() iterator object here -} - -/** @brief Add a new row to the first level of the tree */ -Gtk::TreeIter ProfileStoreComboBox::addRow(const ProfileStoreEntry *profileStoreEntry) -{ - Gtk::TreeIter newEntry = refTreeModel->append(); - Gtk::TreeModel::Row row = *newEntry; - row[methodColumns.label] = profileStoreEntry->label; - row[methodColumns.profileStoreEntry] = profileStoreEntry; - return newEntry; -} - diff --git a/rtgui/profilestorecombobox.cc b/rtgui/profilestorecombobox.cc new file mode 100644 index 000000000..186309aa3 --- /dev/null +++ b/rtgui/profilestorecombobox.cc @@ -0,0 +1,358 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * 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 "../rtengine/profilestore.h" +#include "profilestorecombobox.h" + +#include "../rtengine/dynamicprofile.h" +#include "options.h" +#include "toolpanel.h" +#include "guiutils.h" + +using namespace rtengine; +using namespace rtengine::procparams; + +ProfileStoreLabel::ProfileStoreLabel (const ProfileStoreEntry *entry) : Gtk::Label (entry->label), entry (entry) +{ + set_alignment (0, 0.5); + set_ellipsize (Pango::ELLIPSIZE_END); + show(); +} + +ProfileStoreComboBox::ProfileStoreComboBox () +{ + updateProfileList(); + setPreferredWidth (50, 120); +} + +Glib::ustring ProfileStoreComboBox::getCurrentLabel() +{ + Glib::ustring currLabel; + Gtk::TreeModel::iterator currRow = get_active(); + + if (currRow) { + const ProfileStoreEntry *currEntry = (*currRow)[methodColumns.profileStoreEntry]; + return currEntry->label; + } + + return currLabel; +} + +const ProfileStoreEntry* ProfileStoreComboBox::getSelectedEntry() +{ + Gtk::TreeModel::iterator currRow_ = get_active(); + Gtk::TreeModel::Row currRow = *currRow_; + + if (currRow) { + return currRow[methodColumns.profileStoreEntry]; + } else { + return nullptr; + } +} + +/** @brief Recursive method to update the combobox entries */ +void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow, int parentFolderId, bool initial, const std::vector *entryList) +{ + for (auto entry : *entryList) { + if (entry->parentFolderId == parentFolderId) { // filtering the entry of the same folder + if (entry->type == PSET_FOLDER) { + Glib::ustring folderPath ( ProfileStore::getInstance()->getPathFromId (entry->folderId) ); + + if (options.useBundledProfiles || ((folderPath != "${G}" ) && (folderPath != "${U}" ))) { + // creating the new submenu + Gtk::TreeModel::Row newSubMenu; + + if (initial) { + newSubMenu = * (refTreeModel->append()); + } else { + newSubMenu = * (refTreeModel->append (parentRow->children())); + } + + // 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] = "-"; + menuHeader[methodColumns.profileStoreEntry] = entry; +#endif + refreshProfileList_ (&newSubMenu, entry->folderId, false, entryList); + } else { + refreshProfileList_ (parentRow, entry->folderId, true, entryList); + } + } else { + Gtk::TreeModel::Row newItem; + + // creating a menu entry + if (initial) { + newItem = * (refTreeModel->append()); + } else { + newItem = * (refTreeModel->append (parentRow->children())); + } + + newItem[methodColumns.label] = entry->label; + newItem[methodColumns.profileStoreEntry] = entry; + } + } + } +} +/** @brief Get the ProfileStore's entry list and recreate the combobox entries. + * If you want to update the ProfileStore list itself (rescan the dir tree), use the "ProfileStore::parseProfiles" method instead + * + * This method has to be called by the ProfileStoreListener having a ProfileStoreComboBox. + */ +void ProfileStoreComboBox::updateProfileList () +{ + // clear items + clear(); + refTreeModel.clear(); + // Create the Tree model + refTreeModel = Gtk::TreeStore::create (methodColumns); + // Assign the model to the Combobox + set_model (refTreeModel); + + // this will lock the profilestore's entry list too + const std::vector *entryList = ProfileStore::getInstance()->getFileList(); + + //profileStore.dumpFolderList(); + refreshProfileList_ (NULL, entryList->at (0)->parentFolderId, true, entryList); + + if (entryList->at (0)->parentFolderId != 0) { + // special case for the Internal default entry + addRow (ProfileStore::getInstance()->getInternalDefaultPSE()); + } + + // releasing the profilestore's entry list mutex + ProfileStore::getInstance()->releaseFileList(); + + pack_start (methodColumns.label, false); + + Gtk::CellRendererText* cellRenderer = dynamic_cast (get_first_cell()); + cellRenderer->property_ellipsize() = Pango::ELLIPSIZE_MIDDLE; + cellRenderer->property_ellipsize_set() = true; +} + +Gtk::TreeIter ProfileStoreComboBox::findRowFromEntry_ (Gtk::TreeModel::Children childs, const ProfileStoreEntry *pse) +{ + Gtk::TreeModel::Row row; + Gtk::TreeIter rowInSubLevel; + + for (Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { + row = *iter; + // Hombre: is there a smarter way of knowing if this row has childs? + const ProfileStoreEntry *pse_ = row[methodColumns.profileStoreEntry]; + + if (pse_->type == PSET_FOLDER) { + rowInSubLevel = findRowFromEntry_ (iter->children(), pse); + + if (rowInSubLevel) { + // entry found + return rowInSubLevel; + } + } else if (pse_ == pse) { + // entry found + return iter; + } + } + + return childs.end(); +} + +Gtk::TreeIter ProfileStoreComboBox::findRowFromEntry (const ProfileStoreEntry *pse) +{ + Gtk::TreeModel::Children childs = refTreeModel->children(); + + if (pse) { + Gtk::TreeIter row = findRowFromEntry_ (childs, pse); + return row; + } + + return childs.end(); +} + +Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath_ (Gtk::TreeModel::Children childs, int parentFolderId, Glib::ustring &name) +{ + Gtk::TreeModel::Row row; + Gtk::TreeIter rowInSubLevel; + + for (Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { + row = *iter; + // Hombre: is there a smarter way of knowing if this row has childs? + const ProfileStoreEntry *pse = row[methodColumns.profileStoreEntry]; + + if (pse->type == PSET_FOLDER) { + rowInSubLevel = findRowFromFullPath_ (iter->children(), parentFolderId, name); + + if (rowInSubLevel) { + // entry found + return rowInSubLevel; + } + } else if (parentFolderId == pse->parentFolderId && name == pse->label) { + // entry found + return iter; + } + } + + return childs.end(); +} + +Gtk::TreeIter ProfileStoreComboBox::findRowFromFullPath (Glib::ustring path) +{ + Gtk::TreeIter row; + ProfileStore *profileStore = ProfileStore::getInstance(); + + if (path.empty()) { + return row; + } + + if (path == DEFPROFILE_INTERNAL) { + row = findRowFromEntry (profileStore->getInternalDefaultPSE()); + return row; + } + + if (path == DEFPROFILE_DYNAMIC) { + row = findRowFromEntry (profileStore->getInternalDynamicPSE()); + return row; + } + + // removing the filename + Glib::ustring fName = Glib::path_get_basename (path); + + if (!fName.empty()) { + path = path.substr (0, path.length() - fName.length()); + } else { + // path is malformed; + return row; + } + + path = Glib::path_get_dirname (path); + int parentFolderId = profileStore->findFolderId (path); + + // 1. find the path in the folder list + if (parentFolderId != -1) { + row = findRowFromFullPath_ (refTreeModel->children(), parentFolderId, fName); + } + + return row; +} + +/** @brief Get the absolute full path of the active row entry. + * @return The absolute full path of the active row entry, or the "Internal" keyword, + * or an empty string if the ComboBox is in an invalid state + */ +Glib::ustring ProfileStoreComboBox::getFullPathFromActiveRow() +{ + Glib::ustring path; + Gtk::TreeModel::iterator currRowI = get_active(); + ProfileStore *profileStore = ProfileStore::getInstance(); + + if (!currRowI) { + return path; + } + + Gtk::TreeModel::Row currRow = *currRowI; + + if (currRow) { + + const ProfileStoreEntry *currEntry = currRow[methodColumns.profileStoreEntry]; + + if (!currEntry) { + return path; + } + + if (currEntry == profileStore->getInternalDefaultPSE()) { + return Glib::ustring (DEFPROFILE_INTERNAL); + } + + if (currEntry == profileStore->getInternalDynamicPSE()) { + return Glib::ustring (DEFPROFILE_DYNAMIC); + } + + path = Glib::build_filename (profileStore->getPathFromId (currEntry->parentFolderId), currEntry->label); + } + + return path; +} + +bool ProfileStoreComboBox::setActiveRowFromFullPath (Glib::ustring path) +{ + if (!path.empty()) { + Gtk::TreeIter row = findRowFromFullPath (path); + + if (row) { + set_active (row); + return true; + } + } + + return false; +} + +bool ProfileStoreComboBox::setActiveRowFromEntry (const ProfileStoreEntry *pse) +{ + if (pse) { + Gtk::TreeIter row = findRowFromEntry (pse); + + if (row) { + set_active (row); + return true; + } + } + + return false; +} + +bool ProfileStoreComboBox::setInternalEntry () +{ + return setActiveRowFromEntry (ProfileStore::getInstance()->getInternalDefaultPSE()); +} + +/** @brief Get the row from the first level of the tree that match the provided name */ +Gtk::TreeIter ProfileStoreComboBox::getRowFromLabel (Glib::ustring name) +{ + Gtk::TreeIter row; + Gtk::TreeModel::Children childs = refTreeModel->children(); + + if (!name.empty()) { + Gtk::TreeModel::Row currRow; + + for (Gtk::TreeModel::Children::iterator iter = childs.begin(); iter != childs.end(); ++iter) { + currRow = *iter; + const ProfileStoreEntry *pse = currRow[methodColumns.profileStoreEntry]; + + if (pse->label == name) { + return currRow; + } + } + } + + return childs.end(); + //return refTreeModel->get_iter(""); // is this fast? We want to send back a null, anvalid or end() iterator object here +} + +/** @brief Add a new row to the first level of the tree */ +Gtk::TreeIter ProfileStoreComboBox::addRow (const ProfileStoreEntry *profileStoreEntry) +{ + Gtk::TreeIter newEntry = refTreeModel->append(); + Gtk::TreeModel::Row row = *newEntry; + row[methodColumns.label] = profileStoreEntry->label; + row[methodColumns.profileStoreEntry] = profileStoreEntry; + return newEntry; +} + diff --git a/rtgui/profilestorecombobox.h b/rtgui/profilestorecombobox.h new file mode 100644 index 000000000..111e767c2 --- /dev/null +++ b/rtgui/profilestorecombobox.h @@ -0,0 +1,95 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * 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 _PROFILESTORECOMBOBOX_ +#define _PROFILESTORECOMBOBOX_ + +#include +#include +#include + +#include "../rtengine/rtengine.h" +#include "../rtengine/noncopyable.h" +#include "../rtengine/profilestore.h" + +#include "threadutils.h" +#include "paramsedited.h" +#include "guiutils.h" + + +/** + * @brief subclass of Gtk::Label with extra fields for Combobox and Menu, to link with a ProfileStoreEntry + */ +class ProfileStoreLabel : public Gtk::Label +{ + +public: + const ProfileStoreEntry *entry; + +#ifndef NDEBUG + ProfileStoreLabel() : Gtk::Label ("*** error ***"), entry (nullptr) {} +#else + ProfileStoreLabel() : Gtk::Label (""), entry (NULL) {} +#endif + + /** @brief Create a new ProfileStoreLabel + * + * @param entry Pointer to the ProfileStoreEntry object, be it a directory or a file + */ + explicit ProfileStoreLabel (const ProfileStoreEntry *entry); + ProfileStoreLabel (const ProfileStoreLabel &other); +}; + +class ProfileStoreComboBox : public MyComboBox +{ + +protected: + class MethodColumns : public Gtk::TreeModel::ColumnRecord + { + public: + Gtk::TreeModelColumn label; + Gtk::TreeModelColumn profileStoreEntry; + MethodColumns() + { + add (label); + add (profileStoreEntry); + } + }; + + Glib::RefPtr refTreeModel; + MethodColumns methodColumns; + void refreshProfileList_ (Gtk::TreeModel::Row *parentRow, int parentFolderId, bool initial, const std::vector *entryList); + Gtk::TreeIter findRowFromEntry_ (Gtk::TreeModel::Children childs, const ProfileStoreEntry *pse); + Gtk::TreeIter findRowFromFullPath_ (Gtk::TreeModel::Children childs, int parentFolderId, Glib::ustring &name); + +public: + ProfileStoreComboBox(); + void updateProfileList(); + Glib::ustring getCurrentLabel(); + const ProfileStoreEntry* getSelectedEntry(); + Gtk::TreeIter findRowFromEntry (const ProfileStoreEntry *pse); + Gtk::TreeIter findRowFromFullPath (Glib::ustring path); + Glib::ustring getFullPathFromActiveRow (); + bool setActiveRowFromFullPath (Glib::ustring oldPath); + bool setActiveRowFromEntry (const ProfileStoreEntry *pse); + bool setInternalEntry (); + Gtk::TreeIter getRowFromLabel (Glib::ustring name); + Gtk::TreeIter addRow (const ProfileStoreEntry *profileStoreEntry); +}; + +#endif diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index cd33f393d..bbc3dc01a 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -26,11 +26,13 @@ #include #include "../rtengine/imagedata.h" #include + +#include "../rtengine/dynamicprofile.h" #include "guiutils.h" -#include "profilestore.h" #include "batchqueue.h" #include "extprog.h" -#include "dynamicprofile.h" +#include "profilestorecombobox.h" +#include "md5helper.h" using namespace rtengine::procparams; @@ -177,7 +179,7 @@ const ProcParams& Thumbnail::getProcParamsU () if (pparamsValid) { return pparams; } else { - pparams = *(profileStore.getDefaultProcParams (getType() == FT_Raw)); + pparams = *(ProfileStore::getInstance()->getDefaultProcParams (getType() == FT_Raw)); if (pparams.wb.method == "Camera") { double ct; @@ -231,7 +233,7 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu } else { imageMetaData = rtengine::ImageMetaData::fromFile (fname, nullptr); } - PartialProfile *pp = loadDynamicProfile(imageMetaData); + PartialProfile *pp = ProfileStore::getInstance()->loadDynamicProfile(imageMetaData); int err = pp->pparams->save(outFName); pp->deleteInstance(); delete pp; @@ -239,7 +241,7 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu loadProcParams(); } } else if (create && defProf != DEFPROFILE_DYNAMIC) { - const PartialProfile *p = profileStore.getProfile(defProf); + const PartialProfile *p = ProfileStore::getInstance()->getProfile(defProf); if (p && !p->pparams->save(outFName)) { loadProcParams(); } @@ -315,7 +317,7 @@ void Thumbnail::loadProcParams () pparamsValid = false; pparams.setDefaults(); - const PartialProfile *defaultPP = profileStore.getDefaultPartialProfile(getType() == FT_Raw); + const PartialProfile *defaultPP = ProfileStore::getInstance()->getDefaultPartialProfile(getType() == FT_Raw); defaultPP->applyTo(&pparams); if (options.paramsLoadLocation == PLL_Input) { @@ -929,7 +931,7 @@ void Thumbnail::setFileName (const Glib::ustring &fn) { fname = fn; - cfs.md5 = cachemgr->getMD5 (fname); + cfs.md5 = ::getMD5 (fname); } void Thumbnail::addThumbnailListener (ThumbnailListener* tnl)