diff --git a/rtdata/languages/Francais b/rtdata/languages/Francais
index d24e75205..0679eb280 100644
--- a/rtdata/languages/Francais
+++ b/rtdata/languages/Francais
@@ -537,6 +537,8 @@ NAVIGATOR_S_VALUE;S = %1
NAVIGATOR_V_NA;V = n/d
NAVIGATOR_V_VALUE;V = %1
NAVIGATOR_XY_NA;x = n/d, y = n/d
+OPTIONS_DEFRAW_MISSING;Le profil par défaut pour les images Raw n'a pas été trouvé ou n'a pas été réglé.\n\nVérifiez également le dossier de vos profils, il peut être manquant ou endommagé\n\nLes valeurs internes pas défaut seront utilisées.
+OPTIONS_DEFIMG_MISSING;Le profil par défaut pour les images standards n'a pas été trouvé ou n'a pas été réglé.\n\nVérifiez également le dossier de vos profils, il peut être manquant ou endommagé\n\nLes valeurs internes pas défaut seront utilisées.
PARTIALPASTE_BASICGROUP;Réglages de base
PARTIALPASTE_CACORRECTION;Aberration chromatique
PARTIALPASTE_CHANNELMIXER;Mixage des canaux
diff --git a/rtdata/languages/default b/rtdata/languages/default
index 99a1e9929..8b137d04f 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -542,6 +542,8 @@ NAVIGATOR_S_VALUE;S = %1
NAVIGATOR_V_NA;V = n/a
NAVIGATOR_V_VALUE;V = %1
NAVIGATOR_XY_NA;x = n/a, y = n/a
+OPTIONS_DEFRAW_MISSING;The default profile for raw images could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\nDefault internal values will be used.
+OPTIONS_DEFIMG_MISSING;The default profile for standard images could not be found or is not set.\n\nPlease check your profiles' directory, it may be missing or damaged.\n\nDefault internal values will be used.
PARTIALPASTE_BASICGROUP;Basic Settings
PARTIALPASTE_CACORRECTION;C/A Correction
PARTIALPASTE_CHANNELMIXER;Channel mixer
diff --git a/rtdata/options/options.lin b/rtdata/options/options.lin
index a129fc255..3553b0baa 100644
--- a/rtdata/options/options.lin
+++ b/rtdata/options/options.lin
@@ -11,6 +11,7 @@
MultiUser=true
[File Browser]
+# Image filename extensions to be looked for, and their corresponding search state (0/1 -> skip/include)
ParseExtensions=arw;cr2;crf;crw;dng;jpg;kdc;mef;mrw;nef;nrw;orf;pef;png;raf;raw;rw2;rwz;sr2;srw;tif;tiff;
ParseExtensionsEnabled=1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
@@ -21,3 +22,17 @@ PathTemplate=%p1/converted/%f
# if this is set to a path of a custom program, it will receive the EXIFs as parameters and must generate a PP3 preset file for the given RAW/JPG
# Parameters:
CustomProfileBuilder=
+
+# Set here an absolute or relative path (to the rawtherapee.exe file) to the directory containing your own profiles.
+# If MultiUser=true, each user will have their own "options" file, and can set a common or different absolu path
+#Directory=profiles
+
+# Uncomment and set UseBundledProfiles to false if you don't want to be polluted by RawTherapee's bundled profiles
+# Warning: if you don't set RawDefault and ImgDefault to one of your own profile, Internal values will be used instead
+#UseBundledProfiles=true
+
+# Default profile name (without extension) to use for raw images
+#RawDefault=Default
+
+# Default profile name (without extension) to use for standard (8bits) images
+#ImgDefault=Neutral
diff --git a/rtdata/options/options.osx b/rtdata/options/options.osx
index a129fc255..3553b0baa 100644
--- a/rtdata/options/options.osx
+++ b/rtdata/options/options.osx
@@ -11,6 +11,7 @@
MultiUser=true
[File Browser]
+# Image filename extensions to be looked for, and their corresponding search state (0/1 -> skip/include)
ParseExtensions=arw;cr2;crf;crw;dng;jpg;kdc;mef;mrw;nef;nrw;orf;pef;png;raf;raw;rw2;rwz;sr2;srw;tif;tiff;
ParseExtensionsEnabled=1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
@@ -21,3 +22,17 @@ PathTemplate=%p1/converted/%f
# if this is set to a path of a custom program, it will receive the EXIFs as parameters and must generate a PP3 preset file for the given RAW/JPG
# Parameters:
CustomProfileBuilder=
+
+# Set here an absolute or relative path (to the rawtherapee.exe file) to the directory containing your own profiles.
+# If MultiUser=true, each user will have their own "options" file, and can set a common or different absolu path
+#Directory=profiles
+
+# Uncomment and set UseBundledProfiles to false if you don't want to be polluted by RawTherapee's bundled profiles
+# Warning: if you don't set RawDefault and ImgDefault to one of your own profile, Internal values will be used instead
+#UseBundledProfiles=true
+
+# Default profile name (without extension) to use for raw images
+#RawDefault=Default
+
+# Default profile name (without extension) to use for standard (8bits) images
+#ImgDefault=Neutral
diff --git a/rtdata/options/options.win b/rtdata/options/options.win
index 107bd3ce1..0b72287c6 100644
--- a/rtdata/options/options.win
+++ b/rtdata/options/options.win
@@ -13,6 +13,7 @@ MultiUser=true
UseSystemTheme=false
[File Browser]
+# Image filename extensions to be looked for, and their corresponding search state (0/1 -> skip/include)
ParseExtensions=arw;cr2;crf;crw;dng;jpg;kdc;mef;mrw;nef;nrw;orf;pef;png;raf;raw;rw2;rwz;sr2;srw;tif;tiff;
ParseExtensionsEnabled=1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;
@@ -23,3 +24,17 @@ PathTemplate=%p1/converted/%f
# if this is set to a path of a custom program, it will receive the EXIFs as parameters and must generate a PP3 preset file for the given RAW/JPG
# Parameters:
CustomProfileBuilder=
+
+# Set here an absolute or relative path (to the rawtherapee.exe file) to the directory containing your own profiles.
+# If MultiUser=true, each user will have their own "options" file, and can set a common or different absolu path
+#Directory=profiles
+
+# Uncomment and set UseBundledProfiles to false if you don't want to be polluted by RawTherapee's bundled profiles
+# Warning: if you don't set RawDefault and ImgDefault to one of your own profile, Internal values will be used instead
+#UseBundledProfiles=true
+
+# Default profile name (without extension) to use for raw images
+#RawDefault=Default
+
+# Default profile name (without extension) to use for standard (8bits) images
+#ImgDefault=Neutral
diff --git a/rtengine/init.cc b/rtengine/init.cc
index ced941921..36cabc6e1 100644
--- a/rtengine/init.cc
+++ b/rtengine/init.cc
@@ -25,6 +25,7 @@
#include "dfmanager.h"
#include "ffmanager.h"
#include "rtthumbnail.h"
+#include "../rtgui/profilestore.h"
namespace rtengine {
@@ -40,6 +41,7 @@ int init (const Settings* s, Glib::ustring baseDir) {
dcpStore->init (baseDir + "/dcpprofiles");
+ profileStore.init ();
ProcParams::init ();
CurveFactory::init ();
ImProcFunctions::initMunsell();
diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc
index 9a527c0c3..12ffc4a18 100644
--- a/rtengine/procparams.cc
+++ b/rtengine/procparams.cc
@@ -687,6 +687,9 @@ int ProcParams::write (Glib::ustring &fname, Glib::ustring &content) const {
int ProcParams::load (Glib::ustring fname, ParamsEdited* pedited) {
+ if (fname.empty())
+ return 1;
+
SafeKeyFile keyFile;
try {
//setDefaults ();
diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h
index 50f7a0038..3e00c4419 100644
--- a/rtengine/rtengine.h
+++ b/rtengine/rtengine.h
@@ -395,7 +395,7 @@ namespace rtengine {
* @param tunnelMetaData tunnels IPTC and XMP to output without change */
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData);
-
+
extern Glib::Mutex* lcmsMutex;
}
diff --git a/rtgui/filebrowser.cc b/rtgui/filebrowser.cc
index af3fd6062..cc9c22f98 100644
--- a/rtgui/filebrowser.cc
+++ b/rtgui/filebrowser.cc
@@ -40,8 +40,6 @@ FileBrowser::FileBrowser ()
fbih->destroyed = false;
fbih->pending = 0;
- // profileStore.parseProfiles ();
-
signal_style_changed().connect( sigc::mem_fun(*this, &FileBrowser::styleChanged) );
int p = 0;
diff --git a/rtgui/main.cc b/rtgui/main.cc
index 174a9aa3d..d65567df7 100644
--- a/rtgui/main.cc
+++ b/rtgui/main.cc
@@ -159,7 +159,20 @@ int main(int argc, char **argv)
RTWindow *rtWindow = new class RTWindow();
gdk_threads_enter ();
+
+ // alerting users if the default raw and image profiles are missing
+ if (options.is_defProfRawMissing()) {
+ Gtk::MessageDialog msgd (Glib::ustring::compose(M("OPTIONS_DEFRAW_MISSING"), options.defProfRaw), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
+ msgd.run ();
+ }
+ if (options.is_defProfImgMissing()) {
+ Gtk::MessageDialog msgd (Glib::ustring::compose(M("OPTIONS_DEFIMG_MISSING"), options.defProfImg), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
+ msgd.run ();
+ }
+
+ // opening the main window
m.run(*rtWindow);
+
gdk_threads_leave ();
delete rtWindow;
rtengine::cleanup();
@@ -348,16 +361,18 @@ int processLineParams( int argc, char **argv )
if (useDefault) {
rawParams = new rtengine::procparams::PartialProfile(true);
- if (rawParams->load(options.profilePath+"/" + options.defProfRaw + paramFileExtension)) {
- std::cerr << "Error: default Raw procparams file \""<< (options.profilePath+"/" + options.defProfRaw + paramFileExtension) << "\" not found" << std::endl;
+ Glib::ustring profPath = options.findProfilePath(options.defProfRaw);
+ if (options.is_defProfRawMissing() || profPath.empty() || rawParams->load(Glib::build_filename(profPath, options.defProfRaw + paramFileExtension))) {
+ std::cerr << "Error: default Raw procparams file not found" << std::endl;
rawParams->deleteInstance();
delete rawParams;
deleteProcParams(processingParams);
return -3;
}
imgParams = new rtengine::procparams::PartialProfile(true);
- if (imgParams->load(options.profilePath+"/" + options.defProfImg + paramFileExtension)) {
- std::cerr << "Error: default Image procparams file \""<< (options.profilePath+"/" + options.defProfImg + paramFileExtension) << "\" not found" << std::endl;
+ profPath = options.findProfilePath(options.defProfImg);
+ if (options.is_defProfImgMissing() || profPath.empty() || imgParams->load(Glib::build_filename(profPath, options.defProfImg + paramFileExtension))) {
+ std::cerr << "Error: default Image procparams file not found" << std::endl;
imgParams->deleteInstance();
delete imgParams;
rawParams->deleteInstance();
diff --git a/rtgui/options.cc b/rtgui/options.cc
index 5f8c51eb3..e5a791b1e 100644
--- a/rtgui/options.cc
+++ b/rtgui/options.cc
@@ -42,11 +42,130 @@ Glib::ustring paramFileExtension = ".pp3";
Options::Options () {
+ defProfRawMissing = false;
+ defProfImgMissing = false;
setDefaults ();
}
const char *DefaultLanguage = "English (US)";
+inline bool Options::checkProfilePath(Glib::ustring &path) {
+ if (path.empty())
+ return false;
+
+ Glib::ustring p = getUserProfilePath();
+ if (!p.empty() && safe_file_test (path+paramFileExtension, Glib::FILE_TEST_EXISTS))
+ return true;
+
+ p = getGlobalProfilePath();
+ if (!p.empty() && safe_file_test (path+paramFileExtension, Glib::FILE_TEST_EXISTS))
+ return true;
+ else
+ return false;
+}
+
+bool Options::checkDirPath(Glib::ustring &path, Glib::ustring errString) {
+ if (safe_file_test (path, Glib::FILE_TEST_EXISTS) && safe_file_test (path, Glib::FILE_TEST_IS_DIR))
+ return true;
+ else {
+ if (!errString.empty()) printf("%s\n", errString.c_str());
+ return false;
+ }
+}
+
+void Options::updatePaths() {
+
+ Glib::ustring tmpPath;
+
+ userProfilePath = "";
+ globalProfilePath = "";
+
+ if (Glib::path_is_absolute(profilePath)) {
+ // absolute path
+ if (!checkDirPath (profilePath, "")) {
+ int retVal = safe_g_mkdir_with_parents (profilePath, 511);
+ if (!retVal)
+ printf("Error: user's profiles' directory \"%s\" creation failed\n", profilePath.c_str());
+ }
+ if (checkDirPath (profilePath, "Error: the specified user's profiles' path doesn't point to a directory or doesn't exist!\n")) {
+ if (multiUser) {
+ userProfilePath = profilePath;
+ if (useBundledProfiles) {
+ tmpPath = Glib::build_filename(argv0, "profiles");
+ if(checkDirPath (tmpPath, "Error: the global's profiles' path doesn't point to a directory or doesn't exist!\n")) {
+ if (userProfilePath != tmpPath)
+ globalProfilePath = tmpPath;
+ }
+ }
+ }
+ else {
+ globalProfilePath = profilePath;
+ }
+ }
+ else {
+ tmpPath = Glib::build_filename(argv0, "profiles");
+ if(checkDirPath (tmpPath, "Error: the global's profiles' path doesn't point to a directory or doesn't exist!\n")) {
+ globalProfilePath = tmpPath;
+ }
+ }
+ }
+ else {
+ // relative paths
+ if (multiUser) {
+ tmpPath = Glib::build_filename(rtdir, profilePath);
+ if (!checkDirPath (tmpPath, "")) {
+ int retVal = safe_g_mkdir_with_parents (tmpPath, 511);
+ if (!retVal)
+ printf("Error: user's profiles' directory \"%s\" creation failed\n", tmpPath.c_str());
+ }
+ if(checkDirPath (tmpPath, "Error: the specified user's profiles' path doesn't point to a directory!\n")) {
+ userProfilePath = tmpPath;
+ }
+ if (useBundledProfiles) {
+ tmpPath = Glib::build_filename(argv0, "profiles");
+ if(checkDirPath (tmpPath, "Error: the specified user's profiles' path doesn't point to a directory or doesn't exist!\n")) {
+ globalProfilePath = tmpPath;
+ }
+ }
+ }
+ else {
+ // common directory
+ // directory name set in options is ignored, we use the default directory name
+ tmpPath = Glib::build_filename(argv0, "profiles");
+ if(checkDirPath (tmpPath, "Error: no global profiles' directory found!\n")) {
+ globalProfilePath = tmpPath;
+ }
+ }
+ }
+}
+
+Glib::ustring Options::getPreferredProfilePath() {
+ if (!userProfilePath.empty())
+ return userProfilePath;
+ else if (!globalProfilePath.empty())
+ return globalProfilePath;
+ else
+ return "";
+}
+
+Glib::ustring Options::findProfilePath(Glib::ustring &profName) {
+ if (profName.empty())
+ return "";
+
+ Glib::ustring p = getUserProfilePath();
+ Glib::ustring fullPath = Glib::build_filename(p, profName + paramFileExtension);
+ if (!p.empty() && safe_file_test (fullPath, Glib::FILE_TEST_EXISTS))
+ return p;
+
+ p = getGlobalProfilePath();
+ fullPath = Glib::build_filename(p, profName + paramFileExtension);
+ if (!p.empty() && safe_file_test (fullPath, Glib::FILE_TEST_EXISTS))
+ return p;
+ else
+ return "";
+
+}
+
void Options::setDefaults () {
font = "sans, 10";
@@ -73,13 +192,14 @@ void Options::setDefaults () {
savePathTemplate = "%p1/converted/%f";
savePathFolder = "";
saveUsePathTemplate = true;
- defProfRaw = "Default";
- defProfImg = "Neutral";
+ defProfRaw = DEFPROFILE_RAW;
+ defProfImg = DEFPROFILE_IMG;
dateFormat = "%y-%m-%d";
adjusterDelay = 0;
startupDir = STARTUPDIR_LAST; // was STARTUPDIR_HOME ; an empty startupPath is now correctly handled (open in the Home dir)
startupPath = "";
profilePath = "profiles";
+ useBundledProfiles = true;
loadSaveProfilePath = "";
dirBrowserWidth = 200;
dirBrowserHeight = 150;
@@ -372,10 +492,11 @@ if (keyFile.has_group ("Output")) {
}
if (keyFile.has_group ("Profiles")) {
- if (keyFile.has_key ("Profiles", "Directory")) profilePath = keyFile.get_string ("Profiles", "Directory");
- if (keyFile.has_key ("Profiles", "LoadSaveProfilePath")) loadSaveProfilePath = keyFile.get_string ("Profiles", "LoadSaveProfilePath");
- if (keyFile.has_key ("Profiles", "RawDefault")) defProfRaw = keyFile.get_string ("Profiles", "RawDefault");
- if (keyFile.has_key ("Profiles", "ImgDefault")) defProfImg = keyFile.get_string ("Profiles", "ImgDefault");
+ if (keyFile.has_key ("Profiles", "Directory")) profilePath = keyFile.get_string ("Profiles", "Directory");
+ if (keyFile.has_key ("Profiles", "UseBundledProfiles")) useBundledProfiles = keyFile.get_boolean ("Profiles", "UseBundledProfiles");
+ if (keyFile.has_key ("Profiles", "LoadSaveProfilePath")) loadSaveProfilePath = keyFile.get_string ("Profiles", "LoadSaveProfilePath");
+ if (keyFile.has_key ("Profiles", "RawDefault")) defProfRaw = keyFile.get_string ("Profiles", "RawDefault");
+ if (keyFile.has_key ("Profiles", "ImgDefault")) defProfImg = keyFile.get_string ("Profiles", "ImgDefault");
if (keyFile.has_key ("Profiles", "SaveParamsWithFile")) saveParamsFile = keyFile.get_boolean ("Profiles", "SaveParamsWithFile");
if (keyFile.has_key ("Profiles", "SaveParamsToCache")) saveParamsCache = keyFile.get_boolean ("Profiles", "SaveParamsToCache");
if (keyFile.has_key ("Profiles", "LoadParamsFromLocation")) paramsLoadLocation = (PPLoadLocation)keyFile.get_integer ("Profiles", "LoadParamsFromLocation");
@@ -384,7 +505,7 @@ if (keyFile.has_group ("Profiles")) {
if (keyFile.has_group ("File Browser")) {
if (keyFile.has_key ("File Browser", "ThumbnailSize")) thumbSize = keyFile.get_integer ("File Browser", "ThumbnailSize");
- if (keyFile.has_key ("File Browser", "ThumbnailSizeTab")) thumbSizeTab = keyFile.get_integer ("File Browser", "ThumbnailSizeTab");
+ if (keyFile.has_key ("File Browser", "ThumbnailSizeTab")) thumbSizeTab = keyFile.get_integer ("File Browser", "ThumbnailSizeTab");
if (keyFile.has_key ("File Browser", "BrowseOnlyRaw")) fbOnlyRaw = keyFile.get_boolean ("File Browser", "BrowseOnlyRaw");
if (keyFile.has_key ("File Browser", "BrowserShowsDate")) fbShowDateTime = keyFile.get_boolean ("File Browser", "BrowserShowsDate");
if (keyFile.has_key ("File Browser", "BrowserShowsExif")) fbShowBasicExif = keyFile.get_boolean ("File Browser", "BrowserShowsExif");
@@ -627,6 +748,7 @@ int Options::saveToFile (Glib::ustring fname) {
keyFile.set_boolean ("Output", "TunnelMetaData", tunnelMetaData);
keyFile.set_string ("Profiles", "Directory", profilePath);
+ keyFile.set_boolean ("Profiles", "UseBundledProfiles", useBundledProfiles);
keyFile.set_string ("Profiles", "LoadSaveProfilePath", loadSaveProfilePath);
keyFile.set_string ("Profiles", "RawDefault", defProfRaw);
keyFile.set_string ("Profiles", "ImgDefault", defProfImg);
@@ -744,7 +866,9 @@ int Options::saveToFile (Glib::ustring fname) {
}
}
+// User's settings directory, including images' profiles if used
Glib::ustring Options::rtdir;
+// User's cached datas' directory
Glib::ustring Options::cacheBaseDir;
void Options::load () {
@@ -779,15 +903,12 @@ void Options::load () {
// Check if RT is installed in Multi-User mode
if (options.multiUser) {
// Read the user option file (the one located somewhere in the user's home folder)
- // Those values supersets those of the global option file
+ // Those values supersets those of the global option file
int r = options.readFromFile (rtdir + "/options");
// If the local option file does not exist or is broken, and the local cache folder does not exist, recreate it
if (r && !safe_g_mkdir_with_parents (rtdir, 511)) {
- // Recreate the user's profile folder
- Glib::ustring profdir = rtdir + "/profiles";
- safe_g_mkdir_with_parents (profdir, 511);
// Save the option file
- options.saveToFile (rtdir + "/options");
+ options.saveToFile (rtdir + "/options");
}
// Modify the path of the cache folder to the user's personal folder
#ifdef WIN32
@@ -797,6 +918,38 @@ void Options::load () {
#endif
}
+ // Update profile's path and recreate it if necessary
+ options.updatePaths();
+
+ // Check default Raw and Img procparams existence
+ if (!options.defProfRaw.length())
+ options.defProfRaw = DEFPROFILE_INTERNAL;
+ else {
+ Glib::ustring tmpFName = options.findProfilePath(options.defProfRaw);
+ if (!tmpFName.empty()) {
+ if (options.rtSettings.verbose) printf("Raws' default profile \"%s\" found\n", options.defProfRaw.c_str());
+ }
+ else {
+ if (options.rtSettings.verbose) printf("Raws' default profile \"%s\" not found or not set -> using Internal values\n", options.defProfRaw.c_str());
+ options.defProfRaw = DEFPROFILE_INTERNAL;
+ options.defProfRawMissing = true;
+ }
+ }
+
+ if (!options.defProfImg.length())
+ options.defProfImg = DEFPROFILE_INTERNAL;
+ else {
+ Glib::ustring tmpFName = options.findProfilePath(options.defProfImg);
+ if (!tmpFName.empty()) {
+ if (options.rtSettings.verbose) printf("Images' default profile \"%s\" found\n", options.defProfImg.c_str());
+ }
+ else {
+ if (options.rtSettings.verbose) printf("Images' default profile \"%s\" not found or not set -> using Internal values\n", options.defProfImg.c_str());
+ options.defProfImg = DEFPROFILE_INTERNAL;
+ options.defProfImgMissing = true;
+ }
+ }
+
//We handle languages using a hierarchy of translations. The top of the hierarchy is default. This includes a default translation for all items
// (most likely using simple English). The next level is the language: for instance, English, French, Chinese, etc. This file should contain a
// generic translation for all items which differ from default. Finally there is the locale. This is region-specific items which differ from the
diff --git a/rtgui/options.h b/rtgui/options.h
index 5f34dea6f..c8b7a6831 100644
--- a/rtgui/options.h
+++ b/rtgui/options.h
@@ -27,6 +27,13 @@
#define STARTUPDIR_CUSTOM 2
#define STARTUPDIR_LAST 3
+// Default bundled profile name to use for Raw images
+#define DEFPROFILE_RAW "Default"
+// Default bundled profile name to use for Standard images
+#define DEFPROFILE_IMG "Neutral"
+// Profile name to use for internal values' profile
+#define DEFPROFILE_INTERNAL "Internal"
+
class SaveFormat {
public:
@@ -45,8 +52,15 @@ enum PPLoadLocation {PLL_Cache=0, PLL_Input=1};
class Options {
private:
+ bool defProfRawMissing;
+ bool defProfImgMissing;
+ Glib::ustring userProfilePath;
+ Glib::ustring globalProfilePath;
+ bool checkProfilePath(Glib::ustring &path);
+ bool checkDirPath(Glib::ustring &path, Glib::ustring errString);
+ void updatePaths();
int getString (const char* src, char* dst);
- void error (int line);
+ void error (int line);
public:
bool savesParamsAtExit;
@@ -60,7 +74,8 @@ class Options {
int adjusterDelay;
int startupDir;
Glib::ustring startupPath;
- Glib::ustring profilePath;
+ Glib::ustring profilePath; // can be an absolute or relative path; depending on this value, bundled profiles may not be found
+ bool useBundledProfiles; // only used if multiUser == true
Glib::ustring loadSaveProfilePath;
Glib::ustring lastSaveAsPath;
int saveAsDialogWidth;
@@ -207,8 +222,15 @@ class Options {
static void load ();
static void save ();
+ // if multiUser=false, send back the global profile path
+ Glib::ustring getPreferredProfilePath();
+ Glib::ustring getUserProfilePath() { return userProfilePath; }
+ Glib::ustring getGlobalProfilePath() { return globalProfilePath; }
+ Glib::ustring findProfilePath(Glib::ustring &profName);
bool has_retained_extention (Glib::ustring fname);
bool is_extention_enabled(Glib::ustring ext);
+ bool is_defProfRawMissing() { return defProfRawMissing; }
+ bool is_defProfImgMissing() { return defProfImgMissing; }
};
extern Options options;
diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc
index 9a01b676f..375b9e86e 100644
--- a/rtgui/preferences.cc
+++ b/rtgui/preferences.cc
@@ -359,9 +359,8 @@ Gtk::Widget* Preferences::getProcParamsPanel () {
ffconn = flatFieldDir->signal_current_folder_changed().connect ( sigc::mem_fun(*this, &Preferences::flatFieldChanged), true);
std::vector pnames;
- if (options.multiUser)
- parseDir (Options::rtdir + "/" + options.profilePath, pnames, paramFileExtension);
- parseDir (argv0 + "/" + options.profilePath, pnames, paramFileExtension);
+ parseDir (options.getUserProfilePath(), pnames, paramFileExtension);
+ parseDir (options.getGlobalProfilePath(), pnames, paramFileExtension);
for (size_t i=0; iappend_text (pnames[i]);
iprofiles->append_text (pnames[i]);
@@ -944,6 +943,9 @@ Gtk::Widget* Preferences::getSoundPanel () {
void Preferences::parseDir (Glib::ustring dirname, std::vector& items, Glib::ustring ext) {
+ if (dirname.empty())
+ return;
+
// process directory
Glib::Dir* dir = NULL;
try {
@@ -952,9 +954,8 @@ void Preferences::parseDir (Glib::ustring dirname, std::vector& i
catch (const Glib::FileError& fe) {
return;
}
- dirname = dirname + "/";
for (Glib::DirIterator i = dir->begin(); i!=dir->end(); ++i) {
- Glib::ustring fname = dirname + *i;
+ Glib::ustring fname = Glib::build_filename(dirname, *i);
Glib::ustring sname = *i;
// ignore directories
if (!safe_file_test (fname, Glib::FILE_TEST_IS_DIR) && sname.size() >= ext.size() && sname.substr (sname.size()-ext.size(), ext.size()).casefold() == ext)
@@ -966,7 +967,9 @@ void Preferences::parseDir (Glib::ustring dirname, std::vector& i
void Preferences::storePreferences () {
moptions.defProfRaw = rprofiles->get_active_text();
+ if (moptions.defProfRaw.empty()) moptions.defProfRaw = DEFPROFILE_RAW;
moptions.defProfImg = iprofiles->get_active_text();
+ if (moptions.defProfImg.empty()) moptions.defProfImg = DEFPROFILE_IMG;
moptions.dateFormat = dateformat->get_text();
moptions.panAccelFactor = (int)panFactor->get_value();
moptions.fbShowDateTime = showDateTime->get_active ();
diff --git a/rtgui/profilepanel.cc b/rtgui/profilepanel.cc
index cb06d0c24..7b8189e61 100644
--- a/rtgui/profilepanel.cc
+++ b/rtgui/profilepanel.cc
@@ -126,11 +126,9 @@ void ProfilePanel::save_clicked (GdkEventButton* event) {
Gtk::FileChooserDialog dialog(M("PROFILEPANEL_SAVEDLGLABEL"), Gtk::FILE_CHOOSER_ACTION_SAVE);
if (options.loadSaveProfilePath.length())
- dialog.set_current_folder (options.loadSaveProfilePath);
- else if (options.multiUser)
- dialog.set_current_folder (Options::rtdir + "/" + options.profilePath);
+ dialog.set_current_folder (options.loadSaveProfilePath);
else
- dialog.set_current_folder (argv0 + "/" + options.profilePath);
+ dialog.set_current_folder (options.getPreferredProfilePath());
//Add response buttons the the dialog:
dialog.add_button(Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL);
@@ -269,10 +267,8 @@ void ProfilePanel::load_clicked (GdkEventButton* event) {
Gtk::FileChooserDialog dialog(M("PROFILEPANEL_LOADDLGLABEL"), Gtk::FILE_CHOOSER_ACTION_OPEN);
if (options.loadSaveProfilePath.length())
dialog.set_current_folder (options.loadSaveProfilePath);
- else if (options.multiUser)
- dialog.set_current_folder (Options::rtdir + "/" + options.profilePath);
else
- dialog.set_current_folder (argv0 + "/" + options.profilePath);
+ dialog.set_current_folder (options.getPreferredProfilePath());
//Add response buttons the the dialog:
dialog.add_button(Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL);
@@ -489,13 +485,19 @@ void ProfilePanel::initProfile (const Glib::ustring& profname, ProcParams* lastS
profiles->set_active (0);
PartialProfile* s = profileStore.getProfile (profiles->get_active_text());
if (!s) {
+ changeconn.block (false);
s = new PartialProfile (true);
s->set(true);
dels = true; // we've created a temporary PartialProfile, so we set a flag to destroy it
+ if (tpc)
+ tpc->profileChange (s, EvPhotoLoaded, DEFPROFILE_INTERNAL);
+ }
+ else {
+ Glib::ustring cProfile = profiles->get_active_text();
+ changeconn.block (false);
+ if (tpc)
+ tpc->profileChange (s, EvPhotoLoaded, cProfile);
}
- changeconn.block (false);
- if (tpc)
- tpc->profileChange (s, EvPhotoLoaded, profiles->get_active_text());
if (dels) {
s->deleteInstance();
diff --git a/rtgui/profilestore.cc b/rtgui/profilestore.cc
index 9516a88d5..a971d6455 100644
--- a/rtgui/profilestore.cc
+++ b/rtgui/profilestore.cc
@@ -26,18 +26,58 @@ ProfileStore profileStore;
using namespace rtengine;
using namespace rtengine::procparams;
-extern Glib::ustring argv0;
+ProfileStore::ProfileStore () {
+ storeState = STORESTATE_NOTINITIALIZED;
+ parseMutex = NULL;
+}
+
+bool ProfileStore::init () {
+ if (storeState == STORESTATE_DELETED)
+ return false;
+ if (storeState == STORESTATE_NOTINITIALIZED) {
+ storeState = STORESTATE_BEINGINITIALIZED;
+ parseMutex = new Glib::Mutex();
+ _parseProfiles ();
+ storeState = STORESTATE_INITIALIZED;
+ }
+ return true;
+}
ProfileStore::~ProfileStore () {
+
+ // This lock prevent object's suppression while scanning the directories
+ storeState = STORESTATE_DELETED;
+ Glib::Mutex::Lock lock(*parseMutex);
+
for (std::map::iterator i = partProfiles.begin(); i!=partProfiles.end(); i++) {
if (i->second->pparams) delete i->second->pparams;
if (i->second->pedited) delete i->second->pedited;
delete i->second;
}
+ partProfiles.clear ();
+ lock.release();
+ delete parseMutex;
+ parseMutex = NULL;
}
+/*
+ * 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
+ */
void ProfileStore::parseProfiles () {
+ if (!init())
+ // I don't even know if this situation can occur
+ return;
+ Glib::Mutex::Lock lock(*parseMutex);
+
+ _parseProfiles ();
+}
+
+void ProfileStore::_parseProfiles () {
+
// clear loaded profiles
for (std::map::iterator i = partProfiles.begin(); i!=partProfiles.end(); i++) {
delete i->second->pparams;
@@ -46,35 +86,25 @@ void ProfileStore::parseProfiles () {
}
partProfiles.clear ();
- if (options.multiUser) {
- Glib::ustring userPD = options.rtdir + "/" + options.profilePath;
- if (!safe_file_test (userPD, Glib::FILE_TEST_IS_DIR))
- safe_g_mkdir_with_parents (userPD, 511);
- parseDir (userPD);
- }
- parseDir (argv0 + "/" + options.profilePath);
+ parseDir (options.getUserProfilePath());
+ parseDir (options.getGlobalProfilePath());
}
void ProfileStore::parseDir (const Glib::ustring& pdir) {
// reload the available profiles from the profile dir
- if (pdir!="") {
+ if (pdir!="" && safe_file_test(pdir, Glib::FILE_TEST_EXISTS) && safe_file_test(pdir, Glib::FILE_TEST_IS_DIR)) {
// process directory
Glib::ustring dirname = pdir;
Glib::Dir* dir = NULL;
- try {
- dir = new Glib::Dir (dirname);
- }
- catch (const Glib::FileError& fe) {
- return;
- }
+ dir = new Glib::Dir (dirname);
dirname = dirname + "/";
for (Glib::DirIterator i = dir->begin(); i!=dir->end(); ++i) {
Glib::ustring fname = dirname + *i;
Glib::ustring sname = *i;
// ignore directories
if (!safe_file_test (fname, Glib::FILE_TEST_IS_DIR)) {
- size_t lastdot = sname.find_last_of ('.');
+ size_t lastdot = sname.find_last_of ('.');
if (lastdot!=Glib::ustring::npos && lastdot<=sname.size()-4 && !sname.casefold().compare (lastdot, 4, paramFileExtension)) {
if( options.rtSettings.verbose )
printf ("Processing file %s...\n", fname.c_str());
@@ -99,32 +129,67 @@ void ProfileStore::parseDir (const Glib::ustring& pdir) {
}
delete dir;
}
+ // Check if the default profiles has been found. If no, create default instance
+ // This operation is safe: if the profile is finally found in another directory, the profile will be updated
+ if (partProfiles.find(options.defProfRaw) == partProfiles.end()) {
+ PartialProfile* pProf = new PartialProfile (true);
+ pProf->set(true);
+ partProfiles[options.defProfRaw] = pProf;
+ }
+ if (partProfiles.find(options.defProfImg) == partProfiles.end()) {
+ PartialProfile* pProf = new PartialProfile (true);
+ pProf->set(true);
+ partProfiles[options.defProfImg] = pProf;
+ }
}
PartialProfile* ProfileStore::getProfile (const Glib::ustring& profname) {
- std::map::iterator prof = partProfiles.find(profname);
- if (prof != partProfiles.end())
- return partProfiles[profname];
- else
+ if (!init())
+ // I don't even know if this situation can occur
return NULL;
+ Glib::Mutex::Lock lock(*parseMutex);
+
+ if (partProfiles.find(profname) != partProfiles.end()) {
+ return partProfiles[profname];
+ }
+ else {
+ return NULL;
+ }
}
std::vector ProfileStore::getProfileNames () {
std::vector ret;
+
+ if (!init())
+ // I don't even know if this situation can occur
+ return ret;
+ Glib::Mutex::Lock lock(*parseMutex);
+
for (std::map::iterator i = partProfiles.begin(); i!=partProfiles.end(); i++)
ret.push_back (i->first);
return ret;
}
+/*
+ * 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
+ */
ProcParams* ProfileStore::getDefaultProcParams (bool isRaw) {
+ if (!init())
+ // I don't even know if this situation can occur
+ return NULL;
+ //Note: the mutex is locked in getProfile, called below
+
PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg);
+ // NOTE: pProf should not be NULL anymore, since init() should have created the default profiles already, but the code is left as is
if (!pProf) {
- Glib::ustring profName = isRaw ? options.defProfRaw : options.defProfImg;
- pProf = new PartialProfile (true);
- partProfiles[profName] = pProf;
+ pProf = new PartialProfile (true);
+ pProf->set(true);
+ partProfiles[DEFPROFILE_INTERNAL] = pProf;
}
return pProf->pparams;
}
diff --git a/rtgui/profilestore.h b/rtgui/profilestore.h
index 4c11df795..312df1913 100644
--- a/rtgui/profilestore.h
+++ b/rtgui/profilestore.h
@@ -27,12 +27,24 @@
class ProfileStore {
+ typedef enum {
+ STORESTATE_NOTINITIALIZED,
+ STORESTATE_BEINGINITIALIZED,
+ STORESTATE_INITIALIZED,
+ STORESTATE_DELETED
+ } StoreState;
+
+ Glib::Mutex *parseMutex;
+ StoreState storeState;
std::map partProfiles;
void parseDir (const Glib::ustring& pdir);
+ void _parseProfiles ();
public:
+ ProfileStore();
~ProfileStore();
+ bool init ();
void parseProfiles ();
rtengine::procparams::PartialProfile* getProfile (const Glib::ustring& profname);
std::vector getProfileNames ();
diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc
index 8f94fd392..377efd4f4 100644
--- a/rtgui/thumbnail.cc
+++ b/rtgui/thumbnail.cc
@@ -185,10 +185,11 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu
Glib::ustring defProf = getType()==FT_Raw ? options.defProfRaw : options.defProfImg;
const CacheImageData* cfs=getCacheImageData();
- if (!options.customProfileBuilder.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) {
+ Glib::ustring defaultPparamsPath = options.findProfilePath(defProf);
+ if (!options.customProfileBuilder.empty() && !defaultPparamsPath.empty() && (!hasProcParams() || forceCPB) && cfs && cfs->exifValid) {
// For the filename etc. do NOT use streams, since they are not UTF8 safe
Glib::ustring cmdLine=Glib::ustring("\"") + options.customProfileBuilder + Glib::ustring("\" \"") + fname + Glib::ustring("\" \"")
- + options.rtdir + Glib::ustring("/") + options.profilePath + Glib::ustring("/") + defProf + Glib::ustring(".pp3") + Glib::ustring("\" ");
+ + Glib::build_filename(defaultPparamsPath, defProf + paramFileExtension) + Glib::ustring("\" ");
// ustring doesn't know int etc formatting, so take these via (unsafe) stream
std::ostringstream strm;