diff --git a/.gitignore b/.gitignore index eccbb70..a049e4f 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,11 @@ rawtherapee-3.0-a1.tar.bz2 /rawtherapee-5.6.tar.xz /rawtherapee-5.7.tar.xz /rawtherapee-5.8.tar.xz +/RawTherapee-68ec1a5bb50b496e255a0b843c36345fbeff2468.tar.gz +/RawTherapee-4059ae5bcad8e18443bf8f37bcff84de1dcc0d03.tar.gz +/rawtherapee-5.9.tar.xz +/rawtherapee-5.10-rc1.tar.xz +/rawtherapee-5.10.tar.xz +/rawtherapee-5.11.tar.xz +/rawtherapee-5.12-rc1.tar.xz +/rawtherapee-5.12.tar.xz diff --git a/0001-Fix-static-init-order-fiasco-crashes.patch b/0001-Fix-static-init-order-fiasco-crashes.patch new file mode 100644 index 0000000..15f483c --- /dev/null +++ b/0001-Fix-static-init-order-fiasco-crashes.patch @@ -0,0 +1,5810 @@ +From 9ce6a4dd297d47044c15936c89cb27f3d5cf94bf Mon Sep 17 00:00:00 2001 +From: Daniel Gao +Date: Sun, 29 Jun 2025 15:08:54 -0400 +Subject: [PATCH] Fix static init order fiasco crashes + +- Fix read before initialize of options.rtSettings.srgb +- Use Meyer's singleton for default cmp +- Group extern variables into App singleton +- Fix recursive init and build issues +- Fix Apple build +--- + rtengine/CMakeLists.txt | 3 +- + rtengine/FTblockDN.cc | 13 +- + rtengine/clutstore.cc | 4 +- + rtengine/dcp.cc | 9 +- + rtengine/dfmanager.cc | 2 + + rtengine/dual_demosaic_RT.cc | 1 + + rtengine/ffmanager.cc | 2 + + rtengine/iccstore.cc | 2 + + rtengine/imageio.cc | 2 +- + rtengine/improccoordinator.cc | 9 +- + rtengine/ipwavelet.cc | 5 +- + rtengine/pixelshift.cc | 2 + + rtengine/procparams.cc | 18 ++- + rtengine/profilestore.cc | 7 +- + rtengine/rawimagesource.cc | 2 + + rtengine/rtapp.cc | 57 ++++++++ + rtengine/rtapp.h | 81 ++++++++++++ + rtengine/simpleprocess.cc | 1 + + rtgui/adjuster.cc | 4 +- + rtgui/batchqueue.cc | 25 ++-- + rtgui/batchqueueentry.cc | 1 + + rtgui/batchqueuepanel.cc | 15 ++- + rtgui/batchtoolpanelcoord.cc | 2 + + rtgui/bayerpreprocess.cc | 3 +- + rtgui/bayerprocess.cc | 3 +- + rtgui/bayerrawexposure.cc | 3 +- + rtgui/blackwhite.cc | 1 + + rtgui/bqentryupdater.cc | 1 + + rtgui/cachemanager.cc | 7 +- + rtgui/colorappearance.cc | 2 + + rtgui/colortoning.cc | 2 + + rtgui/controlspotpanel.cc | 6 +- + rtgui/crop.cc | 4 +- + rtgui/cropwindow.cc | 14 +- + rtgui/curveeditorgroup.cc | 3 +- + rtgui/curveeditorgroup.h | 2 +- + rtgui/darkframe.cc | 2 + + rtgui/defringe.cc | 1 + + rtgui/diagonalcurveeditorsubgroup.cc | 1 + + rtgui/dirbrowser.cc | 7 +- + rtgui/dirpyrdenoise.cc | 5 +- + rtgui/editorpanel.cc | 66 ++++++---- + rtgui/editwindow.cc | 9 +- + rtgui/exportpanel.cc | 3 + + rtgui/extprog.cc | 4 +- + rtgui/filebrowser.cc | 28 ++-- + rtgui/filebrowserentry.cc | 6 +- + rtgui/filecatalog.cc | 190 +++++++++++++++------------ + rtgui/filepanel.cc | 14 +- + rtgui/filmnegative.cc | 3 + + rtgui/filmsimulation.cc | 10 +- + rtgui/flatcurveeditorsubgroup.cc | 1 + + rtgui/flatfield.cc | 3 + + rtgui/guiutils.cc | 5 +- + rtgui/histogrampanel.cc | 66 +++++----- + rtgui/hsvequalizer.cc | 2 +- + rtgui/iccprofilecreator.cc | 33 ++--- + rtgui/icmpanel.cc | 4 +- + rtgui/imagearea.cc | 9 +- + rtgui/indclippedpanel.cc | 1 + + rtgui/inspector.cc | 4 + + rtgui/labcurve.cc | 2 +- + rtgui/labgrid.cc | 1 + + rtgui/lensprofile.cc | 3 +- + rtgui/locallab.cc | 4 +- + rtgui/locallabtools.cc | 61 +++++---- + rtgui/locallabtools2.cc | 80 ++++++----- + rtgui/lockablecolorpicker.cc | 11 +- + rtgui/main-cli.cc | 57 ++++---- + rtgui/main.cc | 96 ++++++-------- + rtgui/navigator.cc | 17 +-- + rtgui/options.cc | 148 ++++++++++----------- + rtgui/options.h | 34 ++--- + rtgui/paramsedited.cc | 3 + + rtgui/pdsharpening.cc | 1 + + rtgui/placesbrowser.cc | 3 + + rtgui/preferences.cc | 22 ++-- + rtgui/preprocess.cc | 3 +- + rtgui/previewmodepanel.cc | 1 + + rtgui/previewwindow.cc | 2 + + rtgui/profilepanel.cc | 18 +-- + rtgui/profilestorecombobox.cc | 2 +- + rtgui/rawcacorrection.cc | 1 + + rtgui/rawexposure.cc | 1 + + rtgui/recentbrowser.cc | 3 +- + rtgui/retinex.cc | 2 + + rtgui/rgbcurves.cc | 1 + + rtgui/rtscalable.cc | 7 +- + rtgui/rtwindow.cc | 58 ++++---- + rtgui/saveasdlg.cc | 7 +- + rtgui/saveformatpanel.cc | 2 +- + rtgui/saveformatpanel.h | 2 +- + rtgui/sharpenedge.cc | 1 + + rtgui/soundman.cc | 2 +- + rtgui/splash.cc | 15 +-- + rtgui/thresholdadjuster.cc | 2 +- + rtgui/thumbbrowserbase.cc | 10 +- + rtgui/thumbbrowserbase.h | 2 +- + rtgui/thumbbrowserentrybase.cc | 5 + + rtgui/thumbnail.cc | 48 ++++--- + rtgui/tonecurve.cc | 1 + + rtgui/toolpanelcoord.cc | 7 + + rtgui/vibrance.cc | 1 + + rtgui/wavelet.cc | 28 ++-- + rtgui/whitebalance.cc | 5 +- + rtgui/xtransprocess.cc | 3 +- + rtgui/xtransrawexposure.cc | 1 + + 107 files changed, 942 insertions(+), 637 deletions(-) + create mode 100644 rtengine/rtapp.cc + create mode 100644 rtengine/rtapp.h + +diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt +index 6b388ced4..1ba1376f4 100644 +--- a/rtengine/CMakeLists.txt ++++ b/rtengine/CMakeLists.txt +@@ -175,6 +175,7 @@ set(RTENGINESOURCEFILES + rcd_demosaic.cc + refreshmap.cc + rt_algo.cc ++ rtapp.cc + rtlensfun.cc + rtthumbnail.cc + shmap.cc +@@ -245,7 +246,7 @@ target_include_directories(rtengine + ${CMAKE_CURRENT_SOURCE_DIR} + ) + # UpdateInfo creates "rtgui/version.h" +-target_include_directories(rtengine BEFORE PRIVATE ${CMAKE_BINARY_DIR}) ++target_include_directories(rtengine BEFORE PUBLIC ${CMAKE_BINARY_DIR}) + + target_link_libraries(rtengine + ${EXPAT_LIBRARIES} +diff --git a/rtengine/FTblockDN.cc b/rtengine/FTblockDN.cc +index 4a78e0602..2cd1c56b0 100644 +--- a/rtengine/FTblockDN.cc ++++ b/rtengine/FTblockDN.cc +@@ -710,6 +710,7 @@ BENCHFUN + + bool memoryAllocationFailed = false; + ++ const auto rgbDenoiseThreadLimit = App::get().options().rgbDenoiseThreadLimit; + do { + ++numTries; + +@@ -719,7 +720,7 @@ BENCHFUN + + int numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip; + +- Tile_calc(tilesize, overlap, (options.rgbDenoiseThreadLimit == 0 && !ponder) ? (numTries == 1 ? 0 : 2) : 2, imwidth, imheight, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); ++ Tile_calc(tilesize, overlap, (rgbDenoiseThreadLimit == 0 && !ponder) ? (numTries == 1 ? 0 : 2) : 2, imwidth, imheight, numtiles_W, numtiles_H, tilewidth, tileheight, tileWskip, tileHskip); + memoryAllocationFailed = false; + const int numtiles = numtiles_W * numtiles_H; + +@@ -783,8 +784,8 @@ BENCHFUN + // Calculate number of tiles. If less than omp_get_max_threads(), then limit num_threads to number of tiles + int numthreads = MIN(numtiles, omp_get_max_threads()); + +- if (options.rgbDenoiseThreadLimit > 0) { +- numthreads = MIN(numthreads, options.rgbDenoiseThreadLimit); ++ if (rgbDenoiseThreadLimit > 0) { ++ numthreads = MIN(numthreads, rgbDenoiseThreadLimit); + } + + #ifdef _OPENMP +@@ -797,8 +798,8 @@ BENCHFUN + omp_set_nested(true); + } + +- if (options.rgbDenoiseThreadLimit > 0) +- while (denoiseNestedLevels * numthreads > options.rgbDenoiseThreadLimit) { ++ if (rgbDenoiseThreadLimit > 0) ++ while (denoiseNestedLevels * numthreads > rgbDenoiseThreadLimit) { + denoiseNestedLevels--; + } + +@@ -1764,7 +1765,7 @@ BENCHFUN + fftwf_destroy_plan(plan_forward_blox[1]); + fftwf_destroy_plan(plan_backward_blox[1]); + } +- } while (memoryAllocationFailed && numTries < 2 && (options.rgbDenoiseThreadLimit == 0) && !ponder); ++ } while (memoryAllocationFailed && numTries < 2 && (rgbDenoiseThreadLimit == 0) && !ponder); + + if (memoryAllocationFailed) { + printf("tiled denoise failed due to isufficient memory. Output is not denoised!\n"); +diff --git a/rtengine/clutstore.cc b/rtengine/clutstore.cc +index db2216a8c..fb49d2e1c 100644 +--- a/rtengine/clutstore.cc ++++ b/rtengine/clutstore.cc +@@ -319,7 +319,7 @@ std::shared_ptr rtengine::CLUTStore::getClut(const Glib::ust + + const Glib::ustring full_filename = + !Glib::path_is_absolute(filename) +- ? Glib::ustring(Glib::build_filename(options.clutsDir, filename)) ++ ? Glib::ustring(Glib::build_filename(App::get().options().clutsDir, filename)) + : filename; + + if (!cache.get(full_filename, result)) { +@@ -340,6 +340,6 @@ void rtengine::CLUTStore::clearCache() + } + + rtengine::CLUTStore::CLUTStore() : +- cache(options.clutCacheSize) ++ cache(App::get().options().clutCacheSize) + { + } +diff --git a/rtengine/dcp.cc b/rtengine/dcp.cc +index 3c6c54d33..43c882f2e 100644 +--- a/rtengine/dcp.cc ++++ b/rtengine/dcp.cc +@@ -2156,15 +2156,14 @@ void DCPStore::init(const Glib::ustring& rt_profile_dir, bool loadAll) + + file_std_profiles.clear(); + ++ auto profile_dirpath = Glib::build_filename(App::get().options().rtdir, "dcpprofiles"); ++ + if (!loadAll) { +- profileDir = { rt_profile_dir, Glib::build_filename(options.rtdir, "dcpprofiles") }; ++ profileDir = { rt_profile_dir, profile_dirpath }; + return; + } + +- std::deque dirs = { +- rt_profile_dir, +- Glib::build_filename(options.rtdir, "dcpprofiles") +- }; ++ std::deque dirs = { rt_profile_dir, profile_dirpath }; + + while (!dirs.empty()) { + // Process directory +diff --git a/rtengine/dfmanager.cc b/rtengine/dfmanager.cc +index 875517b24..c9b504940 100644 +--- a/rtengine/dfmanager.cc ++++ b/rtengine/dfmanager.cc +@@ -528,6 +528,8 @@ const std::vector* rtengine::DFManager::Implementation::getBad + + dfInfo* rtengine::DFManager::Implementation::addFileInfo(const Glib::ustring& filename, bool pool) + { ++ const Options& options = App::get().options(); ++ + const auto ext = getFileExtension(filename); + + if (ext.empty() || !options.is_extention_enabled(ext)) { +diff --git a/rtengine/dual_demosaic_RT.cc b/rtengine/dual_demosaic_RT.cc +index 749d07ed7..30ddb1a78 100644 +--- a/rtengine/dual_demosaic_RT.cc ++++ b/rtengine/dual_demosaic_RT.cc +@@ -40,6 +40,7 @@ namespace rtengine + + void RawImageSource::dual_demosaic_RT(bool isBayer, const procparams::RAWParams &raw, int winw, int winh, const array2D &rawData, array2D &red, array2D &green, array2D &blue, double &contrast, bool autoContrast) + { ++ const auto& options = App::get().options(); + + if (contrast == 0.0 && !autoContrast) { + // contrast == 0.0 means only first demosaicer will be used +diff --git a/rtengine/ffmanager.cc b/rtengine/ffmanager.cc +index 984af1e82..89aaa847d 100644 +--- a/rtengine/ffmanager.cc ++++ b/rtengine/ffmanager.cc +@@ -302,6 +302,8 @@ void FFManager::init(const Glib::ustring& pathname) + + ffInfo* FFManager::addFileInfo (const Glib::ustring& filename, bool pool) + { ++ const Options& options = App::get().options(); ++ + auto ext = getFileExtension(filename); + + if (ext.empty() || !options.is_extention_enabled(ext)) { +diff --git a/rtengine/iccstore.cc b/rtengine/iccstore.cc +index 96962e772..230199595 100644 +--- a/rtengine/iccstore.cc ++++ b/rtengine/iccstore.cc +@@ -453,6 +453,8 @@ public: + fileProfiles.clear(); + fileProfileContents.clear(); + ++ const auto& options = App::get().options(); ++ + if (loadAll) { + loadProfiles(profilesDir, &fileProfiles, &fileProfileContents, nullptr, false); + loadProfiles(userICCDir, &fileProfiles, &fileProfileContents, nullptr, false); +diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc +index 1a1605334..bc34287f3 100644 +--- a/rtengine/imageio.cc ++++ b/rtengine/imageio.cc +@@ -688,7 +688,7 @@ int ImageIO::loadTIFF (const Glib::ustring &fname) + static MyMutex thumbMutex; + MyMutex::MyLock lock(thumbMutex); + +- if(!options.serializeTiffRead) { ++ if(!App::get().options().serializeTiffRead) { + lock.release(); + } + +diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc +index 376a67842..b0bf6879f 100644 +--- a/rtengine/improccoordinator.cc ++++ b/rtengine/improccoordinator.cc +@@ -338,7 +338,8 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) + // TODO Locallab printf + MyMutex::MyLock processingLock(mProcessing); + +- bool highDetailNeeded = options.prevdemo == PD_Sidecar ? true : (todo & M_HIGHQUAL); ++ const auto prevdemo = App::get().options().prevdemo; ++ bool highDetailNeeded = prevdemo == PD_Sidecar ? true : (todo & M_HIGHQUAL); + // printf("metwb=%s \n", params->wb.method.c_str()); + + // Check if any detail crops need high detail. If not, take a fast path short cut +@@ -351,7 +352,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) + } + } + +- if (((todo & ALL) == ALL) || (todo & M_MONITOR) || panningRelatedChange || (highDetailNeeded && options.prevdemo != PD_Sidecar)) { ++ if (((todo & ALL) == ALL) || (todo & M_MONITOR) || panningRelatedChange || (highDetailNeeded && prevdemo != PD_Sidecar)) { + bwAutoR = bwAutoG = bwAutoB = -9000.f; + + if (todo == CROP && ipf.needsPCVignetting()) { +@@ -2466,7 +2467,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) + + // process crop, if needed + for (size_t i = 0; i < crops.size(); i++) +- if (crops[i]->hasListener() && (panningRelatedChange || (highDetailNeeded && options.prevdemo != PD_Sidecar) || (todo & (M_MONITOR | M_RGBCURVE | M_LUMACURVE)) || crops[i]->get_skip() == 1)) { ++ if (crops[i]->hasListener() && (panningRelatedChange || (highDetailNeeded && prevdemo != PD_Sidecar) || (todo & (M_MONITOR | M_RGBCURVE | M_LUMACURVE)) || crops[i]->get_skip() == 1)) { + crops[i]->update(todo); // may call ourselves + } + +@@ -3342,7 +3343,7 @@ bool ImProcCoordinator::getHighQualComputed() + { + // this function may only be called from detail windows + if (!highQualityComputed) { +- if (options.prevdemo == PD_Sidecar) { ++ if (App::get().options().prevdemo == PD_Sidecar) { + // we already have high quality preview + setHighQualComputed(); + } else { +diff --git a/rtengine/ipwavelet.cc b/rtengine/ipwavelet.cc +index d9d5fb2e4..85a6c13a4 100644 +--- a/rtengine/ipwavelet.cc ++++ b/rtengine/ipwavelet.cc +@@ -815,8 +815,9 @@ void ImProcFunctions::ip_wavelet(LabImage * lab, LabImage * dst, int kall, const + + + // Calculate number of tiles. If less than omp_get_max_threads(), then limit num_threads to number of tiles +- if (options.rgbDenoiseThreadLimit > 0) { +- maxnumberofthreadsforwavelet = rtengine::LIM(options.rgbDenoiseThreadLimit / 2, 1, maxnumberofthreadsforwavelet); ++ const auto rgbDenoiseThreadLimit = App::get().options().rgbDenoiseThreadLimit; ++ if (rgbDenoiseThreadLimit > 0) { ++ maxnumberofthreadsforwavelet = rtengine::LIM(rgbDenoiseThreadLimit / 2, 1, maxnumberofthreadsforwavelet); + } + + numthreads = rtengine::min(numtiles, omp_get_max_threads()); +diff --git a/rtengine/pixelshift.cc b/rtengine/pixelshift.cc +index bf17b9740..a01728820 100644 +--- a/rtengine/pixelshift.cc ++++ b/rtengine/pixelshift.cc +@@ -308,6 +308,8 @@ using namespace std; + using namespace rtengine; + void RawImageSource::pixelshift(int winx, int winy, int winw, int winh, const procparams::RAWParams &rawParamsIn, unsigned int frame, const std::string &make, const std::string &model, float rawWpCorrection) + { ++ const auto& options = App::get().options(); ++ + BENCHFUN + if(numFrames != 4) { // fallback for non pixelshift files + amaze_demosaic_RT(winx, winy, winw, winh, rawData, red, green, blue, options.chunkSizeAMAZE, options.measure); +diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc +index 2abb30593..a841c9221 100644 +--- a/rtengine/procparams.cc ++++ b/rtengine/procparams.cc +@@ -2713,7 +2713,7 @@ ColorManagementParams::ColorManagementParams() : + labgridcieMx(0.),// + labgridcieMy(0.),// + aRendIntent(RI_RELATIVE), +- outputProfile(options.rtSettings.srgb), ++ outputProfile(App::get().options().rtSettings.srgb), + outputIntent(RI_RELATIVE), + outputBPC(true) + { +@@ -3315,7 +3315,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : + avoidrad(0.), + transitweak(1.0), + transitgrad(0.0), +- hishow(options.complexity != 2), ++ hishow(App::get().options().complexity != 2), + activ(true), + avoidneg(true), + blwh(false), +@@ -5126,13 +5126,15 @@ LocallabParams::LocallabSpot::LocallabSpot() : + // init settings with Preferences / options : must be followed by call to spotMethodChanged in controlspotpanel.cc (idle_register) + // new values default with different SpotMethod. + +- if(options.spotmet == 3) {//global ++ const auto spotmet = App::get().options().spotmet; ++ ++ if(spotmet == 3) {//global + spotMethod = "main"; + loc = {3000, 3000, 3000, 3000}; + transit =100.; + shape = "RECT"; + +- } else if(options.spotmet == 2) {//full image ++ } else if(spotmet == 2) {//full image + spotMethod = "full"; + loc = {3000, 3000, 3000, 3000}; + transit =100.; +@@ -5153,7 +5155,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : + sensimask = 60; + sensicie = 60; + +- } else if(options.spotmet == 1) {//exclude ++ } else if(spotmet == 1) {//exclude + spotMethod = "exc"; + shape = "ELI"; + loc = {150, 150, 150, 150}; +@@ -5173,7 +5175,7 @@ LocallabParams::LocallabSpot::LocallabSpot() : + sensimask = 60; + sensicie = 60; + +- } else if(options.spotmet == 0) {//normal ++ } else if(spotmet == 0) {//normal + spotMethod = "norm"; + shape = "ELI"; + loc = {150, 150, 150, 150}; +@@ -6735,6 +6737,8 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo + return 0; + } + ++ const auto& options = App::get().options(); ++ + Glib::ustring sPParams; + + try { +@@ -8670,6 +8674,8 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) + return 1; + } + ++ const auto& options = App::get().options(); ++ + Glib::KeyFile keyFile; + + try { +diff --git a/rtengine/profilestore.cc b/rtengine/profilestore.cc +index 849e78165..555f30146 100644 +--- a/rtengine/profilestore.cc ++++ b/rtengine/profilestore.cc +@@ -126,6 +126,7 @@ void ProfileStore::_parseProfiles () + + folders.push_back ("<<< ROOT >>>"); // Fake path, so parentFolderId == 0 will be used to attach a ProfileStoreEntry to the root container, not sub-menu + ++ auto& options = App::get().mut_options(); + Glib::ustring p1 = options.getUserProfilePath(); + Glib::ustring p2 = options.getGlobalProfilePath(); + bool displayLevel0 = options.useBundledProfiles && !p1.empty() && !p2.empty() && p1 != p2; +@@ -221,7 +222,7 @@ bool ProfileStore::parseDir (Glib::ustring& realPath, Glib::ustring& virtualPath + } else { + size_t lastdot = currDir.find_last_of ('.'); + +- if (lastdot != Glib::ustring::npos && lastdot == currDir.length() - 4 && currDir.substr (lastdot).casefold() == paramFileExtension) { ++ if (lastdot != Glib::ustring::npos && lastdot == currDir.length() - 4 && currDir.substr (lastdot).casefold() == App::PARAM_FILE_EXTENSION) { + // file found + if (settings->verbose) { + printf ("Processing file %s...", fname.c_str()); +@@ -307,7 +308,7 @@ const ProfileStoreEntry* ProfileStore::findEntryFromFullPathU (Glib::ustring pat + if ( + lastdot_pos != Glib::ustring::npos + && lastdot_pos <= casefolded_path.size() - 4 +- && !casefolded_path.compare (lastdot_pos, 4, paramFileExtension) ++ && !casefolded_path.compare (lastdot_pos, 4, App::PARAM_FILE_EXTENSION) + ) { + // removing the extension + // now use dot position without casefold() +@@ -432,6 +433,7 @@ const ProcParams* ProfileStore::getDefaultProcParams (bool isRaw) + //Note: the mutex is locked in getProfile, called below + // eventual initialization is done there too + ++ const auto& options = App::get().options(); + const PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg); + + if (!pProf) { +@@ -452,6 +454,7 @@ const PartialProfile* ProfileStore::getDefaultPartialProfile (bool isRaw) + //Note: the mutex is locked in getProfile, called below + // eventual initialization is done there too + ++ const auto& options = App::get().options(); + const PartialProfile* pProf = getProfile (isRaw ? options.defProfRaw : options.defProfImg); + + if (!pProf) { +diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc +index c39ee4823..9a7fbefd3 100644 +--- a/rtengine/rawimagesource.cc ++++ b/rtengine/rawimagesource.cc +@@ -1759,6 +1759,7 @@ void RawImageSource::preprocess(const RAWParams &raw, const LensProfParams &lens + plistener->setProgress(0.0); + } + ++ const auto& options = App::get().options(); + if (numFrames == 4) { + double fitParams[64]; + float *buffer = CA_correct_RT(raw.ca_autocorrect, raw.caautoiterations, raw.cared, raw.cablue, raw.ca_avoidcolourshift, raw.bayersensor.border, *rawDataFrames[0], fitParams, false, true, nullptr, false, options.chunkSizeCA, options.measure); +@@ -1799,6 +1800,7 @@ void RawImageSource::demosaic(const RAWParams &raw, bool autoContrast, double &c + MyTime t1, t2; + t1.set(); + ++ const auto& options = App::get().options(); + if (ri->getSensorType() == ST_BAYER) { + if (raw.bayersensor.method == RAWParams::BayerSensor::getMethodString(RAWParams::BayerSensor::Method::HPHD)) { + hphd_demosaic(); +diff --git a/rtengine/rtapp.cc b/rtengine/rtapp.cc +new file mode 100644 +index 000000000..70358acc9 +--- /dev/null ++++ b/rtengine/rtapp.cc +@@ -0,0 +1,57 @@ ++/* ++ * This file is part of RawTherapee. ++ * ++ * Copyright (c) 2025 Daniel Gao ++ * ++ * 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 "rtapp.h" ++ ++#include "rtengine/procparams.h" ++#include "rtengine/settings.h" ++#include "rtgui/options.h" ++#include "rtgui/version.h" ++ ++using namespace rtengine; ++ ++const Glib::ustring App::VERSION = RTVERSION; ++const Glib::ustring App::PARAM_FILE_EXTENSION = ".pp3"; ++ ++App& App::get() ++{ ++ static App inst; ++ return inst; ++} ++ ++App::App() ++ : m_options(new Options()) ++ , m_simple_editor(false) ++ , m_gimp_plugin(false) ++ , m_remote(false) ++{ ++} ++ ++const rtengine::Settings& App::settings() const ++{ ++ return m_options->rtSettings; ++} ++ ++const rtengine::procparams::ColorManagementParams& App::fallbackColorCmp() const ++{ ++ // Constructor accesses App::get().options(), so we can't initialize this ++ // in the constructor of App. It would be a recursive init. ++ static rtengine::procparams::ColorManagementParams cmp; ++ return cmp; ++} +diff --git a/rtengine/rtapp.h b/rtengine/rtapp.h +new file mode 100644 +index 000000000..2dc0ba6fb +--- /dev/null ++++ b/rtengine/rtapp.h +@@ -0,0 +1,81 @@ ++/* ++ * This file is part of RawTherapee. ++ * ++ * Copyright (c) 2025 Daniel Gao ++ * ++ * 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 . ++ */ ++ ++#pragma once ++ ++#include ++ ++#include ++ ++class Options; ++ ++namespace rtengine { ++class Settings; ++ ++namespace procparams { ++class ColorManagementParams; ++} // namespace procparams ++ ++} // namespace rtengine ++ ++class App { ++public: ++ static const Glib::ustring VERSION; ++ static const Glib::ustring PARAM_FILE_EXTENSION; ++ ++ static App& get(); ++ ++ const Options& options() const { return *m_options; } ++ Options& mut_options() { return *m_options; } ++ const rtengine::Settings& settings() const; ++ ++ const rtengine::procparams::ColorManagementParams& fallbackColorCmp() const; ++ ++ const Glib::ustring& argv0() const { return m_argv0; } ++ const Glib::ustring& argv1() const { return m_argv1; } ++ const Glib::ustring& creditsPath() const { return m_credits_path; } ++ const Glib::ustring& licensePath() const { return m_license_path; } ++ ++ bool isSimpleEditor() const { return m_simple_editor; } ++ bool isGimpPlugin() const { return m_gimp_plugin; } ++ bool isRemote() const { return m_remote; } ++ ++ void setArgv0(const Glib::ustring& val) { m_argv0 = val; } ++ void setArgv1(const Glib::ustring& val) { m_argv1 = val; } ++ void setCreditsPath(const Glib::ustring& path) { m_credits_path = path; } ++ void setLicensePath(const Glib::ustring& path) { m_license_path = path; } ++ ++ void setIsSimpleEditor(bool val) { m_simple_editor = val; } ++ void setIsGimpPlugin(bool val) { m_gimp_plugin = val; } ++ void setIsRemote(bool val) { m_remote = val; } ++ ++private: ++ App(); ++ ++ Glib::ustring m_argv0; ++ Glib::ustring m_argv1; ++ Glib::ustring m_credits_path; ++ Glib::ustring m_license_path; ++ ++ std::unique_ptr m_options; ++ ++ bool m_simple_editor; ++ bool m_gimp_plugin; ++ bool m_remote; ++}; +diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc +index c9307d623..04eea7735 100644 +--- a/rtengine/simpleprocess.cc ++++ b/rtengine/simpleprocess.cc +@@ -1457,6 +1457,7 @@ private: + + LUTu histToneCurve; + ++ const auto& options = App::get().options(); + ipf.rgbProc(baseImg, labView, nullptr, curve1, curve2, curve, params.toneCurve.saturation, rCurve, gCurve, bCurve, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, opautili, clToningcurve, cl2Toningcurve, customToneCurve1, customToneCurve2, customToneCurvebw1, customToneCurvebw2, rrm, ggm, bbm, autor, autog, autob, expcomp, hlcompr, hlcomprthresh, dcpProf, as, histToneCurve, options.chunkSizeRGB, options.measure); + + if (settings->verbose) { +diff --git a/rtgui/adjuster.cc b/rtgui/adjuster.cc +index b126d91d9..1d7bc42e4 100644 +--- a/rtgui/adjuster.cc ++++ b/rtgui/adjuster.cc +@@ -55,8 +55,8 @@ Adjuster::Adjuster( + imageIcon2(imgIcon2), + automatic(nullptr), + adjusterListener(nullptr), +- spinChange(options.adjusterMinDelay, options.adjusterMaxDelay), +- sliderChange(options.adjusterMinDelay, options.adjusterMaxDelay), ++ spinChange(App::get().options().adjusterMinDelay, App::get().options().adjusterMaxDelay), ++ sliderChange(App::get().options().adjusterMinDelay, App::get().options().adjusterMaxDelay), + editedCheckBox(nullptr), + afterReset(false), + blocked(false), +diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc +index a62d150f0..0396bd07a 100644 +--- a/rtgui/batchqueue.cc ++++ b/rtgui/batchqueue.cc +@@ -56,7 +56,7 @@ namespace // local helper functions + // For N, return Nth index from the end, and for -N return the Nth index from the start. + // N is a digit 1 through 9. The returned value is not range-checked, so it may be >=numPathElements. + // or negative. The caller performs any required range-checking. +- int decodePathIndex(unsigned int& ix, Glib::ustring& templateText, size_t numPathElements) ++ int decodePathIndex(unsigned int& ix, const Glib::ustring& templateText, size_t numPathElements) + { + int pathIndex = static_cast(numPathElements); // a value that means input was invalid + bool fromStart = false; +@@ -196,7 +196,7 @@ void BatchQueue::resizeLoadedQueue() + // leading to very long waiting when adding more images + int BatchQueue::calcMaxThumbnailHeight() + { +- return std::min(options.maxThumbnailHeight, 200); ++ return std::min(App::get().options().maxThumbnailHeight, 200); + } + + // Function for virtual override in thumbbrowser base +@@ -207,13 +207,13 @@ int BatchQueue::getMaxThumbnailHeight() const + + void BatchQueue::saveThumbnailHeight (int height) + { +- options.thumbSizeQueue = height; ++ App::get().mut_options().thumbSizeQueue = height; + } + + int BatchQueue::getThumbnailHeight () + { + // The user could have manually forced the option to a too big value +- return std::max(std::min(options.thumbSizeQueue, 200), 10); ++ return std::max(std::min(App::get().options().thumbSizeQueue, 200), 10); + } + + void BatchQueue::rightClicked () +@@ -297,7 +297,7 @@ void BatchQueue::addEntries (const std::vector& entries, bool + + bool BatchQueue::saveBatchQueue () + { +- const auto fileName = Glib::build_filename (options.rtdir, "batch", "queue.csv"); ++ const auto fileName = Glib::build_filename (App::get().options().rtdir, "batch", "queue.csv"); + + std::ofstream file (fileName, std::ios::binary | std::ios::trunc); + +@@ -343,6 +343,7 @@ bool BatchQueue::saveBatchQueue () + + bool BatchQueue::loadBatchQueue () + { ++ const auto& options = App::get().options(); + const auto fileName = Glib::build_filename (options.rtdir, "batch", "queue.csv"); + + std::ifstream file (fileName, std::ios::binary); +@@ -471,12 +472,12 @@ Glib::ustring BatchQueue::getTempFilenameForParams( const Glib::ustring &filenam + timeinfo = localtime ( &rawtime ); + strftime (stringTimestamp, sizeof(stringTimestamp), "_%Y%m%d%H%M%S_", timeinfo); + Glib::ustring savedParamPath; +- savedParamPath = options.rtdir + "/batch/"; ++ savedParamPath = App::get().options().rtdir + "/batch/"; + g_mkdir_with_parents (savedParamPath.c_str (), 0755); + savedParamPath += Glib::path_get_basename (filename); + savedParamPath += stringTimestamp; + savedParamPath += mseconds; +- savedParamPath += paramFileExtension; ++ savedParamPath += App::PARAM_FILE_EXTENSION; + return savedParamPath; + } + +@@ -643,6 +644,7 @@ void BatchQueue::updateDestinationPathPreview() + if (!selected.empty()) { + auto& entry = *selected.at(0); + int sequence = 0; // Sequence during subsequent queue processing can't be determined here ++ const auto& options = App::get().options(); + Glib::ustring baseDestination = calcAutoFileNameBase(entry.filename, sequence); + Glib::ustring destination = Glib::ustring::compose ("%1.%2", baseDestination, options.saveFormatBatch.format); + +@@ -758,6 +760,7 @@ rtengine::ProcessingJob* BatchQueue::imageReady(rtengine::IImagefloat* img) + Glib::ustring fname; + SaveFormat saveFormat; + ++ const auto& options = App::get().options(); + if (processing->outFileName.empty()) { // auto file name + Glib::ustring s = calcAutoFileNameBase (processing->filename, processing->sequence); + saveFormat = options.saveFormatBatch; +@@ -803,8 +806,8 @@ rtengine::ProcessingJob* BatchQueue::imageReady(rtengine::IImagefloat* img) + if (saveFormat.saveParams) { + // We keep the extension to avoid overwriting the profile when we have + // the same output filename with different extension +- //processing->params.save (removeExtension(fname) + paramFileExtension); +- processing->params->save (fname + ".out" + paramFileExtension); ++ //processing->params.save (removeExtension(fname) + App::PARAM_FILE_EXTENSION); ++ processing->params->save (fname + ".out" + App::PARAM_FILE_EXTENSION); + } + + if (processing->thumbnail) { +@@ -932,6 +935,8 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam + filename = origFileName[k] + filename; + } + ++ const auto& options = App::get().options(); ++ + // printf ("%d, |%s|\n", extpos, filename.c_str()); + + // constructing full output path +@@ -994,7 +999,7 @@ Glib::ustring BatchQueue::calcAutoFileNameBase (const Glib::ustring& origFileNam + char rank; + rtengine::procparams::ProcParams pparams; + +- if( pparams.load(origFileName + paramFileExtension) == 0 ) { ++ if( pparams.load(origFileName + App::PARAM_FILE_EXTENSION) == 0 ) { + if (!pparams.inTrash) { + rank = pparams.rank + '0'; + } else { +diff --git a/rtgui/batchqueueentry.cc b/rtgui/batchqueueentry.cc +index 81f7176b3..3057407b3 100644 +--- a/rtgui/batchqueueentry.cc ++++ b/rtgui/batchqueueentry.cc +@@ -108,6 +108,7 @@ void BatchQueueEntry::refreshThumbnailImage () + void BatchQueueEntry::calcThumbnailSize () + { + prew = preh * origpw / origph; ++ const auto& options = App::get().options(); + if (prew > options.maxThumbnailWidth) { + const float s = static_cast(options.maxThumbnailWidth) / prew; + prew = options.maxThumbnailWidth; +diff --git a/rtgui/batchqueuepanel.cc b/rtgui/batchqueuepanel.cc +index b477790fc..529f03fa1 100644 +--- a/rtgui/batchqueuepanel.cc ++++ b/rtgui/batchqueuepanel.cc +@@ -42,6 +42,8 @@ static Glib::ustring makeFolderLabel(Glib::ustring path) + + BatchQueuePanel::BatchQueuePanel (FileCatalog* aFileCatalog) : parent(nullptr) + { ++ const auto& options = App::get().options(); ++ + set_orientation(Gtk::ORIENTATION_VERTICAL); + batchQueue = Gtk::manage( new BatchQueue(aFileCatalog) ); + +@@ -215,7 +217,7 @@ void BatchQueuePanel::init (RTWindow *parent) + { + this->parent = parent; + +- saveFormatPanel->init (options.saveFormatBatch); ++ saveFormatPanel->init (App::get().options().saveFormatBatch); + } + + // it is expected to have a non null forceOrientation value on Preferences update only. In this case, qsize is ignored and computed automatically +@@ -228,7 +230,7 @@ void BatchQueuePanel::updateTab (int qsize, int forceOrientation) + } + + Gtk::Grid* grid = Gtk::manage (new Gtk::Grid ()); +- if ((forceOrientation == 0 && options.mainNBVertical) || (forceOrientation == 2)) { ++ if ((forceOrientation == 0 && App::get().options().mainNBVertical) || (forceOrientation == 2)) { + Gtk::Label* l; + + if(!qsize ) { +@@ -279,7 +281,7 @@ void BatchQueuePanel::queueSizeChanged(int qsize, bool queueRunning, bool queueE + // There was work, but it is all done now. + queueShouldRun = false; + +- SoundManager::playSoundAsync(options.sndBatchQueueDone); ++ SoundManager::playSoundAsync(App::get().options().sndBatchQueueDone); + } + + if (queueError) { +@@ -353,6 +355,8 @@ void BatchQueuePanel::templateHelpButtonToggled() + + void BatchQueuePanel::populateTemplateHelpBuffer(Glib::RefPtr buffer) + { ++ auto& options = App::get().mut_options(); ++ + auto pos = buffer->begin(); + const auto insertTopicHeading = [&pos, buffer](const Glib::ustring& text) { + pos = buffer->insert_markup(pos, Glib::ustring::format("\n\n", text, "\n")); +@@ -403,6 +407,7 @@ void BatchQueuePanel::populateTemplateHelpBuffer(Glib::RefPtr b + // number of elements in the path. + const auto insertPathExamples = [&buffer, &pos, pathElementCount, exampleFilePath](char letter, int offset1, int mult1, int offset2, int mult2) + { ++ auto& options = App::get().mut_options(); + for (int n=0; n& ent + + void BatchQueuePanel::saveOptions () + { ++ auto& options = App::get().mut_options(); + + options.savePathTemplate = outdirTemplate->get_text(); + options.saveUsePathTemplate = useTemplate->get_active(); +@@ -506,6 +512,7 @@ void BatchQueuePanel::setDestinationPreviewText(const Glib::ustring &destination + + void BatchQueuePanel::pathFolderButtonPressed () + { ++ auto& options = App::get().mut_options(); + + Gtk::FileChooserDialog fc (getToplevelWindow (this), M("QUEUE_LOCATION_FOLDER"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER ); + fc.add_button( "_Cancel", Gtk::RESPONSE_CANCEL); // STOCKICON WAS THERE +@@ -526,12 +533,14 @@ void BatchQueuePanel::pathFolderButtonPressed () + // since these settings are shared with editorpanel : + void BatchQueuePanel::pathFolderChanged () + { ++ auto& options = App::get().mut_options(); + options.savePathFolder = outdirFolder->get_filename(); + batchQueue->updateDestinationPathPreview(); + } + + void BatchQueuePanel::formatChanged(const Glib::ustring& format) + { ++ auto& options = App::get().mut_options(); + options.saveFormatBatch = saveFormatPanel->getFormat(); + batchQueue->updateDestinationPathPreview(); + } +diff --git a/rtgui/batchtoolpanelcoord.cc b/rtgui/batchtoolpanelcoord.cc +index 4b6e16dca..426c8a6ee 100644 +--- a/rtgui/batchtoolpanelcoord.cc ++++ b/rtgui/batchtoolpanelcoord.cc +@@ -184,6 +184,7 @@ void BatchToolPanelCoordinator::initSession () + bayerrawexposure->setAdjusterBehavior (false); + xtransrawexposure->setAdjusterBehavior (false); + } else { ++ const Options& options = App::get().options(); + + for (size_t i = 0; i < toolPanels.size(); i++) { + toolPanels.at (i)->setMultiImage (true); +@@ -757,6 +758,7 @@ void BatchToolPanelCoordinator::spotWBselected (int x, int y, Thumbnail* thm) + + // toolBar->setTool (TOOL_HAND); + if (x > 0 && y > 0 && thm) { ++ const auto& options = App::get().options(); + for (size_t i = 0; i < selected.size(); i++) + if (selected[i] == thm) { + double temp; +diff --git a/rtgui/bayerpreprocess.cc b/rtgui/bayerpreprocess.cc +index ac2faf58f..d23554a13 100644 +--- a/rtgui/bayerpreprocess.cc ++++ b/rtgui/bayerpreprocess.cc +@@ -30,8 +30,9 @@ using namespace rtengine::procparams; + + const Glib::ustring BayerPreProcess::TOOL_NAME = "bayerpreprocess"; + +-BayerPreProcess::BayerPreProcess() : FoldableToolPanel(this, TOOL_NAME, M("TP_PREPROCESS_LABEL"), options.prevdemo != PD_Sidecar) ++BayerPreProcess::BayerPreProcess() : FoldableToolPanel(this, TOOL_NAME, M("TP_PREPROCESS_LABEL"), App::get().options().prevdemo != PD_Sidecar) + { ++ const auto& options = App::get().options(); + auto m = ProcEventMapper::getInstance(); + EvLineDenoiseDirection = m->newEvent(DARKFRAME, "HISTORY_MSG_PREPROCESS_LINEDENOISE_DIRECTION"); + EvPDAFLinesFilter = m->newEvent(DARKFRAME, "HISTORY_MSG_PREPROCESS_PDAFLINESFILTER"); +diff --git a/rtgui/bayerprocess.cc b/rtgui/bayerprocess.cc +index 4d5d0fa5c..9cdd30b77 100644 +--- a/rtgui/bayerprocess.cc ++++ b/rtgui/bayerprocess.cc +@@ -31,9 +31,10 @@ using namespace rtengine::procparams; + const Glib::ustring BayerProcess::TOOL_NAME = "bayerprocess"; + + BayerProcess::BayerProcess () : +- FoldableToolPanel(this, TOOL_NAME, M("TP_RAW_LABEL"), options.prevdemo != PD_Sidecar), ++ FoldableToolPanel(this, TOOL_NAME, M("TP_RAW_LABEL"), App::get().options().prevdemo != PD_Sidecar), + oldMethod(-1) + { ++ const auto& options = App::get().options(); + + auto m = ProcEventMapper::getInstance(); + EvDemosaicBorder = m->newEvent(DARKFRAME, "HISTORY_MSG_RAW_BORDER"); +diff --git a/rtgui/bayerrawexposure.cc b/rtgui/bayerrawexposure.cc +index db6ceea41..fd37142d5 100644 +--- a/rtgui/bayerrawexposure.cc ++++ b/rtgui/bayerrawexposure.cc +@@ -28,8 +28,9 @@ using namespace rtengine::procparams; + + const Glib::ustring BayerRAWExposure::TOOL_NAME = "bayerrawexposure"; + +-BayerRAWExposure::BayerRAWExposure () : FoldableToolPanel(this, TOOL_NAME, M("TP_EXPOS_BLACKPOINT_LABEL"), options.prevdemo != PD_Sidecar) ++BayerRAWExposure::BayerRAWExposure () : FoldableToolPanel(this, TOOL_NAME, M("TP_EXPOS_BLACKPOINT_LABEL"), App::get().options().prevdemo != PD_Sidecar) + { ++ const auto& options = App::get().options(); + auto m = ProcEventMapper::getInstance(); + EvDehablack = m->newEvent(DARKFRAME, "HISTORY_MSG_DEHABLACK"); + EvDehablackVoid = m->newEvent(M_VOID, "HISTORY_MSG_DEHABLACK"); +diff --git a/rtgui/blackwhite.cc b/rtgui/blackwhite.cc +index f67ee9b7f..15d8131ca 100644 +--- a/rtgui/blackwhite.cc ++++ b/rtgui/blackwhite.cc +@@ -77,6 +77,7 @@ BlackWhite::BlackWhite (): FoldableToolPanel(this, TOOL_NAME, M("TP_BWMIX_LABEL" + bottomMilestones.push_back( GradientMilestone(double(x), double(R), double(G), double(B)) ); + } + ++ auto& options = App::get().mut_options(); + luminanceCEG = new CurveEditorGroup (options.lastBWCurvesDir, M("TP_BWMIX_CHANNEL")); + luminanceCEG->setCurveListener (this); + luminanceCurve = static_cast(luminanceCEG->addCurve(CT_Flat, M("TP_BWMIX_VAL"))); +diff --git a/rtgui/bqentryupdater.cc b/rtgui/bqentryupdater.cc +index e0b887eaa..107bfa3d4 100644 +--- a/rtgui/bqentryupdater.cc ++++ b/rtgui/bqentryupdater.cc +@@ -28,6 +28,7 @@ namespace + + void thumbInterp(const unsigned char* src, int sw, int sh, unsigned char* dst, int dw, int dh) + { ++ const auto& options = App::get().options(); + + if (options.thumbInterp == 0) { + rtengine::nearestInterp (src, sw, sh, dst, dw, dh); +diff --git a/rtgui/cachemanager.cc b/rtgui/cachemanager.cc +index 7804b69b4..1e841ea0b 100644 +--- a/rtgui/cachemanager.cc ++++ b/rtgui/cachemanager.cc +@@ -54,7 +54,7 @@ void CacheManager::init () + MyMutex::MyLock lock (mutex); + + openEntries.clear (); +- baseDir = options.cacheBaseDir; ++ baseDir = App::get().options().cacheBaseDir; + + auto error = g_mkdir_with_parents (baseDir.c_str(), cacheDirMode); + +@@ -198,7 +198,7 @@ void CacheManager::renameEntry (const std::string& oldfilename, const std::strin + + const auto newmd5 = getMD5 (newfilename); + +- auto error = g_rename (getCacheFileName ("profiles", oldfilename, paramFileExtension, oldmd5).c_str (), getCacheFileName ("profiles", newfilename, paramFileExtension, newmd5).c_str ()); ++ auto error = g_rename (getCacheFileName ("profiles", oldfilename, App::PARAM_FILE_EXTENSION, oldmd5).c_str (), getCacheFileName ("profiles", newfilename, App::PARAM_FILE_EXTENSION, newmd5).c_str ()); + error |= g_rename (getCacheFileName ("images", oldfilename, ".rtti", oldmd5).c_str (), getCacheFileName ("images", newfilename, ".rtti", newmd5).c_str ()); + error |= g_rename (getCacheFileName ("embprofiles", oldfilename, ".icc", oldmd5).c_str (), getCacheFileName ("embprofiles", newfilename, ".icc", newmd5).c_str ()); + error |= g_rename (getCacheFileName ("data", oldfilename, ".txt", oldmd5).c_str (), getCacheFileName ("data", newfilename, ".txt", newmd5).c_str ()); +@@ -299,7 +299,7 @@ void CacheManager::deleteFiles (const Glib::ustring& fname, const std::string& m + } + + if (purgeProfile) { +- error |= g_remove (getCacheFileName ("profiles", fname, paramFileExtension, md5).c_str ()); ++ error |= g_remove (getCacheFileName ("profiles", fname, App::PARAM_FILE_EXTENSION, md5).c_str ()); + } + + if (error != 0 && rtengine::settings->verbose) { +@@ -371,6 +371,7 @@ void CacheManager::applyCacheSizeLimitation () const + numFiles -= 2; // because . and .. are counted + } + ++ const auto& options = App::get().options(); + if (numFiles <= options.maxCacheEntries) { + return; + } +diff --git a/rtgui/colorappearance.cc b/rtgui/colorappearance.cc +index 4e371db40..60cf5f810 100644 +--- a/rtgui/colorappearance.cc ++++ b/rtgui/colorappearance.cc +@@ -216,6 +216,8 @@ static double wbTemp2Slider (double temp) + + ColorAppearance::ColorAppearance () : FoldableToolPanel (this, TOOL_NAME, M ("TP_COLORAPP_LABEL"), false, true) + { ++ auto& options = App::get().mut_options(); ++ + CurveListener::setMulti (true); + std::vector milestones; + milestones.push_back ( GradientMilestone (0., 0., 0., 0.) ); +diff --git a/rtgui/colortoning.cc b/rtgui/colortoning.cc +index 585d900eb..432ad9496 100644 +--- a/rtgui/colortoning.cc ++++ b/rtgui/colortoning.cc +@@ -65,6 +65,8 @@ ColorToning::ColorToning () : FoldableToolPanel(this, TOOL_NAME, M("TP_COLORTONI + colorSep = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)); + pack_start (*colorSep); + ++ auto& options = App::get().mut_options(); ++ + colorCurveEditorG = new CurveEditorGroup (options.lastColorToningCurvesDir, M("TP_COLORTONING_COLOR")); + colorCurveEditorG->setCurveListener (this); + +diff --git a/rtgui/controlspotpanel.cc b/rtgui/controlspotpanel.cc +index c301fb3df..63153f8b0 100644 +--- a/rtgui/controlspotpanel.cc ++++ b/rtgui/controlspotpanel.cc +@@ -29,8 +29,6 @@ + using namespace rtengine; + using namespace procparams; + +-extern Options options; +- + //----------------------------------------------------------------------------- + // ControlSpotPanel + //----------------------------------------------------------------------------- +@@ -120,7 +118,7 @@ ControlSpotPanel::ControlSpotPanel(): + auto m = ProcEventMapper::getInstance(); + EvLocallabavoidgamutMethod = m->newEvent(AUTOEXP, "HISTORY_MSG_LOCAL_GAMUTMUNSEL"); + EvLocallabavoidnegative = m->newEvent(AUTOEXP, "HISTORY_MSG_LOCAL_AVOIDNEGATIVE"); +- const bool showtooltip = options.showtooltip; ++ const bool showtooltip = App::get().options().showtooltip; + + // pack_start(*hishow_); + +@@ -2922,7 +2920,7 @@ void ControlSpotPanel::updateguiset(int spottype, bool iscolor, bool issh, bool + + // Update GUI fullimage or main + disableListener(); +- if(spottype >= 2 && options.spotmet >= 2) {//optimize update ++ if(spottype >= 2 && App::get().options().spotmet >= 2) {//optimize update + spotMethodChanged(); + } + +diff --git a/rtgui/crop.cc b/rtgui/crop.cc +index 34f279e80..50bef963d 100644 +--- a/rtgui/crop.cc ++++ b/rtgui/crop.cc +@@ -335,7 +335,7 @@ Crop::~Crop() + void Crop::writeOptions () + { + +- options.cropPPI = (int)ppi->get_value (); ++ App::get().mut_options().cropPPI = (int)ppi->get_value (); + } + + void Crop::readOptions () +@@ -343,7 +343,7 @@ void Crop::readOptions () + + disableListener (); + +- ppi->set_value (options.cropPPI); ++ ppi->set_value (App::get().options().cropPPI); + + enableListener (); + } +diff --git a/rtgui/cropwindow.cc b/rtgui/cropwindow.cc +index 131851372..3bd899d30 100644 +--- a/rtgui/cropwindow.cc ++++ b/rtgui/cropwindow.cc +@@ -79,7 +79,7 @@ Glib::ustring CropWindow::closett; + CropWindow::CropWindow (ImageArea* parent, bool isLowUpdatePriority_, bool isDetailWindow) + : ObjectMOBuffer(parent), state(SNormal), press_x(0), press_y(0), action_x(0), action_y(0), pickedObject(-1), pickModifierKey(0), rot_deg(0), onResizeArea(false), deleted(false), + fitZoomEnabled(true), fitZoom(false), cursor_type(CSArrow), /*isLowUpdatePriority(isLowUpdatePriority_),*/ hoveredPicker(nullptr), cropLabel(Glib::ustring("100%")), +- backColor(options.bgcolor), decorated(true), isFlawnOver(false), titleHeight(30), sideBorderWidth(3), lowerBorderWidth(3), ++ backColor(App::get().options().bgcolor), decorated(true), isFlawnOver(false), titleHeight(30), sideBorderWidth(3), lowerBorderWidth(3), + upperBorderWidth(1), sepWidth(2), xpos(30), ypos(30), width(0), height(0), imgAreaX(0), imgAreaY(0), imgAreaW(0), imgAreaH(0), + imgX(-1), imgY(-1), imgW(1), imgH(1), iarea(parent), cropZoom(0), zoomVersion(0), exposeVersion(0), cropgl(nullptr), + pmlistener(nullptr), pmhlistener(nullptr), scrollAccum(0.0), observedCropWin(nullptr), +@@ -382,7 +382,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) + screenCoordToImage (x, y, action_x, action_y); + changeZoom (zoom11index, true, action_x, action_y); + fitZoom = false; +- } else if (options.cropAutoFit) { ++ } else if (App::get().options().cropAutoFit) { + zoomFitCrop(); + } else { + zoomFit(); +@@ -685,6 +685,7 @@ void CropWindow::buttonPress (int button, int type, int bstate, int x, int y) + + void CropWindow::buttonRelease (int button, int num, int bstate, int x, int y) + { ++ auto& options = App::get().mut_options(); + + EditSubscriber *editSubscriber = iarea->getCurrSubscriber(); + +@@ -891,6 +892,7 @@ void CropWindow::pointerMoved (int bstate, int x, int y) + + iarea->redraw (); + } else if (state == SCropImgMove) { ++ const auto& options = App::get().options(); + // multiplier is the amplification factor ; disabled if the user selected "1" (no amplification) + double factor = options.panAccelFactor == 1 ? 1.0 : options.panAccelFactor * zoomSteps[cropZoom].zoom; + +@@ -1449,6 +1451,8 @@ void CropWindow::expose (Cairo::RefPtr cr) + drawDecoration (cr); + } + ++ auto& options = App::get().mut_options(); ++ + int x = xpos, y = ypos; + + // draw the background +@@ -2251,7 +2255,7 @@ void CropWindow::zoomFitCrop () + centerY = cropHandler.cropParams->y + cropHandler.cropParams->h / 2; + setCropAnchorPosition(centerX, centerY); + changeZoom (cz, true, centerX, centerY); +- fitZoom = options.cropAutoFit; ++ fitZoom = App::get().options().cropAutoFit; + } else { + zoomFit(); + } +@@ -2320,7 +2324,7 @@ void CropWindow::changeZoom (int zoom, bool notify, int centerx, int centery, bo + } + + // Limit zoom according to user preferences +- double zoomLimit = zoomLimitToFraction(options.maxZoomLimit); ++ double zoomLimit = zoomLimitToFraction(App::get().options().maxZoomLimit); + while(zoomSteps[zoom].zoom > zoomLimit && zoom != 0) { + --zoom; + } +@@ -2681,6 +2685,8 @@ void CropWindow::drawObservedFrame (Cairo::RefPtr cr, int rw, in + cr->rectangle (x - 0.5, y - 0.5, w + 4, h + 4); + cr->stroke (); + ++ const auto& options = App::get().options(); ++ + // draw a "frame" line. Color of frame line can be set in preferences + cr->set_source_rgba(options.navGuideBrush[0], options.navGuideBrush[1], options.navGuideBrush[2], options.navGuideBrush[3]); //( 1, 1, 1, 1.0); + cr->rectangle (x - 1.5, y - 1.5, w + 4, h + 4); +diff --git a/rtgui/curveeditorgroup.cc b/rtgui/curveeditorgroup.cc +index 5ce3765a2..dedff2565 100644 +--- a/rtgui/curveeditorgroup.cc ++++ b/rtgui/curveeditorgroup.cc +@@ -30,7 +30,7 @@ + #include "pathutils.h" + #include "rtscalable.h" + +-CurveEditorGroup::CurveEditorGroup (Glib::ustring& curveDir, Glib::ustring groupLabel, int blank) : curveDir(curveDir), line(0), curve_reset(nullptr), ++CurveEditorGroup::CurveEditorGroup (Glib::ustring& curveDir, const Glib::ustring& groupLabel, int blank) : curveDir(curveDir), line(0), curve_reset(nullptr), + displayedCurve(nullptr), flatSubGroup(nullptr), diagonalSubGroup(nullptr), cl(nullptr), numberOfPackedCurve(0) + { + +@@ -426,6 +426,7 @@ CurveEditorSubGroup::~CurveEditorSubGroup() + + void CurveEditorSubGroup::initButton (Gtk::Button &button, const Glib::ustring &iconName, Gtk::Align align, bool separatorButton, const Glib::ustring &tooltip) + { ++ const auto& options = App::get().options(); + bool hExpand, vExpand; + if (separatorButton) { + hExpand = vExpand = true; +diff --git a/rtgui/curveeditorgroup.h b/rtgui/curveeditorgroup.h +index aa9765926..bcc13273f 100644 +--- a/rtgui/curveeditorgroup.h ++++ b/rtgui/curveeditorgroup.h +@@ -71,7 +71,7 @@ public: + * dialogs. + */ + +- explicit CurveEditorGroup(Glib::ustring& curveDir, Glib::ustring groupLabel = "", int blank = 0); ++ explicit CurveEditorGroup(Glib::ustring& curveDir, const Glib::ustring& groupLabel = "", int blank = 0); + ~CurveEditorGroup() override; + void newLine(); + void curveListComplete(); +diff --git a/rtgui/darkframe.cc b/rtgui/darkframe.cc +index ec628da64..0151ec649 100644 +--- a/rtgui/darkframe.cc ++++ b/rtgui/darkframe.cc +@@ -37,6 +37,7 @@ DarkFrame::DarkFrame () : FoldableToolPanel(this, TOOL_NAME, M("TP_DARKFRAME_LAB + hbdf = Gtk::manage(new Gtk::Box()); + hbdf->set_spacing(4); + darkFrameFile = Gtk::manage(new MyFileChooserButton(M("TP_DARKFRAME_LABEL"), Gtk::FILE_CHOOSER_ACTION_OPEN)); ++ auto& options = App::get().mut_options(); + bindCurrentFolder (*darkFrameFile, options.lastDarkframeDir); + dfLabel = Gtk::manage(new Gtk::Label(M("GENERAL_FILE"))); + btnReset = Gtk::manage(new Gtk::Button()); +@@ -211,6 +212,7 @@ void DarkFrame::darkFrameChanged() + void DarkFrame::darkFrameReset() + { + dfChanged = true; ++ const auto& options = App::get().options(); + + // caution: I had to make this hack, because set_current_folder() doesn't work correctly! + // Because szeva doesn't exist since he was committed to happy hunting ground in Issue 316 +diff --git a/rtgui/defringe.cc b/rtgui/defringe.cc +index 162538db0..53190c72a 100644 +--- a/rtgui/defringe.cc ++++ b/rtgui/defringe.cc +@@ -44,6 +44,7 @@ Defringe::Defringe () : FoldableToolPanel(this, TOOL_NAME, M("TP_DEFRINGE_LABEL" + bottomMilestones.push_back( GradientMilestone(double(x), double(R), double(G), double(B)) ); + } + ++ auto& options = App::get().mut_options(); + curveEditorPF = new CurveEditorGroup (options.lastPFCurvesDir); + curveEditorPF->setCurveListener (this); + chshape = static_cast(curveEditorPF->addCurve(CT_Flat, M("TP_PFCURVE_CURVEEDITOR_CH"))); +diff --git a/rtgui/diagonalcurveeditorsubgroup.cc b/rtgui/diagonalcurveeditorsubgroup.cc +index c30aa599a..209087737 100644 +--- a/rtgui/diagonalcurveeditorsubgroup.cc ++++ b/rtgui/diagonalcurveeditorsubgroup.cc +@@ -38,6 +38,7 @@ + + DiagonalCurveEditorSubGroup::DiagonalCurveEditorSubGroup (CurveEditorGroup* prt, Glib::ustring& curveDir) : CurveEditorSubGroup(curveDir) + { ++ const auto& options = App::get().options(); + + editedAdjuster = nullptr; + editedAdjusterValue = 0; +diff --git a/rtgui/dirbrowser.cc b/rtgui/dirbrowser.cc +index fdde48637..ce6112290 100644 +--- a/rtgui/dirbrowser.cc ++++ b/rtgui/dirbrowser.cc +@@ -144,6 +144,7 @@ void DirBrowser::fillDirTree () + + dirtree->append_column(tvc); + ++ const auto& options = App::get().options(); + tvc.set_sort_order(options.dirBrowserSortType); + tvc.set_sort_column(dtColumns.filename); + tvc.set_sort_indicator(true); +@@ -267,6 +268,7 @@ void DirBrowser::fillRoot () + + void DirBrowser::on_sort_column_changed() const + { ++ auto& options = App::get().mut_options(); + options.dirBrowserSortType = tvc.get_sort_order(); + } + +@@ -285,13 +287,14 @@ void DirBrowser::row_expanded (const Gtk::TreeModel::iterator& iter, const Gtk:: + dirTreeModel->get_sort_column_id(prevSortColumn, prevSortType); + dirTreeModel->set_sort_column(Gtk::TreeSortable::DEFAULT_UNSORTED_COLUMN_ID, Gtk::SORT_ASCENDING); + ++ const auto& options = App::get().options(); + auto dir = Gio::File::create_for_path (iter->get_value (dtColumns.dirname)); + auto subDirs = listSubDirs (dir, options.fbShowHidden); + + Gtk::TreeNodeChildren children = iter->children(); + std::list forErase(children.begin(), children.end()); + +- std::sort (subDirs.begin (), subDirs.end (), [] (const Glib::ustring& firstDir, const Glib::ustring& secondDir) ++ std::sort (subDirs.begin (), subDirs.end (), [&](const Glib::ustring& firstDir, const Glib::ustring& secondDir) + { + switch (options.dirBrowserSortType) { + default: +@@ -353,7 +356,7 @@ void DirBrowser::updateDir (const Gtk::TreeModel::iterator& iter) + + // test if new files are created + auto dir = Gio::File::create_for_path (iter->get_value (dtColumns.dirname)); +- auto subDirs = listSubDirs (dir, options.fbShowHidden); ++ auto subDirs = listSubDirs (dir, App::get().options().fbShowHidden); + + for (size_t i = 0; i < subDirs.size(); i++) { + bool found = false; +diff --git a/rtgui/dirpyrdenoise.cc b/rtgui/dirpyrdenoise.cc +index 629898d52..3616e7b40 100644 +--- a/rtgui/dirpyrdenoise.cc ++++ b/rtgui/dirpyrdenoise.cc +@@ -70,6 +70,7 @@ DirPyrDenoise::DirPyrDenoise () : FoldableToolPanel(this, TOOL_NAME, M("TP_DIRPY + + luma = Gtk::manage (new Adjuster (M("TP_DIRPYRDENOISE_LUMINANCE_SMOOTHING"), 0, 100, 0.01, 0)); + Ldetail = Gtk::manage (new Adjuster (M("TP_DIRPYRDENOISE_LUMINANCE_DETAIL"), 0, 100, 0.01, 0)); ++ auto& options = App::get().mut_options(); + NoiscurveEditorG = new CurveEditorGroup (options.lastDenoiseCurvesDir, M("TP_DIRPYRDENOISE_LUMINANCE_CURVE")); + //curveEditorG = new CurveEditorGroup (options.lastLabCurvesDir); + NoiscurveEditorG->setCurveListener (this); +@@ -444,7 +445,7 @@ void DirPyrDenoise::read (const ProcParams* pp, const ParamsEdited* pedited) + + LmethodChanged(); + +- if(options.rtSettings.leveldnautsimpl == 1) { ++ if(App::get().options().rtSettings.leveldnautsimpl == 1) { + Cmethod->set_active (0); + + if (pp->dirpyrDenoise.Cmethod == "MAN") { +@@ -690,7 +691,7 @@ void DirPyrDenoise::write (ProcParams* pp, ParamsEdited* pedited) + pp->dirpyrDenoise.Lmethod = "SLI"; + } + +- if(options.rtSettings.leveldnautsimpl == 1) { ++ if(App::get().options().rtSettings.leveldnautsimpl == 1) { + if (Cmethod->get_active_row_number() == 0) { + pp->dirpyrDenoise.Cmethod = "MAN"; + } else if (Cmethod->get_active_row_number() == 1) { +diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc +index abe0a1ec6..4edd99cdb 100644 +--- a/rtgui/editorpanel.cc ++++ b/rtgui/editorpanel.cc +@@ -396,7 +396,7 @@ private: + if (find_default_monitor_profile (profileBox.get_root_window()->gobj(), defprof, defprofname)) { + profileBox.append (M ("MONITOR_PROFILE_SYSTEM") + " (" + defprofname + ")"); + +- if (options.rtSettings.autoMonitorProfile) { ++ if (App::get().options().rtSettings.autoMonitorProfile) { + rtengine::ICCStore::getInstance()->setDefaultMonitorProfileName (defprof); + profileBox.set_active (1); + } else { +@@ -480,6 +480,7 @@ private: + ConnectionBlocker intentBlocker (intentConn); + + Glib::ustring profile; ++ const auto& options = App::get().options(); + + #if !defined(__APPLE__) // monitor profile not supported on apple + +@@ -645,6 +646,8 @@ public: + + void reset () + { ++ const auto& options = App::get().options(); ++ + ConnectionBlocker intentBlocker (intentConn); + #if !defined(__APPLE__) // monitor profile not supported on apple + ConnectionBlocker profileBlocker (profileConn); +@@ -785,6 +788,7 @@ EditorPanel::EditorPanel (FilePanel* filePanel) + iHistoryShow = new RTImage ("panel-to-right", Gtk::ICON_SIZE_LARGE_TOOLBAR); + iHistoryHide = new RTImage ("panel-to-left", Gtk::ICON_SIZE_LARGE_TOOLBAR); + ++ const auto& options = App::get().options(); + hidehp->set_relief (Gtk::RELIEF_NONE); + hidehp->set_active (options.showHistory); + hidehp->set_tooltip_markup (M ("MAIN_TOOLTIP_HIDEHP")); +@@ -797,7 +801,7 @@ EditorPanel::EditorPanel (FilePanel* filePanel) + + tbTopPanel_1 = nullptr; + +- if (!simpleEditor && filePanel) { ++ if (!App::get().isSimpleEditor() && filePanel) { + tbTopPanel_1 = new Gtk::ToggleButton (); + iTopPanel_1_Show = new RTImage ("panel-to-bottom", Gtk::ICON_SIZE_LARGE_TOOLBAR); + iTopPanel_1_Hide = new RTImage ("panel-to-top", Gtk::ICON_SIZE_LARGE_TOOLBAR); +@@ -942,7 +946,7 @@ EditorPanel::EditorPanel (FilePanel* filePanel) + + navPrev = navNext = navSync = nullptr; + +- if (!simpleEditor && !options.tabbedUI) { ++ if (!App::get().isSimpleEditor() && !options.tabbedUI) { + // Navigation buttons + Gtk::Image *navPrevImage = Gtk::manage (new RTImage ("arrow2-left", Gtk::ICON_SIZE_LARGE_TOOLBAR)); + navPrevImage->set_padding (0, 0); +@@ -976,15 +980,15 @@ EditorPanel::EditorPanel (FilePanel* filePanel) + iops->attach_next_to (*progressLabel, Gtk::POS_LEFT, 1, 1); + iops->attach_next_to (*vsep1, Gtk::POS_LEFT, 1, 1); + +- if (!gimpPlugin) { ++ if (!App::get().isGimpPlugin()) { + iops->attach_next_to(*send_to_external->buttonGroup, Gtk::POS_LEFT, 1, 1); + } + +- if (!gimpPlugin && !simpleEditor) { ++ if (!App::get().isGimpPlugin() && !App::get().isSimpleEditor()) { + iops->attach_next_to (*queueimg, Gtk::POS_LEFT, 1, 1); + } + +- if (!gimpPlugin) { ++ if (!App::get().isGimpPlugin()) { + iops->attach_next_to (*saveimgas, Gtk::POS_LEFT, 1, 1); + } + +@@ -993,7 +997,7 @@ EditorPanel::EditorPanel (FilePanel* filePanel) + colorMgmtToolBar.reset (new ColorManagementToolbar (ipc)); + colorMgmtToolBar->pack_right_in (iops); + +- if (!simpleEditor && !options.tabbedUI) { ++ if (!App::get().isSimpleEditor() && !options.tabbedUI) { + Gtk::Separator* vsep3 = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)); + iops->attach_next_to (*vsep3, Gtk::POS_RIGHT, 1, 1); + iops->attach_next_to (*navPrev, Gtk::POS_RIGHT, 1, 1); +@@ -1168,7 +1172,7 @@ void EditorPanel::leftPaneButtonReleased (GdkEventButton *event) + { + if (event->button == 1) { + // Button 1 released : it's a resize +- options.historyPanelWidth = hpanedl->get_position(); ++ App::get().mut_options().historyPanelWidth = hpanedl->get_position(); + } + + /*else if (event->button == 3) { +@@ -1181,7 +1185,7 @@ void EditorPanel::rightPaneButtonReleased (GdkEventButton *event) + int winW, winH; + parentWindow->get_size (winW, winH); + // Button 1 released : it's a resize +- options.toolPanelWidth = winW - hpanedr->get_position(); ++ App::get().mut_options().toolPanelWidth = winW - hpanedr->get_position(); + } + + /*else if (event->button == 3) { +@@ -1223,6 +1227,7 @@ void EditorPanel::showTopPanel (bool show) + + void EditorPanel::setAspect () + { ++ const auto& options = App::get().options(); + int winW, winH; + parentWindow->get_size (winW, winH); + hpanedl->set_position (options.historyPanelWidth); +@@ -1281,6 +1286,7 @@ void EditorPanel::open (Thumbnail* tmb, rtengine::InitialImage* isrc) + // try to load the last saved parameters from the cache or from the paramfile file + ProcParams* ldprof = openThm->createProcParamsForUpdate (true, false); // will be freed by initProfile + ++ const auto& options = App::get().options(); + // initialize profile + Glib::ustring defProf = openThm->getType() == FT_Raw ? options.defProfRaw : options.defProfImg; + profilep->initProfile (defProf, ldprof); +@@ -1535,6 +1541,7 @@ void EditorPanel::refreshProcessingState (bool inProcessingP) + if (processingStartedTime != 0) { + time_t curTime = ::time (nullptr); + ++ const auto& options = App::get().options(); + if (::difftime (curTime, processingStartedTime) > options.sndLngEditProcDoneSecs) { + SoundManager::playSoundAsync (options.sndLngEditProcDone); + } +@@ -1641,6 +1648,7 @@ void EditorPanel::hideHistoryActivated () + hpanedl->pack1 (*leftbox, false, false); + } + ++ auto& options = App::get().mut_options(); + options.showHistory = hidehp->get_active(); + + if (options.showHistory) { +@@ -1697,6 +1705,7 @@ void EditorPanel::tbTopPanel_1_toggled () + + if (catalogPane) { // catalogPane does not exist in multitab mode + ++ auto& options = App::get().mut_options(); + if (tbTopPanel_1->get_active()) { + catalogPane->show(); + tbTopPanel_1->set_image (*iTopPanel_1_Hide); +@@ -1882,7 +1891,7 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) + return true; + + case GDK_KEY_y: // synchronize filebrowser with image in Editor +- if (!simpleEditor && fPanel && !fname.empty()) { ++ if (!App::get().isSimpleEditor() && fPanel && !fname.empty()) { + fPanel->fileCatalog->selectImage (fname, false); + return true; + } +@@ -1890,7 +1899,7 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) + break; // to avoid gcc complain + + case GDK_KEY_x: // clear filters and synchronize filebrowser with image in Editor +- if (!simpleEditor && fPanel && !fname.empty()) { ++ if (!App::get().isSimpleEditor() && fPanel && !fname.empty()) { + fPanel->fileCatalog->selectImage (fname, true); + return true; + } +@@ -1906,21 +1915,21 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) + return true; + + case GDK_KEY_s: +- if (!gimpPlugin) { ++ if (!App::get().isGimpPlugin()) { + saveAsPressed(); + } + + return true; + + case GDK_KEY_b: +- if (!gimpPlugin && !simpleEditor) { ++ if (!App::get().isGimpPlugin() && !App::get().isSimpleEditor()) { + queueImgPressed(); + } + + return true; + + case GDK_KEY_e: +- if (!gimpPlugin) { ++ if (!App::get().isGimpPlugin()) { + sendToExternalPressed(); + } + +@@ -1962,7 +1971,7 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) + if (shift) { + switch (event->keyval) { + case GDK_KEY_F3: // open Previous image from Editor's perspective +- if (!simpleEditor && fPanel && !fname.empty()) { ++ if (!App::get().isSimpleEditor() && fPanel && !fname.empty()) { + EditorPanel::openPreviousEditorImage(); + return true; + } +@@ -1970,7 +1979,7 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) + break; // to avoid gcc complain + + case GDK_KEY_F4: // open next image from Editor's perspective +- if (!simpleEditor && fPanel && !fname.empty()) { ++ if (!App::get().isSimpleEditor() && fPanel && !fname.empty()) { + EditorPanel::openNextEditorImage(); + return true; + } +@@ -1987,7 +1996,7 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) + return true; + } + +- if (!simpleEditor && fPanel) { ++ if (!App::get().isSimpleEditor() && fPanel) { + if (fPanel->handleShortcutKey (event)) { + return true; + } +@@ -2060,7 +2069,7 @@ bool EditorPanel::idle_imageSaved (ProgressConnector *pc, rtengine::IImagef + if (sf.saveParams) { + // We keep the extension to avoid overwriting the profile when we have + // the same output filename with different extension +- pparams.save (fname + ".out" + paramFileExtension); ++ pparams.save (fname + ".out" + App::PARAM_FILE_EXTENSION); + } + } else { + error (M ("MAIN_MSG_CANNOTSAVE"), fname); +@@ -2075,7 +2084,7 @@ bool EditorPanel::idle_imageSaved (ProgressConnector *pc, rtengine::IImagef + setProgressState (false); + + delete pc; +- SoundManager::playSoundAsync (options.sndBatchQueueDone); ++ SoundManager::playSoundAsync (App::get().options().sndBatchQueueDone); + isProcessing = false; + return false; + } +@@ -2091,7 +2100,7 @@ BatchQueueEntry* EditorPanel::createBatchQueueEntry () + isrc->getImageSource()->getFullSize (fullW, fullH, pparams.coarse.rotate == 90 || pparams.coarse.rotate == 270 ? TR_R90 : TR_NONE); + int prevh = BatchQueue::calcMaxThumbnailHeight(); + int prevw = int ((size_t)fullW * (size_t)prevh / (size_t)fullH); +- return new BatchQueueEntry (job, pparams, openThm->getFileName(), prevw, prevh, openThm, options.overwriteOutputFile); ++ return new BatchQueueEntry (job, pparams, openThm->getFileName(), prevw, prevh, openThm, App::get().options().overwriteOutputFile); + } + + +@@ -2108,6 +2117,7 @@ void EditorPanel::saveAsPressed () + SaveAsDialog* saveAsDialog; + auto toplevel = static_cast (get_toplevel ()); + ++ auto& options = App::get().mut_options(); + if (Glib::file_test (options.lastSaveAsPath, Glib::FILE_TEST_IS_DIR)) { + saveAsDialog = new SaveAsDialog (options.lastSaveAsPath, toplevel); + } else { +@@ -2218,7 +2228,7 @@ void EditorPanel::sendToExternal() + // develop image + rtengine::procparams::ProcParams pparams; + ipc->getParams (&pparams); +- if (options.editor_bypass_output_profile) { ++ if (App::get().options().editor_bypass_output_profile) { + pparams.icm.outputProfile = rtengine::procparams::ColorManagementParams::NoProfileString; + } + +@@ -2241,6 +2251,7 @@ void EditorPanel::sendToExternal() + void EditorPanel::sendToExternalChanged(int) + { + int index = send_to_external->getSelected(); ++ auto& options = App::get().mut_options(); + if (index >= 0 && static_cast(index) == options.externalEditors.size()) { + index = -1; + } +@@ -2252,6 +2263,7 @@ void EditorPanel::sendToExternalChanged(int) + + void EditorPanel::sendToExternalPressed() + { ++ const auto& options = App::get().options(); + if (options.externalEditorIndex == -1) { + // "Other" external editor. Show app chooser dialog to let user pick. + RTAppChooserDialog *dialog = getAppChooserDialog(); +@@ -2279,7 +2291,7 @@ bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveForm + + int err = 0; + +- if (gimpPlugin) { ++ if (App::get().isGimpPlugin()) { + err = img->saveAsTIFF (filename, 32, true, true); + } else if (sf.format == "tif") { + err = img->saveAsTIFF (filename, sf.tiffBits, sf.tiffFloat, sf.tiffUncompressed, sf.bigTiff); +@@ -2298,21 +2310,21 @@ bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveForm + + void EditorPanel::openPreviousEditorImage() + { +- if (!simpleEditor && fPanel && !fname.empty()) { ++ if (!App::get().isSimpleEditor() && fPanel && !fname.empty()) { + fPanel->fileCatalog->openNextPreviousEditorImage (fname, false, NAV_PREVIOUS); + } + } + + void EditorPanel::openNextEditorImage() + { +- if (!simpleEditor && fPanel && !fname.empty()) { ++ if (!App::get().isSimpleEditor() && fPanel && !fname.empty()) { + fPanel->fileCatalog->openNextPreviousEditorImage (fname, false, NAV_NEXT); + } + } + + void EditorPanel::syncFileBrowser() // synchronize filebrowser with image in Editor + { +- if (!simpleEditor && fPanel && !fname.empty()) { ++ if (!App::get().isSimpleEditor() && fPanel && !fname.empty()) { + fPanel->fileCatalog->selectImage (fname, false); + } + } +@@ -2336,6 +2348,7 @@ void EditorPanel::setExternalEditorChangedSignal(ExternalEditorChangedSignal *si + + void EditorPanel::histogramProfile_toggled() + { ++ auto& options = App::get().mut_options(); + options.rtSettings.HistogramWorking = toggleHistogramProfile->get_active(); + colorMgmtToolBar->updateHistogram(); + } +@@ -2346,6 +2359,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector *p + rtengine::IImagefloat* img = pc->returnValue(); + delete pc; + ++ const auto& options = App::get().options(); + if (img) { + // get file name base + Glib::ustring shortname = removeExtension(Glib::path_get_basename(fname)); +@@ -2477,6 +2491,7 @@ void EditorPanel::onAppChooserDialogResponse(int responseId) + + void EditorPanel::updateExternalEditorSelection() + { ++ const auto& options = App::get().options(); + int index = send_to_external->getSelected(); + if (index >= 0 && static_cast(index) == options.externalEditors.size()) { + index = -1; +@@ -2816,6 +2831,7 @@ void EditorPanel::updateTPVScrollbar (bool hide) + + void EditorPanel::updateHistogramPosition (int oldPosition, int newPosition) + { ++ const auto& options = App::get().options(); + + switch (newPosition) { + case 0: +diff --git a/rtgui/editwindow.cc b/rtgui/editwindow.cc +index ce39f5d6f..f48199b03 100644 +--- a/rtgui/editwindow.cc ++++ b/rtgui/editwindow.cc +@@ -26,15 +26,13 @@ + #include + #include "threadutils.h" + +-extern Glib::ustring argv0; +- + // Check if the system has more than one display and option is set + bool EditWindow::isMultiDisplayEnabled() + { + const auto screen = Gdk::Screen::get_default(); + + if (screen) { +- return options.multiDisplayMode > 0 && screen->get_display()->get_n_monitors() > 1; ++ return App::get().options().multiDisplayMode > 0 && screen->get_display()->get_n_monitors() > 1; + } else { + return false; // There is no default screen + } +@@ -70,6 +68,7 @@ EditWindow::EditWindow (RTWindow* p) + set_title_decorated(""); + set_modal(false); + set_resizable(true); ++ const auto& options = App::get().options(); + set_default_size(options.meowWidth, options.meowHeight); + + property_destroy_with_parent().set_value(false); +@@ -96,6 +95,7 @@ void EditWindow::restoreWindow() + + int meowMonitor = 0; // By default, set to main monitor + const auto display = get_screen()->get_display(); ++ const auto& options = App::get().options(); + + if (isMultiDisplayEnabled()) { + if (options.meowMonitor >= 0) { // Use display from last session if available +@@ -168,6 +168,7 @@ void EditWindow::on_realize () + + bool EditWindow::on_configure_event(GdkEventConfigure* event) + { ++ auto& options = App::get().mut_options(); + if (!options.meowMaximized && !isFullscreen && !isMinimized) { + get_position(options.meowX, options.meowY); + get_size(options.meowWidth, options.meowHeight); +@@ -178,6 +179,7 @@ bool EditWindow::on_configure_event(GdkEventConfigure* event) + + bool EditWindow::on_window_state_event(GdkEventWindowState* event) + { ++ auto& options = App::get().mut_options(); + // Retrieve RT window states + options.meowMaximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; + isMinimized = event->new_window_state & GDK_WINDOW_STATE_ICONIFIED; +@@ -346,6 +348,7 @@ void EditWindow::get_position(int& x, int& y) const + + void EditWindow::writeOptions() + { ++ auto& options = App::get().mut_options(); + if (is_visible()) { + if (isMultiDisplayEnabled()) { + // Retrieve window monitor ID +diff --git a/rtgui/exportpanel.cc b/rtgui/exportpanel.cc +index 6e9c13fc8..8456c25aa 100644 +--- a/rtgui/exportpanel.cc ++++ b/rtgui/exportpanel.cc +@@ -169,6 +169,7 @@ ExportPanel::ExportPanel () : listener (nullptr), ornamentSurface(new RTSurface( + pack_start (*wbox, Gtk::PACK_SHRINK, 4); + pack_start (*hbox, Gtk::PACK_SHRINK, 4); + ++ const auto& options = App::get().options(); + MaxWidth->set_digits (0); + MaxWidth->set_width_chars (5); + MaxWidth->set_max_width_chars (5); +@@ -255,6 +256,7 @@ void ExportPanel::SaveSettingsAsDefault() + changed = true; \ + } \ + } while (false) ++ auto& options = App::get().mut_options(); + // Save fast export settings to options + FE_OPT_STORE_ (options.fastexport_bypass_sharpening, bypass_sharpening->get_active ()); + FE_OPT_STORE_ (options.fastexport_bypass_sharpenEdge, bypass_sharpenEdge->get_active ()); +@@ -318,6 +320,7 @@ void ExportPanel::SaveSettingsAsDefault() + + void ExportPanel::LoadDefaultSettings() + { ++ const auto& options = App::get().options(); + // Load fast export settings from options + bypass_sharpening->set_active (options.fastexport_bypass_sharpening ); + bypass_sharpenEdge->set_active (options.fastexport_bypass_sharpenEdge ); +diff --git a/rtgui/extprog.cc b/rtgui/extprog.cc +index dc3cb61b4..8579062e4 100644 +--- a/rtgui/extprog.cc ++++ b/rtgui/extprog.cc +@@ -242,6 +242,7 @@ bool ExtProgStore::openInGimp (const Glib::ustring& fileName) + { + #if defined _WIN32 + ++ const auto& options = App::get().options(); + auto executable = Glib::build_filename (options.gimpDir, "bin", "gimp-win-remote"); + auto success = ShellExecute( NULL, "open", executable.c_str(), fileName.c_str(), NULL, SW_SHOWNORMAL ); + +@@ -300,6 +301,7 @@ bool ExtProgStore::openInGimp (const Glib::ustring& fileName) + + bool ExtProgStore::openInPhotoshop (const Glib::ustring& fileName) + { ++ const auto& options = App::get().options(); + #if defined _WIN32 + + const auto executable = Glib::build_filename(options.psDir, "Photoshop.exe"); +@@ -321,7 +323,7 @@ bool ExtProgStore::openInPhotoshop (const Glib::ustring& fileName) + bool ExtProgStore::openInCustomEditor (const Glib::ustring& fileName, const Glib::ustring* command) + { + if (!command) { +- command = &(options.customEditorProg); ++ command = &(App::get().options().customEditorProg); + } + + #if defined _WIN32 +diff --git a/rtgui/filebrowser.cc b/rtgui/filebrowser.cc +index a1043d661..5035ad6e2 100644 +--- a/rtgui/filebrowser.cc ++++ b/rtgui/filebrowser.cc +@@ -51,7 +51,7 @@ const Glib::ustring* getOriginalExtension (const ThumbBrowserEntryBase* entry) + typedef std::vector ExtensionVector; + typedef ExtensionVector::const_iterator ExtensionIterator; + +- const ExtensionVector& originalExtensions = options.parsedExtensions; ++ const ExtensionVector& originalExtensions = App::get().options().parsedExtensions; + + // Extract extension from basename + const Glib::ustring basename = Glib::path_get_basename (entry->filename.lowercase()); +@@ -148,6 +148,7 @@ FileBrowser::FileBrowser () : + session_id_ = 0; + + ProfileStore::getInstance()->addListener(this); ++ const auto& options = App::get().options(); + + int p = 0; + pmenu = new Gtk::Menu (); +@@ -771,6 +772,8 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) + return; + } + ++ const auto& options = App::get().options(); ++ + for (size_t j = 0; j < mMenuExtProgs.size(); j++) { + if (m == amiExtProg[j]) { + const auto pAct = mMenuExtProgs[m->get_label()]; +@@ -869,7 +872,7 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) + if( !mselected.empty() ) { + rtengine::procparams::ProcParams pp = mselected[0]->thumbnail->getProcParams(); + Gtk::FileChooserDialog fc (getToplevelWindow (this), "Dark Frame", Gtk::FILE_CHOOSER_ACTION_OPEN ); +- bindCurrentFolder (fc, options.lastDarkframeDir); ++ bindCurrentFolder (fc, App::get().mut_options().lastDarkframeDir); + fc.add_button( M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL); + fc.add_button( M("GENERAL_APPLY"), Gtk::RESPONSE_APPLY); + +@@ -913,7 +916,7 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) + rtengine::DFManager::getInstance().init( options.rtSettings.darkFramesPath ); + } else { + // Target directory creation failed, we clear the darkFramesPath setting +- options.rtSettings.darkFramesPath.clear(); ++ App::get().mut_options().rtSettings.darkFramesPath.clear(); + Glib::ustring msg_ = Glib::ustring::compose (M("MAIN_MSG_PATHDOESNTEXIST"), escapeHtmlChars(options.rtSettings.darkFramesPath)) + + "\n\n" + M("MAIN_MSG_OPERATIONCANCELLED"); + Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); +@@ -945,7 +948,7 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) + if( !mselected.empty() ) { + rtengine::procparams::ProcParams pp = mselected[0]->thumbnail->getProcParams(); + Gtk::FileChooserDialog fc (getToplevelWindow (this), "Flat Field", Gtk::FILE_CHOOSER_ACTION_OPEN ); +- bindCurrentFolder (fc, options.lastFlatfieldDir); ++ bindCurrentFolder (fc, App::get().mut_options().lastFlatfieldDir); + fc.add_button( M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL); + fc.add_button( M("GENERAL_APPLY"), Gtk::RESPONSE_APPLY); + +@@ -989,7 +992,7 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) + rtengine::ffm.init( options.rtSettings.flatFieldsPath ); + } else { + // Target directory creation failed, we clear the flatFieldsPath setting +- options.rtSettings.flatFieldsPath.clear(); ++ App::get().mut_options().rtSettings.flatFieldsPath.clear(); + Glib::ustring msg_ = Glib::ustring::compose (M("MAIN_MSG_PATHDOESNTEXIST"), escapeHtmlChars(options.rtSettings.flatFieldsPath)) + + "\n\n" + M("MAIN_MSG_OPERATIONCANCELLED"); + Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); +@@ -1392,6 +1395,7 @@ bool FileBrowser::keyPressed (GdkEventKey* event) + + void FileBrowser::saveThumbnailHeight (int height) + { ++ auto& options = App::get().mut_options(); + if (!options.sameThumbSize && getLocation() == THLOC_EDITOR) { + options.thumbSizeTab = height; + } else { +@@ -1401,6 +1405,7 @@ void FileBrowser::saveThumbnailHeight (int height) + + int FileBrowser::getThumbnailHeight () + { ++ const auto& options = App::get().options(); + // The user could have manually forced the option to a too big value + if (!options.sameThumbSize && getLocation() == THLOC_EDITOR) { + return std::max(std::min(options.thumbSizeTab, 800), 10); +@@ -1412,6 +1417,7 @@ int FileBrowser::getThumbnailHeight () + void FileBrowser::enableTabMode(bool enable) + { + ThumbBrowserBase::enableTabMode(enable); ++ const auto& options = App::get().options(); + if (options.inspectorWindow) { + if (enable) { + inspect->remove_accelerator(pmenu->get_accel_group(), GDK_KEY_f, (Gdk::ModifierType)0); +@@ -1669,12 +1675,14 @@ void FileBrowser::fromTrashRequested (std::vector tbe) + + void FileBrowser::sortMethodRequested (int method) + { ++ auto& options = App::get().mut_options(); + options.sortMethod = Options::SortMethod(method); + resort (); + } + + void FileBrowser::sortOrderRequested (int order) + { ++ auto& options = App::get().mut_options(); + options.sortDescending = !!order; + resort (); + } +@@ -1800,7 +1808,7 @@ void FileBrowser::openNextImage() + { + MYWRITERLOCK(l, entryRW); + +- if (!fd.empty() && selected.size() > 0 && !options.tabbedUI) { ++ if (!fd.empty() && selected.size() > 0 && !App::get().options().tabbedUI) { + for (size_t i = 0; i < fd.size() - 1; i++) { + if (selected[0]->thumbnail->getFileName() == fd[i]->filename) { // located 1-st image in current selection + if (i < fd.size() && tbl) { +@@ -1863,7 +1871,7 @@ void FileBrowser::openPrevImage() + { + MYWRITERLOCK(l, entryRW); + +- if (!fd.empty() && selected.size() > 0 && !options.tabbedUI) { ++ if (!fd.empty() && selected.size() > 0 && !App::get().options().tabbedUI) { + for (size_t i = 1; i < fd.size(); i++) { + if (selected[0]->thumbnail->getFileName() == fd[i]->filename) { // located 1-st image in current selection + if (i > 0 && tbl) { +@@ -1926,7 +1934,7 @@ void FileBrowser::selectImage(const Glib::ustring& fname, bool doScroll) + { + MYWRITERLOCK(l, entryRW); + +- if (!fd.empty() && !options.tabbedUI) { ++ if (!fd.empty() && !App::get().options().tabbedUI) { + for (size_t i = 0; i < fd.size(); i++) { + if (fname == fd[i]->filename && !fd[i]->filtered) { + // matching file found for sync +@@ -2058,6 +2066,8 @@ void FileBrowser::updateProfileList() + + subMenuList[0] = Gtk::manage (new Gtk::Menu ()); // adding the root submenu + ++ const auto& options = App::get().options(); ++ + // iterate the profile store's profile list + for (size_t i = 0; i < profEntries->size(); i++) { + // create a new label for the current entry (be it a folder or file) +@@ -2149,7 +2159,7 @@ void FileBrowser::openRequested( std::vector mselected) + { + std::vector entries; + // in Single Editor Mode open only last selected image +- size_t openStart = options.tabbedUI ? 0 : ( mselected.size() > 0 ? mselected.size() - 1 : 0); ++ size_t openStart = App::get().options().tabbedUI ? 0 : ( mselected.size() > 0 ? mselected.size() - 1 : 0); + + for (size_t i = openStart; i < mselected.size(); i++) { + entries.push_back (mselected[i]->thumbnail); +diff --git a/rtgui/filebrowserentry.cc b/rtgui/filebrowserentry.cc +index 85ec0fad3..bb672efda 100644 +--- a/rtgui/filebrowserentry.cc ++++ b/rtgui/filebrowserentry.cc +@@ -112,7 +112,7 @@ void FileBrowserEntry::refreshQuickThumbnailImage () + } + + // Only make a (slow) processed preview if the picture has been edited at all +- bool upgrade_to_processed = (!options.internalThumbIfUntouched || thumbnail->isPParamsValid()); ++ bool upgrade_to_processed = (!App::get().options().internalThumbIfUntouched || thumbnail->isPParamsValid()); + thumbImageUpdater->add(this, &updatepriority, upgrade_to_processed, false, this); + } + +@@ -176,7 +176,7 @@ void FileBrowserEntry::customBackBufferUpdate (Cairo::RefPtr c) + drawCrop (c, prex, prey, prew, preh, 0, 0, scale, *cropParams, true, false); + } else { + rtengine::procparams::CropParams cparams = thumbnail->getProcParams().crop; +- switch (options.cropGuides) { ++ switch (App::get().options().cropGuides) { + case Options::CROP_GUIDE_NONE: + cparams.guide = rtengine::procparams::CropParams::Guide::NONE; + break; +@@ -296,7 +296,7 @@ bool FileBrowserEntry::motionNotify (int x, int y) + + Inspector* inspector = parent->getInspector(); + +- if (inspector && inspector->isActive() && (!parent->isInTabMode() || options.inspectorWindow)) { ++ if (inspector && inspector->isActive() && (!parent->isInTabMode() || App::get().options().inspectorWindow)) { + const rtengine::Coord2D coord(getPosInImgSpace(x, y)); + + if (coord.x != -1.) { +diff --git a/rtgui/filecatalog.cc b/rtgui/filecatalog.cc +index ce22bbfd8..9d4acc624 100644 +--- a/rtgui/filecatalog.cc ++++ b/rtgui/filecatalog.cc +@@ -48,6 +48,83 @@ + + using namespace std; + ++namespace { ++ ++void getFilesRecursively( ++ const Glib::ustring &dir_path, ++ int max_depth, ++ int &dir_quota, ++ std::vector &file_names, ++ std::vector> *directories_explored) ++{ ++ const auto& options = App::get().options(); ++ try { ++ const auto dir = Gio::File::create_for_path(dir_path); ++ ++ static const auto enumerate_attrs = ++ std::string(G_FILE_ATTRIBUTE_STANDARD_NAME) + "," + ++ G_FILE_ATTRIBUTE_STANDARD_TYPE + "," + ++ G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN + "," + ++ G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET; ++ auto enumerator = dir->enumerate_children( ++ enumerate_attrs, ++ options.browseRecursiveFollowLinks ++ ? Gio::FileQueryInfoFlags::FILE_QUERY_INFO_NONE ++ : Gio::FileQueryInfoFlags::FILE_QUERY_INFO_NOFOLLOW_SYMLINKS); ++ ++ if (directories_explored) { ++ directories_explored->push_back(dir); ++ } ++ ++ while (true) { ++ try { ++ const auto file = enumerator->next_file(); ++ if (!file) { ++ break; ++ } ++ ++ if (!options.fbShowHidden && file->is_hidden()) { ++ continue; ++ } ++ ++ if (file->get_file_type() == Gio::FILE_TYPE_DIRECTORY) { ++ if (max_depth > 0 && dir_quota > 0) { ++ const Glib::ustring child_dir_path = Glib::build_filename(dir_path, file->get_name()); ++ getFilesRecursively(child_dir_path, max_depth - 1, --dir_quota, file_names, directories_explored); ++ } ++ continue; ++ } ++ ++ const Glib::ustring fname = file->get_name(); ++ const auto lastdot = fname.find_last_of('.'); ++ ++ if (lastdot >= fname.length() - 1) { ++ continue; ++ } ++ ++ const auto& extensions = options.parsedExtensionsSet; ++ if (extensions.find(fname.substr(lastdot + 1).lowercase()) == extensions.end()) { ++ continue; ++ } ++ ++ file_names.emplace_back(Glib::build_filename(dir_path, fname)); ++ } catch (const Glib::Exception& exception) { ++ if (rtengine::settings->verbose) { ++ std::cerr << exception.what() << std::endl; ++ } ++ } ++ } ++ ++ } catch (const Glib::Exception& exception) { ++ if (rtengine::settings->verbose) { ++ std::cerr << "Failed to list directory \"" << dir_path << "\": " << exception.what() << std::endl; ++ } ++ } ++} ++ ++ ++} // namespace ++ + FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb, FilePanel* filepanel) : + filepanel(filepanel), + selectedDirectoryId(1), +@@ -136,6 +213,8 @@ FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb, FilePanel* filepanel) : + Query->signal_activate().connect (sigc::mem_fun(*this, &FileCatalog::executeQuery)); //respond to the Enter key + Query->signal_key_press_event().connect(sigc::mem_fun(*this, &FileCatalog::Query_key_pressed)); + ++ const auto& options = App::get().options(); ++ + // if NOT a single row toolbar + if (!options.FileBrowserToolbarSingleRow) { + hbToolBar1STB = Gtk::manage(new MyScrolledToolbar()); +@@ -521,6 +600,7 @@ bool FileCatalog::capture_event(GdkEventButton* event) + + void FileCatalog::exifInfoButtonToggled() + { ++ auto& options = App::get().mut_options(); + if (inTabMode) { + options.filmStripShowFileNames = exifInfo->get_active(); + } else { +@@ -577,78 +657,9 @@ void FileCatalog::closeDir () + + std::vector FileCatalog::getFileList(std::vector> *dirs_explored) + { +- + std::vector names; + +- const std::set& extensions = options.parsedExtensionsSet; +- +- static void (*getFilesRecursively)(const Glib::ustring &, int, int &, std::vector &, std::vector> *) = [](const Glib::ustring &dir_path, int max_depth, int &dir_quota, std::vector &file_names, std::vector> * directories_explored) { +- try { +- +- const auto dir = Gio::File::create_for_path(dir_path); +- +- static const auto enumerate_attrs = +- std::string(G_FILE_ATTRIBUTE_STANDARD_NAME) + "," + +- G_FILE_ATTRIBUTE_STANDARD_TYPE + "," + +- G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN + "," + +- G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET; +- auto enumerator = dir->enumerate_children( +- enumerate_attrs, +- options.browseRecursiveFollowLinks +- ? Gio::FileQueryInfoFlags::FILE_QUERY_INFO_NONE +- : Gio::FileQueryInfoFlags::FILE_QUERY_INFO_NOFOLLOW_SYMLINKS); +- +- if (directories_explored) { +- directories_explored->push_back(dir); +- } +- +- while (true) { +- try { +- const auto file = enumerator->next_file(); +- if (!file) { +- break; +- } +- +- if (!options.fbShowHidden && file->is_hidden()) { +- continue; +- } +- +- if (file->get_file_type() == Gio::FILE_TYPE_DIRECTORY) { +- if (max_depth > 0 && dir_quota > 0) { +- const Glib::ustring child_dir_path = Glib::build_filename(dir_path, file->get_name()); +- getFilesRecursively(child_dir_path, max_depth - 1, --dir_quota, file_names, directories_explored); +- } +- continue; +- } +- +- const Glib::ustring fname = file->get_name(); +- const auto lastdot = fname.find_last_of('.'); +- +- if (lastdot >= fname.length() - 1) { +- continue; +- } +- +- if (extensions.find(fname.substr(lastdot + 1).lowercase()) == extensions.end()) { +- continue; +- } +- +- file_names.emplace_back(Glib::build_filename(dir_path, fname)); +- } catch (Glib::Exception& exception) { +- if (rtengine::settings->verbose) { +- std::cerr << exception.what() << std::endl; +- } +- } +- } +- +- } catch (Glib::Exception& exception) { +- +- if (rtengine::settings->verbose) { +- std::cerr << "Failed to list directory \"" << dir_path << "\": " << exception.what() << std::endl; +- } +- +- } +- }; +- ++ const auto& options = App::get().options(); + int dirs_left = options.browseRecursive ? options.browseRecursiveMaxDirs : 0; + getFilesRecursively(selectedDirectory, options.browseRecursiveDepth, dirs_left, names, dirs_explored); + +@@ -738,6 +749,7 @@ void FileCatalog::enableTabMode(bool enable) + { + inTabMode = enable; + ++ const auto& options = App::get().options(); + if (enable) { + if (options.showFilmStripToolBar) { + showToolBar(); +@@ -769,6 +781,7 @@ void FileCatalog::_refreshProgressBar () + if (!inTabMode && (!previewsToLoad || std::floor(100.f * previewsLoaded / previewsToLoad) != std::floor(100.f * (previewsLoaded - 1) / previewsToLoad))) { + GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected + ++ const auto& options = App::get().options(); + if (!progressImage || !progressLabel) { + // create tab label once + Gtk::Notebook *nb = (Gtk::Notebook *)(filepanel->get_parent()); +@@ -981,7 +994,7 @@ void FileCatalog::_openImage(const std::vector& tmb) + for (size_t i = 0; i < tmb.size() && continueToLoad; i++) { + // Open the image here, and stop if in Single Editor mode, or if an image couldn't + // be opened, would it be because the file doesn't exist or because of lack of RAM +- if( !(listener->fileSelected (tmb[i])) && !options.tabbedUI ) { ++ if( !(listener->fileSelected (tmb[i])) && !App::get().options().tabbedUI ) { + continueToLoad = false; + } + +@@ -1039,17 +1052,18 @@ void FileCatalog::deleteRequested(const std::vector& tbe, boo + // delete from file system + ::g_remove (fname.c_str ()); + // delete paramfile if found +- ::g_remove ((fname + paramFileExtension).c_str ()); +- ::g_remove ((removeExtension(fname) + paramFileExtension).c_str ()); ++ ::g_remove ((fname + App::PARAM_FILE_EXTENSION).c_str ()); ++ ::g_remove ((removeExtension(fname) + App::PARAM_FILE_EXTENSION).c_str ()); + // delete .thm file + ::g_remove ((removeExtension(fname) + ".thm").c_str ()); + ::g_remove ((removeExtension(fname) + ".THM").c_str ()); + + if (inclBatchProcessed) { ++ const auto& options = App::get().options(); + Glib::ustring procfName = Glib::ustring::compose ("%1.%2", BatchQueue::calcAutoFileNameBase(fname), options.saveFormatBatch.format); + ::g_remove (procfName.c_str ()); + +- Glib::ustring procfNameParamFile = Glib::ustring::compose ("%1.%2.out%3", BatchQueue::calcAutoFileNameBase(fname), options.saveFormatBatch.format, paramFileExtension); ++ Glib::ustring procfNameParamFile = Glib::ustring::compose ("%1.%2.out%3", BatchQueue::calcAutoFileNameBase(fname), options.saveFormatBatch.format, App::PARAM_FILE_EXTENSION); + ::g_remove (procfNameParamFile.c_str ()); + } + +@@ -1078,6 +1092,7 @@ void FileCatalog::copyMoveRequested(const std::vector& tbe, b + Gtk::FileChooserDialog fc (getToplevelWindow (this), fc_title, Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER ); + fc.add_button( M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL); + fc.add_button( M("GENERAL_OK"), Gtk::RESPONSE_OK); ++ auto& options = App::get().mut_options(); + if (!options.lastCopyMovePath.empty() && Glib::file_test(options.lastCopyMovePath, Glib::FILE_TEST_IS_DIR)) { + fc.set_current_folder(options.lastCopyMovePath); + } else { +@@ -1105,7 +1120,7 @@ void FileCatalog::copyMoveRequested(const std::vector& tbe, b + + // construct destination File Paths + Glib::ustring dest_fPath = Glib::build_filename (options.lastCopyMovePath, fname); +- Glib::ustring dest_fPath_param = dest_fPath + paramFileExtension; ++ Glib::ustring dest_fPath_param = dest_fPath + App::PARAM_FILE_EXTENSION; + + if (moveRequested && (src_Dir == options.lastCopyMovePath)) { + continue; +@@ -1138,17 +1153,17 @@ void FileCatalog::copyMoveRequested(const std::vector& tbe, b + + + // attempt to copy/move paramFile only if it exist next to the src +- Glib::RefPtr scr_param = Gio::File::create_for_path ( src_fPath + paramFileExtension ); ++ Glib::RefPtr scr_param = Gio::File::create_for_path ( src_fPath + App::PARAM_FILE_EXTENSION ); + +- if (Glib::file_test( src_fPath + paramFileExtension, Glib::FILE_TEST_EXISTS)) { ++ if (Glib::file_test( src_fPath + App::PARAM_FILE_EXTENSION, Glib::FILE_TEST_EXISTS)) { + Glib::RefPtr dest_param = Gio::File::create_for_path ( dest_fPath_param); + + // copy/move paramFile to destination + if (moveRequested) { +- if (Glib::file_test( dest_fPath + paramFileExtension, Glib::FILE_TEST_EXISTS)) { ++ if (Glib::file_test( dest_fPath + App::PARAM_FILE_EXTENSION, Glib::FILE_TEST_EXISTS)) { + // profile already got copied to destination from cache after cacheMgr->renameEntry + // delete source profile as cleanup +- ::g_remove ((src_fPath + paramFileExtension).c_str ()); ++ ::g_remove ((src_fPath + App::PARAM_FILE_EXTENSION).c_str ()); + } else { + scr_param->move(dest_param); + } +@@ -1163,7 +1178,7 @@ void FileCatalog::copyMoveRequested(const std::vector& tbe, b + Glib::ustring dest_fname = Glib::ustring::compose("%1%2%3%4%5", fname_noExt, "_", i_copyindex, ".", fname_Ext); + // re-construct destination File Paths + dest_fPath = Glib::build_filename (options.lastCopyMovePath, dest_fname); +- dest_fPath_param = dest_fPath + paramFileExtension; ++ dest_fPath_param = dest_fPath + App::PARAM_FILE_EXTENSION; + i_copyindex++; + } + }//while +@@ -1187,6 +1202,8 @@ void FileCatalog::developRequested(const std::vector& tbe, bo + Thumbnail* th = fbe->thumbnail; + rtengine::procparams::ProcParams params = th->getProcParams(); + ++ const auto& options = App::get().options(); ++ + // if fast mode is selected, override (disable) params + // controlling time and resource consuming tasks + // and also those which effect is not pronounced after reducing the image size +@@ -1352,7 +1369,7 @@ void FileCatalog::renameRequested(const std::vector& tbe) + + if (::g_rename (ofname.c_str (), nfname.c_str ()) == 0) { + cacheMgr->renameEntry (ofname, tbe[i]->thumbnail->getMD5(), nfname); +- ::g_remove((ofname + paramFileExtension).c_str ()); ++ ::g_remove((ofname + App::PARAM_FILE_EXTENSION).c_str ()); + reparseDirectory (); + } + } +@@ -1644,7 +1661,7 @@ void FileCatalog::categoryButtonToggled (Gtk::ToggleButton* b, bool isMouseClick + + void FileCatalog::showRecursiveToggled() + { +- options.browseRecursive = bRecursive->get_active(); ++ App::get().mut_options().browseRecursive = bRecursive->get_active(); + reparseDirectory(); + } + +@@ -1791,7 +1808,7 @@ void FileCatalog::reparseDirectory () + fileNamesToDel.push_back(entry->filename); + fileNamesToRemove.push_back(entry->filename); + } +- else if (!options.browseRecursive && Glib::path_get_dirname(entry->filename) != selectedDirectory) { ++ else if (!App::get().options().browseRecursive && Glib::path_get_dirname(entry->filename) != selectedDirectory) { + fileNamesToRemove.push_back(entry->filename); + } + } +@@ -1830,7 +1847,7 @@ void FileCatalog::reparseDirectory () + void FileCatalog::on_dir_changed (const Glib::RefPtr& file, const Glib::RefPtr& other_file, Gio::FileMonitorEvent event_type, bool internal) + { + +- if ((options.has_retained_extention(file->get_parse_name()) ++ if ((App::get().options().has_retained_extention(file->get_parse_name()) + && (event_type == Gio::FILE_MONITOR_EVENT_CREATED || event_type == Gio::FILE_MONITOR_EVENT_DELETED || event_type == Gio::FILE_MONITOR_EVENT_CHANGED)) + || (event_type == Gio::FILE_MONITOR_EVENT_CREATED && Glib::file_test(file->get_path(), Glib::FileTest::FILE_TEST_IS_DIR)) + || (event_type == Gio::FILE_MONITOR_EVENT_DELETED && std::find_if(dirMonitors.cbegin(), dirMonitors.cend(), [&file](const FileMonitorInfo &monitor) { return monitor.filePath == file->get_path(); }) != dirMonitors.cend())) { +@@ -1873,7 +1890,7 @@ void FileCatalog::addAndOpenFile (const Glib::ustring& fname) + + const auto lastdot = info->get_name().find_last_of('.'); + if (lastdot != Glib::ustring::npos) { +- if (!options.is_extention_enabled(info->get_name().substr(lastdot + 1))) { ++ if (!App::get().options().is_extention_enabled(info->get_name().substr(lastdot + 1))) { + return; + } + } else { +@@ -2153,6 +2170,7 @@ void FileCatalog::tbLeftPanel_1_toggled () + { + removeIfThere (filepanel->dirpaned, filepanel->placespaned, false); + ++ auto& options = App::get().mut_options(); + if (tbLeftPanel_1->get_active()) { + filepanel->dirpaned->pack1 (*filepanel->placespaned, false, true); + tbLeftPanel_1->set_image (*iLeftPanel_1_Hide); +@@ -2165,6 +2183,7 @@ void FileCatalog::tbLeftPanel_1_toggled () + + void FileCatalog::tbRightPanel_1_toggled () + { ++ auto& options = App::get().mut_options(); + if (tbRightPanel_1->get_active()) { + filepanel->rightBox->show(); + tbRightPanel_1->set_image (*iRightPanel_1_Hide); +@@ -2507,6 +2526,7 @@ bool FileCatalog::handleShortcutKey (GdkEventKey* event) + } + } + ++ auto& options = App::get().mut_options(); + if (!ctrl || (alt && !options.tabbedUI)) { + switch(event->keyval) { + +diff --git a/rtgui/filepanel.cc b/rtgui/filepanel.cc +index 1032b45e1..f069f099e 100644 +--- a/rtgui/filepanel.cc ++++ b/rtgui/filepanel.cc +@@ -32,6 +32,7 @@ + + FilePanel::FilePanel () : parent(nullptr), error(0) + { ++ const auto& options = App::get().options(); + + // Contains everything except for the batch Tool Panel and tabs (Fast Export, Inspect, etc) + dirpaned = Gtk::manage ( new Gtk::Paned () ); +@@ -186,6 +187,7 @@ void FilePanel::setAspect () + { + int winW, winH; + parent->get_size(winW, winH); ++ const auto& options = App::get().options(); + placespaned->set_position(options.dirBrowserHeight); + dirpaned->set_position(options.dirBrowserWidth); + tpcPaned->set_position(options.browserToolPanelHeight); +@@ -206,17 +208,18 @@ void FilePanel::init () + dirBrowser->fillDirTree (); + placesBrowser->refreshPlacesList (); + +- if (!argv1.empty() && Glib::file_test (argv1, Glib::FILE_TEST_EXISTS)) { +- Glib::ustring d(argv1); ++ if (!App::get().argv0().empty() && Glib::file_test (App::get().argv1(), Glib::FILE_TEST_EXISTS)) { ++ Glib::ustring d(App::get().argv1()); + if (!Glib::file_test(d, Glib::FILE_TEST_IS_DIR)) { + d = Glib::path_get_dirname(d); + } + dirBrowser->open(d); + } else { ++ const auto& options = App::get().options(); + if (options.startupDir == STARTUPDIR_HOME) { + dirBrowser->open (PlacesBrowser::userPicturesDir ()); + } else if (options.startupDir == STARTUPDIR_CURRENT) { +- dirBrowser->open (argv0); ++ dirBrowser->open (App::get().argv0()); + } else if (options.startupDir == STARTUPDIR_CUSTOM || options.startupDir == STARTUPDIR_LAST) { + if (options.startupPath.length() && Glib::file_test(options.startupPath, Glib::FILE_TEST_EXISTS) && Glib::file_test(options.startupPath, Glib::FILE_TEST_IS_DIR)) { + dirBrowser->open (options.startupPath); +@@ -246,7 +249,7 @@ bool FilePanel::fileSelected (Thumbnail* thm) + } + + // Check if it's already open BEFORE loading the file +- if (options.tabbedUI && parent->selectEditorPanel(thm->getFileName())) { ++ if (App::get().options().tabbedUI && parent->selectEditorPanel(thm->getFileName())) { + return true; + } + +@@ -294,6 +297,8 @@ bool FilePanel::imageLoaded( Thumbnail* thm, ProgressConnector 0 && pendingLoads.front()->complete) { + pendingLoad *pl = pendingLoads.front(); +@@ -363,6 +368,7 @@ MAXGDIHANDLESREACHED: + + void FilePanel::saveOptions () + { ++ auto& options = App::get().mut_options(); + + int winW, winH; + parent->get_size(winW, winH); +diff --git a/rtgui/filmnegative.cc b/rtgui/filmnegative.cc +index 73b940da2..fd2fd24cc 100644 +--- a/rtgui/filmnegative.cc ++++ b/rtgui/filmnegative.cc +@@ -55,6 +55,7 @@ Adjuster* createExponentAdjuster(AdjusterListener* listener, const Glib::ustring + adj->setAdjusterListener(listener); + adj->setLogScale(6, 1, true); + ++ const auto& options = App::get().options(); + adj->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + adj->show(); +@@ -68,6 +69,7 @@ Adjuster* createLevelAdjuster(AdjusterListener* listener, const Glib::ustring& l + adj->setAdjusterListener(listener); + // adj->setLogScale(6, 1000.0, true); + ++ const auto& options = App::get().options(); + adj->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + adj->show(); +@@ -82,6 +84,7 @@ Adjuster* createBalanceAdjuster(AdjusterListener* listener, const Glib::ustring& + adj->setAdjusterListener(listener); + adj->setLogScale(9, 0, true); + ++ const auto& options = App::get().options(); + adj->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + adj->show(); +diff --git a/rtgui/filmsimulation.cc b/rtgui/filmsimulation.cc +index a4a7e9bb7..769d55da6 100644 +--- a/rtgui/filmsimulation.cc ++++ b/rtgui/filmsimulation.cc +@@ -65,7 +65,7 @@ bool notifySlowParseDir (const std::chrono::system_clock::time_point& startedAt) + FilmSimulation::FilmSimulation() + : FoldableToolPanel( this, TOOL_NAME, M("TP_FILMSIMULATION_LABEL"), false, true ) + { +- m_clutComboBox = Gtk::manage( new ClutComboBox(options.clutsDir) ); ++ m_clutComboBox = Gtk::manage( new ClutComboBox(App::get().options().clutsDir) ); + + int foundClutsCount = m_clutComboBox->foundClutsCount(); + +@@ -132,6 +132,7 @@ void FilmSimulation::read( const rtengine::procparams::ProcParams* pp, const Par + + setEnabled(pp->filmSimulation.enabled); + ++ const auto& options = App::get().options(); + if (!pp->filmSimulation.clutFilename.empty()) { + m_clutComboBox->setSelectedClut( + !Glib::path_is_absolute(pp->filmSimulation.clutFilename) +@@ -185,7 +186,7 @@ void FilmSimulation::write( rtengine::procparams::ProcParams* pp, ParamsEdited* + const Glib::ustring clutFName = m_clutComboBox->getSelectedClut(); + + if (clutFName != "NULL") { // We do not want to set "NULL" in clutFilename, even if "unedited" +- pp->filmSimulation.clutFilename = stripPrefixDir(clutFName, options.clutsDir); ++ pp->filmSimulation.clutFilename = stripPrefixDir(clutFName, App::get().options().clutsDir); + } + + pp->filmSimulation.strength = m_strength->getValue(); +@@ -214,6 +215,7 @@ ClutComboBox::ClutComboBox(const Glib::ustring &path): + if (!cm) { + cm.reset(new ClutModel(path)); + } ++ const auto& options = App::get().options(); + if (!cm2 && options.multiDisplayMode) { + cm2.reset(new ClutModel(path)); + } +@@ -236,7 +238,7 @@ ClutComboBox::ClutComboBox(const Glib::ustring &path): + + inline Glib::RefPtr &ClutComboBox::m_model() + { +- if (!batchMode || !options.multiDisplayMode) { ++ if (!batchMode || !App::get().options().multiDisplayMode) { + return cm->m_model; + } else { + return cm2->m_model; +@@ -246,7 +248,7 @@ inline Glib::RefPtr &ClutComboBox::m_model() + + inline ClutComboBox::ClutColumns &ClutComboBox::m_columns() + { +- if (!batchMode || !options.multiDisplayMode) { ++ if (!batchMode || !App::get().options().multiDisplayMode) { + return cm->m_columns; + } else { + return cm2->m_columns; +diff --git a/rtgui/flatcurveeditorsubgroup.cc b/rtgui/flatcurveeditorsubgroup.cc +index afd9d8bd6..fbfecb6d0 100644 +--- a/rtgui/flatcurveeditorsubgroup.cc ++++ b/rtgui/flatcurveeditorsubgroup.cc +@@ -44,6 +44,7 @@ FlatCurveEditorSubGroup::FlatCurveEditorSubGroup (CurveEditorGroup* prt, Glib::u + valUnchanged = (int)FCT_Unchanged; + parent = prt; + ++ const auto& options = App::get().options(); + Gtk::PositionType sideStart = options.curvebboxpos == 0 || options.curvebboxpos == 2 ? Gtk::POS_LEFT : Gtk::POS_TOP; + Gtk::PositionType sideEnd = options.curvebboxpos == 0 || options.curvebboxpos == 2 ? Gtk::POS_RIGHT : Gtk::POS_BOTTOM; + +diff --git a/rtgui/flatfield.cc b/rtgui/flatfield.cc +index d12e39730..db94573d8 100644 +--- a/rtgui/flatfield.cc ++++ b/rtgui/flatfield.cc +@@ -38,6 +38,8 @@ FlatField::FlatField () : FoldableToolPanel(this, TOOL_NAME, M("TP_FLATFIELD_LAB + auto m = ProcEventMapper::getInstance(); + EvFlatFieldFromMetaData = m->newEvent(DARKFRAME, "HISTORY_MSG_FF_FROMMETADATA"); + ++ auto& options = App::get().mut_options(); ++ + hbff = Gtk::manage(new Gtk::Box()); + flatFieldFile = Gtk::manage(new MyFileChooserButton(M("TP_FLATFIELD_LABEL"), Gtk::FILE_CHOOSER_ACTION_OPEN)); + bindCurrentFolder (*flatFieldFile, options.lastFlatfieldDir); +@@ -332,6 +334,7 @@ void FlatField::flatFieldFileChanged() + void FlatField::flatFieldFile_Reset() + { + ffChanged = true; ++ const auto& options = App::get().options(); + + // caution: I had to make this hack, because set_current_folder() doesn't work correctly! + // Because szeva doesn't exist since he was committed to happy hunting ground in Issue 316 +diff --git a/rtgui/guiutils.cc b/rtgui/guiutils.cc +index 8645771ee..54f271f83 100644 +--- a/rtgui/guiutils.cc ++++ b/rtgui/guiutils.cc +@@ -222,6 +222,7 @@ void drawCrop (Cairo::RefPtr cr, int imx, int imy, int imw, int + double c2x = (cparams.x + cparams.w - startx) * scale - (fullImageVisible ? 0.0 : 1.0); + double c2y = (cparams.y + cparams.h - starty) * scale - (fullImageVisible ? 0.0 : 1.0); + ++ const auto& options = App::get().options(); + // crop overlay color, linked with crop windows background + if (options.bgcolor == 0 || !useBgColor) { + cr->set_source_rgba (options.cutOverlayBrush[0], options.cutOverlayBrush[1], options.cutOverlayBrush[2], options.cutOverlayBrush[3]); +@@ -715,7 +716,7 @@ bool MyExpander::on_enter_leave_enable (GdkEventCrossing* event) + + void MyExpander::updateStyle() + { +- updateVScrollbars(options.hideTPVScrollbar); ++ updateVScrollbars(App::get().options().hideTPVScrollbar); + + //GTK318 + #if GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 20 +@@ -942,7 +943,7 @@ MyScrolledWindow::MyScrolledWindow () + + bool MyScrolledWindow::on_scroll_event (GdkEventScroll* event) + { +- if (!options.hideTPVScrollbar) { ++ if (!App::get().options().hideTPVScrollbar) { + Gtk::ScrolledWindow::on_scroll_event (event); + return true; + } +diff --git a/rtgui/histogrampanel.cc b/rtgui/histogrampanel.cc +index 97c782677..6ee76948e 100644 +--- a/rtgui/histogrampanel.cc ++++ b/rtgui/histogrampanel.cc +@@ -36,14 +36,6 @@ constexpr float HistogramArea::MIN_BRIGHT; + + using ScopeType = Options::ScopeType; + +- +-namespace +-{ +- +-const rtengine::procparams::ColorManagementParams DEFAULT_CMP; +- +-} +- + // + // + // HistogramPanel +@@ -94,6 +86,7 @@ HistogramPanel::HistogramPanel () : + histogramRGBAreaVert.reset(new HistogramRGBAreaVert()); + setExpandAlignProperties(histogramRGBAreaVert.get(), false, true, Gtk::ALIGN_END, Gtk::ALIGN_FILL); + ++ const auto& options = App::get().options(); + switch (options.histogramScopeType) { + case ScopeType::NONE: + case ScopeType::HISTOGRAM_RAW: +@@ -440,7 +433,7 @@ void HistogramPanel::resized (Gtk::Allocation& req) + } + + // Store current height of the histogram +- options.histogramHeight = get_height(); ++ App::get().mut_options().histogramHeight = get_height(); + + old_height = req.get_height(); + old_width = req.get_width(); +@@ -451,7 +444,7 @@ void HistogramPanel::red_toggled () + // Update button image + showRed->set_image(showRed->get_active() ? *redImage : *redImage_g); + // Update options value +- options.histogramRed = showRed->get_active() ? true : false; ++ App::get().mut_options().histogramRed = showRed->get_active() ? true : false; + // Update drawing areas + rgbv_toggled(); + } +@@ -461,7 +454,7 @@ void HistogramPanel::green_toggled () + // Update button image + showGreen->set_image(showGreen->get_active() ? *greenImage : *greenImage_g); + // Update options value +- options.histogramGreen = showGreen->get_active() ? true : false; ++ App::get().mut_options().histogramGreen = showGreen->get_active() ? true : false; + // Update drawing areas + rgbv_toggled(); + } +@@ -471,7 +464,7 @@ void HistogramPanel::blue_toggled () + // Update button image + showBlue->set_image(showBlue->get_active() ? *blueImage : *blueImage_g); + // Update options value +- options.histogramBlue = showBlue->get_active() ? true : false; ++ App::get().mut_options().histogramBlue = showBlue->get_active() ? true : false; + // Update drawing areas + rgbv_toggled(); + } +@@ -480,7 +473,7 @@ void HistogramPanel::value_toggled () + // Update button image + showValue->set_image(showValue->get_active() ? *valueImage : *valueImage_g); + // Update options value +- options.histogramLuma = showValue->get_active() ? true : false; ++ App::get().mut_options().histogramLuma = showValue->get_active() ? true : false; + // Update drawing areas + rgbv_toggled(); + } +@@ -489,13 +482,14 @@ void HistogramPanel::chro_toggled () + // Update button image + showChro->set_image(showChro->get_active() ? *chroImage : *chroImage_g); + // Update options value +- options.histogramChroma = showChro->get_active() ? true : false; ++ App::get().mut_options().histogramChroma = showChro->get_active() ? true : false; + // Update drawing areas + rgbv_toggled(); + } + + void HistogramPanel::mode_released () + { ++ auto& options = App::get().mut_options(); + // Update options value + options.histogramDrawMode = (options.histogramDrawMode + 1) % 3; + // Update button image +@@ -514,19 +508,19 @@ void HistogramPanel::brightnessWidgetValueChanged(void) + { + ConnectionBlocker blocker(brightness_changed_connection); + histogramArea->setBrightness(exp(brightnessWidget->get_value())); +- options.histogramTraceBrightness = histogramArea->getBrightness(); ++ App::get().mut_options().histogramTraceBrightness = histogramArea->getBrightness(); + } + + void HistogramPanel::brightnessUpdated(float brightness) + { + brightnessWidget->set_value(log(brightness)); +- options.histogramTraceBrightness = histogramArea->getBrightness(); ++ App::get().mut_options().histogramTraceBrightness = histogramArea->getBrightness(); + } + + void HistogramPanel::scopeOptionsToggled() + { + // Update options value +- options.histogramShowOptionButtons = scopeOptions->get_active(); ++ App::get().mut_options().histogramShowOptionButtons = scopeOptions->get_active(); + // Show/hide secondary buttons column + optionButtons->set_visible(scopeOptions->get_active()); + } +@@ -550,6 +544,7 @@ void HistogramPanel::type_selected(Gtk::RadioButton* button) + new_type = ScopeType::VECTORSCOPE_HS; + } + ++ auto& options = App::get().mut_options(); + // Do not update if selected radio button is identical + if (new_type == options.histogramScopeType) { + return; +@@ -583,6 +578,7 @@ void HistogramPanel::type_selected(Gtk::RadioButton* button) + + void HistogramPanel::type_changed() + { ++ const auto& options = App::get().options(); + switch (options.histogramScopeType) { + case ScopeType::HISTOGRAM: + showRed->show(); +@@ -651,7 +647,7 @@ void HistogramPanel::bar_toggled () + // Update button image + showBAR->set_image(showBAR->get_active() ? *barImage : *barImage_g); + // Update options value +- options.histogramBar = showBAR->get_active() ? true : false; ++ App::get().mut_options().histogramBar = showBAR->get_active() ? true : false; + // Update drawing areas + rgbv_toggled(); + // Update histogram bar visibility +@@ -721,6 +717,7 @@ void HistogramPanel::reorder (Gtk::PositionType align) + // DrawModeListener interface: + void HistogramPanel::toggleButtonMode () + { ++ const auto& options = App::get().options(); + if (options.histogramDrawMode == 0) + showMode->set_image(*mode0Image); + else if (options.histogramDrawMode == 1) +@@ -734,7 +731,7 @@ void HistogramPanel::setPanelListener(HistogramPanelListener* listener) + panel_listener = listener; + + if (listener) { +- listener->scopeTypeChanged(options.histogramScopeType); ++ listener->scopeTypeChanged(App::get().options().histogramScopeType); + } + } + +@@ -756,10 +753,10 @@ HistogramRGBArea::HistogramRGBArea () : + // Saved pointer parameters + r(-1), g(-1), b(-1), lab_L(0.f), lab_a(0.f), lab_b(0.f), pointerValid(false), + // Drawing options (initialized at options file values) +- needRed(options.histogramRed), needGreen(options.histogramGreen), +- needBlue(options.histogramBlue), needLuma(options.histogramLuma), +- needChroma(options.histogramChroma), scopeType(options.histogramScopeType), +- scaleMode(options.histogramDrawMode), showBar(options.histogramBar) ++ needRed(App::get().options().histogramRed), needGreen(App::get().options().histogramGreen), ++ needBlue(App::get().options().histogramBlue), needLuma(App::get().options().histogramLuma), ++ needChroma(App::get().options().histogramChroma), scopeType(App::get().options().histogramScopeType), ++ scaleMode(App::get().options().histogramDrawMode), showBar(App::get().options().histogramBar) + { + get_style_context()->add_class("drawingarea"); + set_name("HistogramRGBArea"); +@@ -898,7 +895,7 @@ bool HistogramRGBArea::updatePointer (const int new_r, const int new_g, const in + static_cast(g), + static_cast(b), + lab_L, lab_a, lab_b, +- cmp != nullptr ? *cmp : DEFAULT_CMP, ++ cmp != nullptr ? *cmp : App::get().fallbackColorCmp(), + true); + pointerValid = true; + } +@@ -908,6 +905,7 @@ bool HistogramRGBArea::updatePointer (const int new_r, const int new_g, const in + + void HistogramRGBArea::updateFromOptions () + { ++ const auto& options = App::get().options(); + needRed = options.histogramRed; + needGreen = options.histogramGreen; + needBlue = options.histogramBlue; +@@ -960,7 +958,7 @@ void HistogramRGBArea::factorChanged (double newFactor) + void HistogramRGBAreaHori::drawBar(const Cairo::RefPtr &cc, const double value, const double max_value, const int winw, const int winh) + { + double pos; +- if (options.histogramDrawMode < 2) { ++ if (App::get().options().histogramDrawMode < 2) { + pos = padding + value * (winw - padding * 2.0) / max_value; + } else { + pos = padding + HistogramScaling::log (max_value, value) * (winw - padding * 2.0) / max_value; +@@ -998,6 +996,7 @@ void HistogramRGBAreaHori::get_preferred_width_for_height_vfunc (int height, int + void HistogramRGBAreaVert::drawBar(const Cairo::RefPtr &cc, const double value, const double max_value, const int winw, const int winh) + { + double pos; ++ const auto& options = App::get().options(); + if (options.histogramDrawMode < 2 || options.histogramScopeType == ScopeType::PARADE || options.histogramScopeType == ScopeType::WAVEFORM) { + pos = padding + value * (winh - padding * 2.0 - 1) / max_value + 0.5; + } else { +@@ -1055,11 +1054,14 @@ HistogramArea::HistogramArea (DrawModeListener *fml) : + pointer_a(0.f), pointer_b(0.f), + pointer_valid(false), + // Drawing options +- needRed(options.histogramRed), needGreen(options.histogramGreen), needBlue(options.histogramBlue), +- needLuma(options.histogramLuma), needChroma(options.histogramChroma), +- needPointer(options.histogramBar), +- scopeType(options.histogramScopeType), +- drawMode(options.histogramDrawMode), myDrawModeListener(fml), ++ needRed(App::get().options().histogramRed), ++ needGreen(App::get().options().histogramGreen), ++ needBlue(App::get().options().histogramBlue), ++ needLuma(App::get().options().histogramLuma), ++ needChroma(App::get().options().histogramChroma), ++ needPointer(App::get().options().histogramBar), ++ scopeType(App::get().options().histogramScopeType), ++ drawMode(App::get().options().histogramDrawMode), myDrawModeListener(fml), + // Motion event management + isPressed(false), movingPosition(0.0) + { +@@ -1108,6 +1110,7 @@ void HistogramArea::get_preferred_width_for_height_vfunc (int height, int &minim + + void HistogramArea::updateFromOptions () + { ++ const auto& options = App::get().options(); + wave_buffer_dirty = wave_buffer_dirty || needRed != options.histogramRed || + needGreen != options.histogramGreen || needBlue != options.histogramBlue; + +@@ -1426,7 +1429,7 @@ bool HistogramArea::updatePointer(const int r, const int g, const int b, const r + static_cast(g), + static_cast(b), + L, pointer_a, pointer_b, +- cmp != nullptr ? *cmp : DEFAULT_CMP, ++ cmp != nullptr ? *cmp : App::get().fallbackColorCmp(), + true); + L /= 327.68f; + pointer_a /= 327.68f; +@@ -1879,6 +1882,7 @@ bool HistogramArea::on_button_press_event (GdkEventButton* event) + ) { + + drawMode = (drawMode + 1) % 3; ++ auto& options = App::get().mut_options(); + options.histogramDrawMode = (options.histogramDrawMode + 1) % 3; + + if (myDrawModeListener) { +diff --git a/rtgui/hsvequalizer.cc b/rtgui/hsvequalizer.cc +index 2c6066442..ad24766dd 100644 +--- a/rtgui/hsvequalizer.cc ++++ b/rtgui/hsvequalizer.cc +@@ -45,7 +45,7 @@ HSVEqualizer::HSVEqualizer () : FoldableToolPanel(this, TOOL_NAME, M("TP_HSVEQUA + bottomMilestones.push_back( GradientMilestone(double(x), double(R), double(G), double(B)) ); + } + +- curveEditorG = new CurveEditorGroup (options.lastHsvCurvesDir, M("TP_HSVEQUALIZER_CHANNEL")); ++ curveEditorG = new CurveEditorGroup (App::get().mut_options().lastHsvCurvesDir, M("TP_HSVEQUALIZER_CHANNEL")); + curveEditorG->setCurveListener (this); + + hshape = static_cast(curveEditorG->addCurve(CT_Flat, M("TP_HSVEQUALIZER_HUE"))); +diff --git a/rtgui/iccprofilecreator.cc b/rtgui/iccprofilecreator.cc +index b7d3a1b2d..81994abd2 100644 +--- a/rtgui/iccprofilecreator.cc ++++ b/rtgui/iccprofilecreator.cc +@@ -53,21 +53,21 @@ cmsToneCurve *make_trc(size_t size, float (*trcFunc)(float, bool)) + + ICCProfileCreator::ICCProfileCreator(RTWindow *rtwindow) + : Gtk::Dialog(M("MAIN_BUTTON_ICCPROFCREATOR"), *rtwindow, true) +- , primariesPreset(options.ICCPC_primariesPreset) +- , redPrimaryX(options.ICCPC_redPrimaryX) +- , redPrimaryY(options.ICCPC_redPrimaryY) +- , greenPrimaryX(options.ICCPC_greenPrimaryX) +- , greenPrimaryY(options.ICCPC_greenPrimaryY) +- , bluePrimaryX(options.ICCPC_bluePrimaryX) +- , bluePrimaryY(options.ICCPC_bluePrimaryY) +- , gammaPreset(options.ICCPC_gammaPreset) +- , gamma(options.ICCPC_gamma) +- , slope(options.ICCPC_slope) +- , appendParamsToDesc(options.ICCPC_appendParamsToDesc) +- , profileVersion(options.ICCPC_profileVersion) +- , illuminant(options.ICCPC_illuminant) +- , description(options.ICCPC_description) +- , copyright(options.ICCPC_copyright) ++ , primariesPreset(App::get().options().ICCPC_primariesPreset) ++ , redPrimaryX(App::get().options().ICCPC_redPrimaryX) ++ , redPrimaryY(App::get().options().ICCPC_redPrimaryY) ++ , greenPrimaryX(App::get().options().ICCPC_greenPrimaryX) ++ , greenPrimaryY(App::get().options().ICCPC_greenPrimaryY) ++ , bluePrimaryX(App::get().options().ICCPC_bluePrimaryX) ++ , bluePrimaryY(App::get().options().ICCPC_bluePrimaryY) ++ , gammaPreset(App::get().options().ICCPC_gammaPreset) ++ , gamma(App::get().options().ICCPC_gamma) ++ , slope(App::get().options().ICCPC_slope) ++ , appendParamsToDesc(App::get().options().ICCPC_appendParamsToDesc) ++ , profileVersion(App::get().options().ICCPC_profileVersion) ++ , illuminant(App::get().options().ICCPC_illuminant) ++ , description(App::get().options().ICCPC_description) ++ , copyright(App::get().options().ICCPC_copyright) + , parent(rtwindow) + { + +@@ -154,6 +154,7 @@ ICCProfileCreator::ICCProfileCreator(RTWindow *rtwindow) + aGamma = Gtk::manage(new Adjuster(M("ICCPROFCREATOR_GAMMA"), 1, 3.5, 0.00001, 2.4)); + setExpandAlignProperties(aGamma, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE); + ++ const auto& options = App::get().options(); + aGamma->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + aGamma->show(); +@@ -429,6 +430,7 @@ void ICCProfileCreator::trcPresetsChanged() + + void ICCProfileCreator::storeValues() + { ++ auto& options = App::get().mut_options(); + if (iccVersion->get_active_text() == M("ICCPROFCREATOR_PROF_V4")) { + options.ICCPC_profileVersion = profileVersion = "v4"; + } else if (iccVersion->get_active_text() == M("ICCPROFCREATOR_PROF_V2")) { +@@ -683,6 +685,7 @@ void ICCProfileCreator::savePressed() + + //necessary for V2 profile + ++ auto& options = App::get().mut_options(); + if (!v2except) { + //used partially for v4, and in case of if we want to back to old manner for v2 + if (primariesPreset == "ACES-AP0" && rtengine::ICCStore::getInstance()->outputProfileExist(options.rtSettings.ACESp0)) { +diff --git a/rtgui/icmpanel.cc b/rtgui/icmpanel.cc +index ecd48082e..df72dc519 100644 +--- a/rtgui/icmpanel.cc ++++ b/rtgui/icmpanel.cc +@@ -84,6 +84,8 @@ ICMPanel::ICMPanel() : FoldableToolPanel(this, TOOL_NAME, M("TP_ICM_LABEL")), iu + EvICMresidtrc = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_ICM_RESIDTRC"); + EvICMwavExp = m->newEvent(LUMINANCECURVE, "HISTORY_MSG_ICM_WAVEXP"); + ++ auto& options = App::get().mut_options(); ++ + isBatchMode = lastToneCurve = lastApplyLookTable = lastApplyBaselineExposureOffset = lastApplyHueSatMap = false; + + ipDialog = Gtk::manage(new MyFileChooserButton(M("TP_ICM_INPUTDLGLABEL"), Gtk::FILE_CHOOSER_ACTION_OPEN)); +@@ -2703,7 +2705,7 @@ void ICMPanel::saveReferencePressed() + } + + Gtk::FileChooserDialog dialog(getToplevelWindow(this), M("TP_ICM_SAVEREFERENCE"), Gtk::FILE_CHOOSER_ACTION_SAVE); +- bindCurrentFolder(dialog, options.lastProfilingReferenceDir); ++ bindCurrentFolder(dialog, App::get().mut_options().lastProfilingReferenceDir); + dialog.set_current_name(lastRefFilename); + + dialog.add_button(M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL); +diff --git a/rtgui/imagearea.cc b/rtgui/imagearea.cc +index 209b1d0ab..94fe44c6b 100644 +--- a/rtgui/imagearea.cc ++++ b/rtgui/imagearea.cc +@@ -24,7 +24,6 @@ + #include "cropwindow.h" + #include "rtengine/refreshmap.h" + #include "rtengine/procparams.h" +-#include "options.h" + #include "rtscalable.h" + + ImageArea::ImageArea (ImageAreaPanel* p) : parent(p), fullImageWidth(0), fullImageHeight(0) +@@ -155,7 +154,7 @@ void ImageArea::setInfoText (Glib::ustring text) + + // update font + fontd.set_weight (Pango::WEIGHT_BOLD); +- const int fontSize = options.fontSize; ++ const int fontSize = App::get().options().fontSize; + // Non-absolute size is defined in "Pango units" and shall be multiplied by + // Pango::SCALE from "pt": + fontd.set_size (fontSize * Pango::SCALE); +@@ -195,7 +194,7 @@ void ImageArea::setInfoText (Glib::ustring text) + + void ImageArea::infoEnabled (bool e) + { +- ++ auto& options = App::get().mut_options(); + if (options.showInfo != e) { + options.showInfo = e; + queue_draw (); +@@ -250,7 +249,7 @@ bool ImageArea::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) + (*i)->expose (cr); + } + +- if (options.showInfo && !infotext.empty()) { ++ if (App::get().options().showInfo && !infotext.empty()) { + iBackBuffer.copySurface(cr); + } + +@@ -467,6 +466,7 @@ void ImageArea::addCropWindow () + cw->setCropGUIListener (cropgl); + cw->setPointerMotionListener (pmlistener); + cw->setPointerMotionHListener (pmhlistener); ++ const auto& options = App::get().options(); + int lastWidth = options.detailWindowWidth; + int lastHeight = options.detailWindowHeight; + +@@ -685,6 +685,7 @@ void ImageArea::initialImageArrived () + if (mainCropWindow) { + int w, h; + mainCropWindow->cropHandler.getFullImageSize(w, h); ++ const auto& options = App::get().options(); + if(options.prevdemo != PD_Sidecar || !options.rememberZoomAndPan || w != fullImageWidth || h != fullImageHeight) { + if (options.cropAutoFit || options.bgcolor != 0) { + mainCropWindow->zoomFitCrop(); +diff --git a/rtgui/indclippedpanel.cc b/rtgui/indclippedpanel.cc +index 72f68887f..bab73876a 100644 +--- a/rtgui/indclippedpanel.cc ++++ b/rtgui/indclippedpanel.cc +@@ -45,6 +45,7 @@ IndicateClippedPanel::IndicateClippedPanel (ImageArea* ia) : + indClippedH = Gtk::manage (new Gtk::ToggleButton ()); + indClippedH->set_relief(Gtk::RELIEF_NONE); + indClippedH->add (*Gtk::manage (new RTImage ("warning-highlights", Gtk::ICON_SIZE_LARGE_TOOLBAR))); ++ const auto& options = App::get().options(); + tt = Glib::ustring::compose("%1\n%2 = %3", M("MAIN_TOOLTIP_INDCLIPPEDH"), M("MAIN_TOOLTIP_THRESHOLD"), options.highlightThreshold); + + if (tt.find("<") == Glib::ustring::npos && tt.find(">") == Glib::ustring::npos) { +diff --git a/rtgui/inspector.cc b/rtgui/inspector.cc +index a01a6ce77..45b80372f 100644 +--- a/rtgui/inspector.cc ++++ b/rtgui/inspector.cc +@@ -87,6 +87,7 @@ Inspector::Inspector () : currImage(nullptr), scaled(false), scale(1.0), zoomSca + { + set_name("Inspector"); + ++ const auto& options = App::get().options(); + if (!options.inspectorWindow) { + window = nullptr; + } +@@ -321,6 +322,7 @@ bool Inspector::on_scroll_event(GdkEventScroll *event) + break; + } + ++ const auto& options = App::get().options(); + if ((options.zoomOnScroll && !alt) || (!options.zoomOnScroll && alt)) { + // zoom + beginZoom(event->x, event->y); +@@ -579,6 +581,7 @@ void Inspector::switchImage (const Glib::ustring &fullPath) + if (window && !window->get_visible()) + return; + ++ const auto& options = App::get().options(); + if (!options.inspectorDelay) { + doSwitchImage(); + } else { +@@ -591,6 +594,7 @@ bool Inspector::doSwitchImage() + { + Glib::ustring fullPath = next_image_path; + ++ const auto& options = App::get().options(); + // we first check the size of the list, it may have been changed in Preference + if (images.size() > size_t(options.maxInspectorBuffers)) { + // deleting the last entries +diff --git a/rtgui/labcurve.cc b/rtgui/labcurve.cc +index d45b71e07..98ab7a32c 100644 +--- a/rtgui/labcurve.cc ++++ b/rtgui/labcurve.cc +@@ -107,7 +107,7 @@ LCurve::LCurve() : FoldableToolPanel(this, TOOL_NAME, M("TP_LABCURVE_LABEL"), fa + hsep3->show(); + pack_start(*hsep3, Gtk::PACK_EXPAND_WIDGET, 4); + +- curveEditorG = new CurveEditorGroup(options.lastLabCurvesDir); ++ curveEditorG = new CurveEditorGroup(App::get().mut_options().lastLabCurvesDir); + curveEditorG->setCurveListener(this); + + lshape = static_cast(curveEditorG->addCurve(CT_Diagonal, "L*")); +diff --git a/rtgui/labgrid.cc b/rtgui/labgrid.cc +index 36e558a84..6af90600f 100644 +--- a/rtgui/labgrid.cc ++++ b/rtgui/labgrid.cc +@@ -593,6 +593,7 @@ bool LabGridArea::on_motion_notify_event(GdkEventMotion *event) + } + edited = true; + grab_focus(); ++ const auto& options = App::get().options(); + if (options.adjusterMinDelay == 0) { + notifyListener(); + } else { +diff --git a/rtgui/lensprofile.cc b/rtgui/lensprofile.cc +index 79f6313c9..a5f458523 100644 +--- a/rtgui/lensprofile.cc ++++ b/rtgui/lensprofile.cc +@@ -130,6 +130,7 @@ LensProfilePanel::LensProfilePanel() : + + const Glib::ustring defDir = LCPStore::getInstance()->getDefaultCommonDirectory(); + ++ auto& options = App::get().mut_options(); + if (!defDir.empty()) { + #ifdef _WIN32 + corrLcpFileChooser->set_show_hidden(true); // ProgramData is hidden on Windows +@@ -247,7 +248,7 @@ void LensProfilePanel::read(const rtengine::procparams::ProcParams* pp, const Pa + const Glib::ustring lastFolder = corrLcpFileChooser->get_current_folder(); + corrLcpFileChooser->set_current_folder(lastFolder); + corrLcpFileChooser->unselect_all(); +- bindCurrentFolder(*corrLcpFileChooser, options.lastLensProfileDir); ++ bindCurrentFolder(*corrLcpFileChooser, App::get().mut_options().lastLensProfileDir); + updateLCPDisabled(false); + } + else if (LCPStore::getInstance()->isValidLCPFileName(pp->lensProf.lcpFile)) { +diff --git a/rtgui/locallab.cc b/rtgui/locallab.cc +index d533727a0..9cb042680 100644 +--- a/rtgui/locallab.cc ++++ b/rtgui/locallab.cc +@@ -27,8 +27,6 @@ + using namespace rtengine; + using namespace procparams; + +-extern Options options; +- + const Glib::ustring Locallab::TOOL_NAME = "locallab"; + + /* ==== LocallabToolList ==== */ +@@ -231,7 +229,7 @@ Locallab::Locallab(): + + // Update Locallab tools advice tooltips visibility based on saved option + for (auto tool : locallabTools) { +- tool->updateAdviceTooltips(options.showtooltip); ++ tool->updateAdviceTooltips(App::get().options().showtooltip); + } + + // By default, if no photo is loaded, all Locallab tools are removed and it's not possible to add them +diff --git a/rtgui/locallabtools.cc b/rtgui/locallabtools.cc +index 31bf1e1a5..35e29d7c1 100644 +--- a/rtgui/locallabtools.cc ++++ b/rtgui/locallabtools.cc +@@ -74,7 +74,6 @@ void update_ghs_curve( + using namespace rtengine; + using namespace procparams; + +-extern Options options; + static double blurSlider2radius(double sval) + { + // Slider range: 0 - 1000 +@@ -241,7 +240,7 @@ void LocallabTool::addLocallabTool(bool raiseEvent) + if (needMode) { + // Set complexity mode according to chosen default one + complexityConn.block(true); +- complexity->set_active(options.complexity); ++ complexity->set_active(App::get().options().complexity); + complexityConn.block(false); + + // Update GUI accordingly +@@ -515,19 +514,19 @@ LocallabColor::LocallabColor(): + expcurvcol(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_EXPCURV")))), + labqualcurv(Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_QUALCURV_METHOD") + ":"))), + qualitycurveMethod(Gtk::manage(new MyComboBoxText())), +- llCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_LUM"))), ++ llCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_LUM"))), + llshape(static_cast(llCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + ccshape(static_cast(llCurveEditorG->addCurve(CT_Diagonal, "C(C)"))), +- clCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_CH"))), ++ clCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_CH"))), + clshape(static_cast(clCurveEditorG->addCurve(CT_Diagonal, "C(L)"))), + lcshape(static_cast(clCurveEditorG->addCurve(CT_Diagonal, "L(C)"))), +- HCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_HLH"))), ++ HCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_HLH"))), + LHshape(static_cast(HCurveEditorG->addCurve(CT_Flat, "L(h)", nullptr, false, true))), +- H3CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_HLH"))), ++ H3CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_HLH"))), + CHshape(static_cast(H3CurveEditorG->addCurve(CT_Flat, "C(h)", nullptr, false, true))), +- H2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_HLH"))), ++ H2CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_HLH"))), + HHshape(static_cast(H2CurveEditorG->addCurve(CT_Flat, "h(h)", nullptr, false, true))), +- rgbCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_RGB"))), ++ rgbCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_RGB"))), + toneMethod(Gtk::manage(new MyComboBoxText())), + rgbshape(static_cast(rgbCurveEditorG->addCurve(CT_Diagonal, "", toneMethod))), + special(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_SPECIAL")))), +@@ -546,8 +545,8 @@ LocallabColor::LocallabColor(): + showmaskcolMethod(Gtk::manage(new MyComboBoxText())), + showmaskcolMethodinv(Gtk::manage(new MyComboBoxText())), + enaColorMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), +-// maskCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), +- maskCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir,"", 1)), ++// maskCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), ++ maskCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir,"", 1)), + CCmaskshape(static_cast(maskCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskshape(static_cast(maskCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskshape(static_cast(maskCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -567,12 +566,12 @@ LocallabColor::LocallabColor(): + gammaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + shadmaskcol(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHAMASKCOL"), 0, 100, 1, 0))), +- // maskHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), +- maskHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "")), ++ // maskHCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), ++ maskHCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "")), + HHhmaskshape(static_cast(maskHCurveEditorG->addCurve(CT_Flat, "h(h)", nullptr, false, true))), +- mask2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskshape(static_cast(mask2CurveEditorG->addCurve(CT_Diagonal, "L(L)"))), +- mask2CurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), ++ mask2CurveEditorGwav(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), + LLmaskcolshapewav(static_cast(mask2CurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + csThresholdcol(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))) + { +@@ -2744,7 +2743,7 @@ LocallabExposure::LocallabExposure(): + shadex(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHADEX"), 0, 100, 1, 0))), + shcompr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHADEXCOMP"), 0, 100, 1, 50))), + expchroma(Gtk::manage(new Adjuster(M("TP_LOCALLAB_EXPCHROMA"), -50, 100, 1, 5))), +- curveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_CURVEEDITOR_TONES_LABEL"))), ++ curveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_CURVEEDITOR_TONES_LABEL"))), + shapeexpos(static_cast(curveEditorG->addCurve(CT_Diagonal, ""))), + exprecove(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_DENOI2_EXP")))), + maskusablee(Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_MASKUSABLE")))), +@@ -2764,8 +2763,8 @@ LocallabExposure::LocallabExposure(): + showmaskexpMethodinv(Gtk::manage(new MyComboBoxText())), + enaExpMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + enaExpMaskaft(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASKAFT")))), +- // maskexpCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), +- maskexpCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++ // maskexpCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++ maskexpCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskexpshape(static_cast(maskexpCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -2778,7 +2777,7 @@ LocallabExposure::LocallabExposure(): + gradFramemask(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GRADFRA")))), + strmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADSTR"), -2., 2., 0.05, 0.))), + angmaskexp(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GRADANG"), -180., 180., 0.1, 0.))), +- mask2expCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2expCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskexpshape(static_cast(mask2expCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + Evlocallabtmosatur(ProcEventMapper::getInstance()->newEvent(AUTOEXP, "HISTORY_MSG_LOCAL_TMO_SATUR")) + +@@ -4345,8 +4344,8 @@ LocallabShadow::LocallabShadow(): + showmaskSHMethod(Gtk::manage(new MyComboBoxText())), + showmaskSHMethodinv(Gtk::manage(new MyComboBoxText())), + enaSHMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), +-// maskSHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), +- maskSHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++// maskSHCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++ maskSHCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskSHshape(static_cast(maskSHCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -4356,7 +4355,7 @@ LocallabShadow::LocallabShadow(): + chromaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), +- mask2SHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2SHCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + LmaskSHshape(static_cast(mask2SHCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + fatSHFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_FATSHFRA")))), + fatamountSH(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATAMOUNT"), 1., 100., 1., 1.))), +@@ -6157,7 +6156,7 @@ LocallabVibrance::LocallabVibrance(): + pastSatTog(Gtk::manage(new Gtk::CheckButton(M("TP_VIBRANCE_PASTSATTOG")))), + sensiv(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SENSI"), 0, 100, 1, 30))),//reused - unused here, but used for normalize_mean_dt + previewvib(Gtk::manage(new Gtk::ToggleButton(M("TP_LOCALLAB_PREVIEW")))), +- curveEditorGG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL"))), ++ curveEditorGG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL"))), + skinTonesCurve(static_cast(curveEditorGG->addCurve(CT_Diagonal, M("TP_VIBRANCE_CURVEEDITOR_SKINTONES")))), + exprecovv(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_DENOI2_EXP")))), + maskusablev(Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_MASKUSABLE")))), +@@ -6175,8 +6174,8 @@ LocallabVibrance::LocallabVibrance(): + expmaskvib(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWVI")))), + showmaskvibMethod(Gtk::manage(new MyComboBoxText())), + enavibMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), +- // maskvibCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), +- maskvibCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++ // maskvibCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++ maskvibCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskvibshape(static_cast(maskvibCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -6186,7 +6185,7 @@ LocallabVibrance::LocallabVibrance(): + chromaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskvib(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), +- mask2vibCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2vibCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskvibshape(static_cast(mask2vibCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) + { + auto m = ProcEventMapper::getInstance(); +@@ -7694,7 +7693,7 @@ LocallabBlur::LocallabBlur(): + expdenoisenl(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_NLFRA")))), + expdenoiselum(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_DENOIWAVLUM")))), + expdenoisech(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_DENOIWAVCH")))), +- LocalcurveEditorwavden(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVDEN"))), ++ LocalcurveEditorwavden(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVDEN"))), + wavshapeden(static_cast(LocalcurveEditorwavden->addCurve(CT_Flat, "", nullptr, false, false))), + // lCLabels(Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_LCLABELS")))), + lCLabels(Gtk::manage(new Gtk::Label("-----------------"))), +@@ -7720,7 +7719,7 @@ LocallabBlur::LocallabBlur(): + noiselumdetail(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELUMDETAIL"), 0., 100., 0.01, 50.))), + noiselequal(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISELEQUAL"), -2, 10, 1, 7, Gtk::manage(new RTImage("circle-white-small")), Gtk::manage(new RTImage("circle-black-small"))))), + noisegam(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISEGAM"), 1.0, 5., 0.1, 1.))), +- LocalcurveEditorwavhue(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_WAVELET_DENOISEHUE"))), ++ LocalcurveEditorwavhue(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_WAVELET_DENOISEHUE"))), + wavhue(static_cast(LocalcurveEditorwavhue->addCurve(CT_Flat, "", nullptr, false, true))), + noisechrof(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHROFINE"), MINCHRO, MAXCHRO, 0.01, 0.))), + noisechroc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_NOISECHROCOARSE"), MINCHRO, MAXCHROCC, 0.01, 0.))), +@@ -7752,8 +7751,8 @@ LocallabBlur::LocallabBlur(): + showmaskblMethod(Gtk::manage(new MyComboBoxText())), + showmaskblMethodtyp(Gtk::manage(new MyComboBoxText())), + enablMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), +-// maskblCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), +- maskblCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++// maskblCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++ maskblCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmaskblshape(static_cast(maskblCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskblshape(static_cast(maskblCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskblshape(static_cast(maskblCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -7769,9 +7768,9 @@ LocallabBlur::LocallabBlur(): + slomaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + shadmaskbl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_HIGHMASKCOL"), 0, 100, 1, 0))), + shadmaskblsha(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHAMASKCOL"), 0, 100, 1, 0))), +- mask2blCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++ mask2blCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), + Lmaskblshape(static_cast(mask2blCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), +- mask2blCurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), ++ mask2blCurveEditorGwav(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), + LLmaskblshapewav(static_cast(mask2blCurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + quaHBox(Gtk::manage(new Gtk::Box())), + csThresholdblur(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))) +diff --git a/rtgui/locallabtools2.cc b/rtgui/locallabtools2.cc +index aabdd199a..9f982031e 100644 +--- a/rtgui/locallabtools2.cc ++++ b/rtgui/locallabtools2.cc +@@ -35,8 +35,6 @@ + using namespace rtengine; + using namespace procparams; + +-extern Options options; +- + static double retiSlider2neigh(double sval) + { + // Slider range: 0 - 5000 +@@ -144,8 +142,8 @@ LocallabTone::LocallabTone(): + showmasktmMethod(Gtk::manage(new MyComboBoxText())), + enatmMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + enatmMaskaft(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_AFTER_MASK")))), +-// masktmCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), +- masktmCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++// masktmCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++ masktmCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmasktmshape(static_cast(masktmCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmasktmshape(static_cast(masktmCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmasktmshape(static_cast(masktmCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -155,7 +153,7 @@ LocallabTone::LocallabTone(): + chromasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), + slomasktm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), +- mask2tmCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2tmCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmasktmshape(static_cast(mask2tmCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) + { + auto m = ProcEventMapper::getInstance(); +@@ -899,12 +897,12 @@ LocallabRetinex::LocallabRetinex(): + lightnessreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LIGHTRETI"), 0.01, 4.0, 0.01, 1.))), + cliptm(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CLIPTM"), 0.02, 2.0, 0.01, 1.))), + softradiusret(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRETI"), 0.0, 100.0, 0.5, 40.))), +- LocalcurveEditortransT(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_TRANSMISSIONMAP"))), ++ LocalcurveEditortransT(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_TRANSMISSIONMAP"))), + cTtransshape(static_cast(LocalcurveEditortransT->addCurve(CT_Flat, "", nullptr, false, false))), + mMLabels(Gtk::manage(new Gtk::Label("---"))), + transLabels(Gtk::manage(new Gtk::Label("---"))), + transLabels2(Gtk::manage(new Gtk::Label("---"))), +- LocalcurveEditorgainT(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_TRANSMISSIONGAIN"))), ++ LocalcurveEditorgainT(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_TRANSMISSIONGAIN"))), + cTgainshape(static_cast(LocalcurveEditorgainT->addCurve(CT_Flat, "", nullptr, false, false))), + exprecovr(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_DENOI2_EXP")))), + maskusabler(Gtk::manage(new Gtk::Label(M("TP_LOCALLAB_MASKUSABLE")))), +@@ -917,8 +915,8 @@ LocallabRetinex::LocallabRetinex(): + showmaskretiMethod(Gtk::manage(new MyComboBoxText())), + enaretiMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + enaretiMasktmap(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_TM_MASK")))), +-// maskretiCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), +- maskretiCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++// maskretiCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++ maskretiCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskretishape(static_cast(maskretiCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -928,7 +926,7 @@ LocallabRetinex::LocallabRetinex(): + chromaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.05, 5.0, 0.01, 1.))), + slomaskreti(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), +- mask2retiCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2retiCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskretishape(static_cast(mask2retiCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + inversret(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_INVERS")))) + { +@@ -2536,7 +2534,7 @@ LocallabContrast::LocallabContrast(): + contFrame(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_CONTWFRA")))), + sigmalc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + offslc(Gtk::manage(new Adjuster(M("TP_WAVELET_WAVOFFSET"), 0.33, 1.6, 0.01, 1.))), +- LocalcurveEditorwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAV"))), ++ LocalcurveEditorwav(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAV"))), + wavshape(static_cast(LocalcurveEditorwav->addCurve(CT_Flat, "", nullptr, false, false))), + csThreshold(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLD"), 0, 9, 0, 0, 7, 5, 0, false))), + processwav(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_PROCESSWAV")))), +@@ -2569,7 +2567,7 @@ LocallabContrast::LocallabContrast(): + wavedg(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_EDGFRA")))), + strengthw(Gtk::manage(new Adjuster(M("TP_WAVELET_EDVAL"), 0., 100.0, 0.5, 0.))), + sigmaed(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), +- LocalcurveEditorwavedg(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVEDG"))), ++ LocalcurveEditorwavedg(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVEDG"))), + wavshapeedg(static_cast(LocalcurveEditorwavedg->addCurve(CT_Flat, "", nullptr, false, false))), + gradw(Gtk::manage(new Adjuster(M("TP_WAVELET_EDGEDETECT"), 0., 100.0, 0.5, 90.))), + waveshow(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_EDGSHOW")))), +@@ -2586,7 +2584,7 @@ LocallabContrast::LocallabContrast(): + levelblur(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LEVELBLUR"), 0., 100., 0.5, 0.))), + sigmabl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + chromablu(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMABLU"), 0.0, 5., 0.1, 0.))), +- LocalcurveEditorwavlev(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVLEV"))), ++ LocalcurveEditorwavlev(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVLEV"))), + wavshapelev(static_cast(LocalcurveEditorwavlev->addCurve(CT_Flat, "", nullptr, false, false))), + residblur(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RESIDBLUR"), 0., 100., 0.5, 0.))), + blurlc(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_BLURLC")))), +@@ -2595,10 +2593,10 @@ LocallabContrast::LocallabContrast(): + sigma(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + offset(Gtk::manage(new Adjuster(M("TP_LOCALLAB_OFFSETWAV"), 0.33, 1.66, 0.01, 1., Gtk::manage(new RTImage("circle-black-small")), Gtk::manage(new RTImage("circle-white-small"))))), + chromalev(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMALEV"), 0.1, 5., 0.1, 1.))), +- LocalcurveEditorwavcon(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVCON"))), ++ LocalcurveEditorwavcon(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVCON"))), + wavshapecon(static_cast(LocalcurveEditorwavcon->addCurve(CT_Flat, "", nullptr, false, false))), + wavcompre(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_COMPREFRA")))), +- LocalcurveEditorwavcompre(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVCOMPRE"))), ++ LocalcurveEditorwavcompre(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVCOMPRE"))), + wavshapecompre(static_cast(LocalcurveEditorwavcompre->addCurve(CT_Flat, "", nullptr, false, false))), + sigmadr(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), + threswav(Gtk::manage(new Adjuster(M("TP_LOCALLAB_THRESWAV"), 0.9, 2., 0.01, 1.4))), +@@ -2606,7 +2604,7 @@ LocallabContrast::LocallabContrast(): + wavcomp(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_COMPFRA")))), + sigmadc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 3., 0.01, 1.))), + deltad(Gtk::manage(new Adjuster(M("TP_LOCALLAB_DELTAD"), -3., 3., 0.1, 0.))),//, Gtk::manage(new RTImage("circle-black-small")), Gtk::manage(new RTImage("circle-white-small"))))), +- LocalcurveEditorwavcomp(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVCOMP"))), ++ LocalcurveEditorwavcomp(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVCOMP"))), + wavshapecomp(static_cast(LocalcurveEditorwavcomp->addCurve(CT_Flat, "", nullptr, false, false))), + //fatres(Gtk::manage(new Adjuster(M("TP_LOCALLAB_FATRES"), 0., 100., 1., 0.))), + fftwlc(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_FFTW")))), +@@ -2621,15 +2619,15 @@ LocallabContrast::LocallabContrast(): + expmasklc(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWLC")))), + showmasklcMethod(Gtk::manage(new MyComboBoxText())), + enalcMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), +-// masklcCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), +- masklcCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++// masklcCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++ masklcCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmasklcshape(static_cast(masklcCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), + blendmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromasklc(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), +- mask2lcCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2lcCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmasklcshape(static_cast(mask2lcCurveEditorG->addCurve(CT_Diagonal, "L(L)"))) + { + auto m = ProcEventMapper::getInstance(); +@@ -4767,8 +4765,8 @@ decaycb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_MASKDDECAY"), 0.5, 4., 0.1, 2.)) + expmaskcb(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWCB")))), + showmaskcbMethod(Gtk::manage(new MyComboBoxText())), + enacbMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), +-// maskcbCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), +-maskcbCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++// maskcbCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK"))), ++maskcbCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskcbshape(static_cast(maskcbCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -4778,7 +4776,7 @@ lapmaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_LAPMASKCOL"), 0.0, 100.0, 0.1, + chromaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), + gammaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slomaskcb(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), +-mask2cbCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++mask2cbCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskcbshape(static_cast(mask2cbCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + + lumacontrastMinusButton(Gtk::manage(new Gtk::Button(M("TP_DIRPYREQUALIZER_LUMACONTRAST_MINUS")))), +@@ -5597,7 +5595,7 @@ LocallabLog::LocallabLog(): + saturl(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SATURV"), -100., 100., 0.5, 0.))), + chroml(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROML"), -100., 100., 0.5, 0.))), + expL(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_LOGEXP")))), +- //CurveEditorL(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_LOGCONTQ"))), ++ //CurveEditorL(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_LOGCONTQ"))), + //LshapeL(static_cast(CurveEditorL->addCurve(CT_Diagonal, "Q(Q)"))), + targabs(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOURCE_ABS"), 0.01, 16384.0, 0.01, 16.0))), + surround(Gtk::manage(new MyComboBoxText())), +@@ -5620,15 +5618,15 @@ LocallabLog::LocallabLog(): + expmaskL(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_SHOWC")))), + showmaskLMethod(Gtk::manage(new MyComboBoxText())), + enaLMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), +-// maskCurveEditorL(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), +- maskCurveEditorL(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++// maskCurveEditorL(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), ++ maskCurveEditorL(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmaskshapeL(static_cast(maskCurveEditorL->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskshapeL(static_cast(maskCurveEditorL->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskshapeL(static_cast(maskCurveEditorL->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), + blendmaskL(Gtk::manage(new Adjuster(M("TP_LOCALLAB_BLENDMASKCOL"), -100, 100, 1, 0))), + radmaskL(Gtk::manage(new Adjuster(M("TP_LOCALLAB_RADMASKCOL"), 0.0, 100.0, 0.1, 0.))), + chromaskL(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROMASKCOL"), -100.0, 100.0, 0.1, 0.))), +- mask2CurveEditorL(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2CurveEditorL(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + LmaskshapeL(static_cast(mask2CurveEditorL->addCurve(CT_Diagonal, "L(L)"))) + + +@@ -7101,8 +7099,8 @@ LocallabMask::LocallabMask(): + softradiusmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SOFTRADIUSCOL"), 0.0, 100.0, 0.5, 1.))), + showmask_Method(Gtk::manage(new MyComboBoxText())), + enamask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), +-// mask_CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), +- mask_CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++// mask_CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASKCOL"))), ++ mask_CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmask_shape(static_cast(mask_CurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -7120,11 +7118,11 @@ LocallabMask::LocallabMask(): + gammask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_GAMMASKCOL"), 0.25, 4.0, 0.01, 1.))), + slopmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + shadmask(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHAMASKCOL"), 0, 100, 1, 0))), +- mask_HCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), ++ mask_HCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASKH"))), + HHhmask_shape(static_cast(mask_HCurveEditorG->addCurve(CT_Flat, "h(h)", nullptr, false, true))), +- mask2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmask_shape(static_cast(mask2CurveEditorG->addCurve(CT_Diagonal, "L(L)"))), +- mask2CurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), ++ mask2CurveEditorGwav(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), + LLmask_shapewav(static_cast(mask2CurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + csThresholdmask(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))), + gradFramemask(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_GRADFRA")))), +@@ -8076,7 +8074,7 @@ Locallabcie::Locallabcie(): + expwavjz(Gtk::manage(new MyExpander(false, M("TP_LOCALLAB_JZWAVEXP")))), + contFramejz(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_CONTWFRA")))), + sigmalcjz(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SIGMAWAV"), 0.2, 2.5, 0.01, 1.))), +- LocalcurveEditorwavjz(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAV"))), ++ LocalcurveEditorwavjz(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAV"))), + wavshapejz(static_cast(LocalcurveEditorwavjz->addCurve(CT_Flat, "", nullptr, false, false))), + csThresholdjz(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLD"), 0, 9, 0, 0, 7, 4, 0, false))), + clariFramejz(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_CLARIFRA")))), +@@ -8209,24 +8207,24 @@ Locallabcie::Locallabcie(): + rstprotectcie(Gtk::manage(new Adjuster(M("TP_COLORAPP_RSTPRO"), 0., 100., 0.1, 0.))), + chromlcie(Gtk::manage(new Adjuster(M("TP_LOCALLAB_CHROML"), -100., 100., 0.5, 0.))), + huecie(Gtk::manage(new Adjuster(M("TP_LOCALLAB_HUECIE"), -100., 100., 0.1, 0.))), +- cieCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_CURVES_CIE"))), ++ cieCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_CURVES_CIE"))), + toneMethodcie(Gtk::manage(new MyComboBoxText())), + shapecie(static_cast(cieCurveEditorG->addCurve(CT_Diagonal, "", toneMethodcie))), +- cieCurveEditorG2(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_COLOR_CIE"))), ++ cieCurveEditorG2(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_COLOR_CIE"))), + toneMethodcie2(Gtk::manage(new MyComboBoxText())), + shapecie2(static_cast(cieCurveEditorG2->addCurve(CT_Diagonal, "", toneMethodcie2))), + + chromjzcie(Gtk::manage(new Adjuster(M("TP_LOCALLAB_JZCHROM"), -100., 100., 0.5, 0.))), + saturjzcie(Gtk::manage(new Adjuster(M("TP_LOCALLAB_JZSAT"), -100., 100., 0.5, 0.))), + huejzcie(Gtk::manage(new Adjuster(M("TP_LOCALLAB_JZHUECIE"), -100., 100., 0.1, 0.))), +- jz1CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++ jz1CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + shapejz(static_cast(jz1CurveEditorG->addCurve(CT_Diagonal, "Jz(J)"))), + shapecz(static_cast(jz1CurveEditorG->addCurve(CT_Diagonal, "Cz(C)"))), + + HFramejz(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_JZHFRA")))), + JzHFramejz(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_JZHJZFRA")))), +- jz2CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), +- jz3CurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++ jz2CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), ++ jz3CurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + shapeczjz(static_cast(jz1CurveEditorG->addCurve(CT_Diagonal, "Cz(J)"))), + HHshapejz(static_cast(jz3CurveEditorG->addCurve(CT_Flat, "Hz(Hz)", nullptr, false, true))), + CHshapejz(static_cast(jz3CurveEditorG->addCurve(CT_Flat, "Cz(Hz)", nullptr, false, true))), +@@ -8261,7 +8259,7 @@ Locallabcie::Locallabcie(): + showmaskcieMethod(Gtk::manage(new MyComboBoxText())), + enacieMask(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASK")))), + enacieMaskall(Gtk::manage(new Gtk::CheckButton(M("TP_LOCALLAB_ENABLE_MASKALL")))), +- maskcieCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "", 1)), ++ maskcieCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "", 1)), + CCmaskcieshape(static_cast(maskcieCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))), + LLmaskcieshape(static_cast(maskcieCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))), + HHmaskcieshape(static_cast(maskcieCurveEditorG->addCurve(CT_Flat, "LC(h)", nullptr, false, true))), +@@ -8280,12 +8278,12 @@ Locallabcie::Locallabcie(): + slomaskcie(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SLOMASKCOL"), 0.0, 15.0, 0.1, 0.))), + highmaskcie(Gtk::manage(new Adjuster(M("TP_LOCALLAB_HIGHMASKCOL"), 0, 100, 1, 0))), + shadmaskcie(Gtk::manage(new Adjuster(M("TP_LOCALLAB_SHAMASKCOL"), 0, 100, 1, 0))), +- maskcieHCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, "")), ++ maskcieHCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, "")), + HHhmaskcieshape(static_cast(maskcieHCurveEditorG->addCurve(CT_Flat, "h(h)", nullptr, false, true))), +- mask2cieCurveEditorG(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), ++ mask2cieCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_MASK2"))), + Lmaskcieshape(static_cast(mask2cieCurveEditorG->addCurve(CT_Diagonal, "L(L)"))), + wavFramecie(Gtk::manage(new Gtk::Frame(M("TP_LOCALLAB_TOOLMASK_2")))), +- mask2cieCurveEditorGwav(new CurveEditorGroup(options.lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), ++ mask2cieCurveEditorGwav(new CurveEditorGroup(App::get().mut_options().lastlocalCurvesDir, M("TP_LOCALLAB_WAVMASK"))), + LLmaskcieshapewav(static_cast(mask2cieCurveEditorGwav->addCurve(CT_Flat, "L(L)", nullptr, false, false))), + quaHcieBox(Gtk::manage(new Gtk::Box())), + csThresholdcie(Gtk::manage(new ThresholdAdjuster(M("TP_LOCALLAB_CSTHRESHOLDBLUR"), 0, 9, 0, 0, 6, 5, 0, false))) +diff --git a/rtgui/lockablecolorpicker.cc b/rtgui/lockablecolorpicker.cc +index 8c8d73418..206b51e19 100644 +--- a/rtgui/lockablecolorpicker.cc ++++ b/rtgui/lockablecolorpicker.cc +@@ -27,13 +27,6 @@ + #include "multilangmgr.h" + #include "navigator.h" + +-namespace +-{ +- +-const rtengine::procparams::ColorManagementParams DEFAULT_CMP; +- +-} +- + LockableColorPicker::LockableColorPicker (CropWindow* cropWindow, rtengine::procparams::ColorManagementParams *color_management_params) + : cropWindow(cropWindow), displayedValues(ColorPickerType::RGB), position(0, 0), size(Size::S15), + color_management_params(color_management_params), validity(Validity::OUTSIDE), +@@ -54,6 +47,7 @@ void LockableColorPicker::updateBackBuffer () + + Glib::RefPtr pangoContext = iArea->get_pango_context (); + Pango::FontDescription fontd = iArea->get_style_context()->get_font(); ++ const auto& options = App::get().options(); + // set font family and size + fontd.set_family(options.CPFontFamily == "default" ? "sans" : options.CPFontFamily); + const int fontSize = options.CPFontFamily == "default" ? 8 : options.CPFontSize; // pt +@@ -293,7 +287,8 @@ void LockableColorPicker::setRGB (const float R, const float G, const float B, c + static_cast(255 * g), + static_cast(255 * b), + L, a, bb, +- color_management_params != nullptr ? *color_management_params : DEFAULT_CMP, ++ color_management_params != nullptr ++ ? *color_management_params : App::get().fallbackColorCmp(), + true); + L /= 327.68f; + a /= 327.68f; +diff --git a/rtgui/main-cli.cc b/rtgui/main-cli.cc +index c754416a9..a03daaac7 100644 +--- a/rtgui/main-cli.cc ++++ b/rtgui/main-cli.cc +@@ -56,14 +56,6 @@ + // Set this to 1 to make RT work when started with Eclipse and arguments, at least on Windows platform + #define ECLIPSE_ARGS 0 + +-// stores path to data files +-Glib::ustring argv0; +-Glib::ustring creditsPath; +-Glib::ustring licensePath; +-Glib::ustring argv1; +-//bool simpleEditor; +-//Glib::Threads::Thread* mainThread; +- + namespace + { + +@@ -90,7 +82,7 @@ int main (int argc, char **argv) + + Gio::init (); + +- //mainThread = Glib::Threads::Thread::self(); ++ auto& app = App::get(); + + #ifdef BUILD_BUNDLE + char exname[512] = {0}; +@@ -106,38 +98,36 @@ int main (int argc, char **argv) + strncpy (exname, argv[0], 511); + } + +-#endif ++#endif // _WIN32 + exePath = Glib::path_get_dirname (exname); + + // set paths + if (Glib::path_is_absolute (DATA_SEARCH_PATH)) { +- argv0 = DATA_SEARCH_PATH; ++ app.setArgv0(DATA_SEARCH_PATH); + } else { +- argv0 = Glib::build_filename (exePath, DATA_SEARCH_PATH); ++ app.setArgv0(Glib::build_filename(exePath, DATA_SEARCH_PATH)); + } + + if (Glib::path_is_absolute (CREDITS_SEARCH_PATH)) { +- creditsPath = CREDITS_SEARCH_PATH; ++ app.setCreditsPath(CREDITS_SEARCH_PATH); + } else { +- creditsPath = Glib::build_filename (exePath, CREDITS_SEARCH_PATH); ++ app.setCreditsPath(Glib::build_filename(exePath, CREDITS_SEARCH_PATH)); + } + + if (Glib::path_is_absolute (LICENCE_SEARCH_PATH)) { +- licensePath = LICENCE_SEARCH_PATH; ++ app.setLicensePath(LICENCE_SEARCH_PATH); + } else { +- licensePath = Glib::build_filename (exePath, LICENCE_SEARCH_PATH); ++ app.setLicensePath(Glib::build_filename(exePath, LICENCE_SEARCH_PATH)); + } +- +- options.rtSettings.lensfunDbDirectory = LENSFUN_DB_PATH; +- options.rtSettings.lensfunDbBundleDirectory = LENSFUN_DB_PATH; +- + #else +- argv0 = DATA_SEARCH_PATH; +- creditsPath = CREDITS_SEARCH_PATH; +- licensePath = LICENCE_SEARCH_PATH; ++ app.setArgv0(DATA_SEARCH_PATH); ++ app.setCreditsPath(CREDITS_SEARCH_PATH); ++ app.setLicensePath(LICENCE_SEARCH_PATH); ++#endif // BUILD_BUNDLE ++ ++ Options& options = app.mut_options(); + options.rtSettings.lensfunDbDirectory = LENSFUN_DB_PATH; + options.rtSettings.lensfunDbBundleDirectory = LENSFUN_DB_PATH; +-#endif + + bool quickstart = dontLoadCache (argc, argv); + +@@ -256,6 +246,7 @@ int processLineParams ( int argc, char **argv ) + std::string outputType; + unsigned errors = 0; + ++ auto& options = App::get().mut_options(); + for ( int iArg = 1; iArg < argc; iArg++) { + Glib::ustring currParam (argv[iArg]); + if ( currParam.empty() ) { +@@ -482,7 +473,7 @@ int processLineParams ( int argc, char **argv ) + + if (sideProcParams && skipIfNoSidecar) { + // look for the sidecar proc params +- if (!Glib::file_test (fileName + paramFileExtension, Glib::FILE_TEST_EXISTS)) { ++ if (!Glib::file_test (fileName + App::PARAM_FILE_EXTENSION, Glib::FILE_TEST_EXISTS)) { + std::cout << "\"" << fileName << "\" has no side-car file. Image skipped." << std::endl; + continue; + } +@@ -504,7 +495,7 @@ int processLineParams ( int argc, char **argv ) + case 'h': + case '?': + default: { +- Glib::ustring pparamsExt = paramFileExtension.substr (1); ++ Glib::ustring pparamsExt = App::PARAM_FILE_EXTENSION.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; +@@ -579,9 +570,9 @@ int processLineParams ( int argc, char **argv ) + } + } + } else { +- argv1 = Glib::ustring (fname_to_utf8 (argv[iArg])); ++ App::get().setArgv1(Glib::ustring (fname_to_utf8 (argv[iArg]))); + #if ECLIPSE_ARGS +- argv1 = argv1.substr (1, argv1.length() - 2); ++ App::get().setArgv1(App::get().argv1().substr (1, App::get().argv1().length() - 2)); + #endif + + if ( outputDirectory ) { +@@ -623,7 +614,7 @@ int processLineParams ( int argc, char **argv ) + } + } + +- if ( !argv1.empty() ) { ++ if ( !App::get().argv1().empty() ) { + return 1; + } + +@@ -635,7 +626,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, Glib::path_get_basename (options.defProfRaw) + 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) + App::PARAM_FILE_EXTENSION)))) { + std::cerr << "Error: default raw processing profile not found." << std::endl; + rawParams->deleteInstance(); + delete rawParams; +@@ -646,7 +637,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, Glib::path_get_basename (options.defProfImg) + 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) + App::PARAM_FILE_EXTENSION)))) { + std::cerr << "Error: default non-raw processing profile not found." << std::endl; + imgParams->deleteInstance(); + delete imgParams; +@@ -750,7 +741,7 @@ int processLineParams ( int argc, char **argv ) + do { + if (sideProcParams && i == sideCarFilePos) { + // using the sidecar file +- Glib::ustring sideProcessingParams = inputFile + paramFileExtension; ++ Glib::ustring sideProcessingParams = inputFile + App::PARAM_FILE_EXTENSION; + + // the "load" method don't reset the procparams values anymore, so values found in the procparam file override the one of currentParams + if ( !Glib::file_test ( sideProcessingParams, Glib::FILE_TEST_EXISTS ) || currentParams.load ( sideProcessingParams )) { +@@ -811,7 +802,7 @@ int processLineParams ( int argc, char **argv ) + std::cerr << "Error saving to: " << outputFile << std::endl; + } else { + if ( copyParamsFile ) { +- Glib::ustring outputProcessingParams = outputFile + paramFileExtension; ++ Glib::ustring outputProcessingParams = outputFile + App::PARAM_FILE_EXTENSION; + currentParams.save ( outputProcessingParams ); + } + } +diff --git a/rtgui/main.cc b/rtgui/main.cc +index e11042e49..ded7076be 100644 +--- a/rtgui/main.cc ++++ b/rtgui/main.cc +@@ -61,15 +61,7 @@ + #define ECLIPSE_ARGS 0 + + // stores path to data files +-Glib::ustring argv0; +-Glib::ustring creditsPath; +-Glib::ustring licensePath; +-Glib::ustring argv1; + Glib::ustring argv2; +-bool simpleEditor = false; +-bool gimpPlugin = false; +-bool remote = false; +-//Glib::Threads::Thread* mainThread; + + namespace { + +@@ -104,6 +96,8 @@ static void myGdkLockLeave() + //int processLineParams ( int argc, char **argv ); + int processLineParams ( int argc, char **argv ) + { ++ auto& app = App::get(); ++ + int ret = 1; + for ( int iArg = 1; iArg < argc; iArg++) { + Glib::ustring currParam (argv[iArg]); +@@ -134,8 +128,8 @@ int processLineParams ( int argc, char **argv ) + #ifndef __APPLE__ // TODO agriggio - there seems to be already some "single instance app" support for OSX in rtwindow. Disabling it here until I understand how to merge the two + + case 'R': +- if (!gimpPlugin) { +- remote = true; ++ if (!app.isGimpPlugin()) { ++ app.setIsRemote(true); + } + + break; +@@ -143,9 +137,9 @@ int processLineParams ( int argc, char **argv ) + + case 'g': + if (currParam == "-gimp") { +- gimpPlugin = true; +- simpleEditor = true; +- remote = false; ++ app.setIsGimpPlugin(true); ++ app.setIsSimpleEditor(true); ++ app.setIsRemote(true); + break; + } + +@@ -180,17 +174,17 @@ int processLineParams ( int argc, char **argv ) + } + } + } else { +- if (argv1.empty()) { +- argv1 = Glib::ustring (fname_to_utf8 (argv[iArg])); ++ if (app.argv1().empty()) { ++ app.setArgv1(Glib::ustring (fname_to_utf8 (argv[iArg]))); + #if ECLIPSE_ARGS +- argv1 = argv1.substr (1, argv1.length() - 2); ++ app.setArgv1(app.argv1().substr (1, app.argv1().length() - 2)); + #endif +- } else if (gimpPlugin) { ++ } else if (app.isGimpPlugin()) { + argv2 = Glib::ustring (fname_to_utf8 (argv[iArg])); + break; + } + +- if (!gimpPlugin) { ++ if (!app.isGimpPlugin()) { + break; + } + } +@@ -211,6 +205,7 @@ bool init_rt() + + #ifndef _WIN32 + ++ const auto& options = App::get().options(); + // Move the old path to the new one if the new does not exist + if (Glib::file_test (Glib::build_filename (options.rtdir, "cache"), Glib::FILE_TEST_IS_DIR) && !Glib::file_test (options.cacheBaseDir, Glib::FILE_TEST_IS_DIR)) { + g_rename (Glib::build_filename (options.rtdir, "cache").c_str (), options.cacheBaseDir.c_str ()); +@@ -230,7 +225,7 @@ void cleanup_rt() + + RTWindow *create_rt_window() + { +- Glib::ustring icon_path = Glib::build_filename (argv0, "icons"); ++ Glib::ustring icon_path = Glib::build_filename (App::get().argv0(), "icons"); + Glib::RefPtr defaultIconTheme = Gtk::IconTheme::get_default(); + defaultIconTheme->append_search_path (icon_path); + +@@ -332,6 +327,7 @@ private: + + void show_gimp_plugin_info_dialog(Gtk::Window *parent) + { ++ auto& options = App::get().mut_options(); + if (options.gimpPluginShowInfoDialog) { + Gtk::MessageDialog info(*parent, M("GIMP_PLUGIN_INFO"), false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); + Gtk::Box *box = info.get_message_area(); +@@ -351,13 +347,6 @@ int main (int argc, char **argv) + setlocale (LC_ALL, ""); + setlocale (LC_NUMERIC, "C"); // to set decimal point to "." + +- simpleEditor = false; +- gimpPlugin = false; +- remote = false; +- argv0 = ""; +- argv1 = ""; +- argv2 = ""; +- + Glib::init(); // called by Gtk::Main, but this may be important for thread handling, so we call it ourselves now + Gio::init (); + +@@ -368,6 +357,8 @@ int main (int argc, char **argv) + } + #endif + ++ auto& app = App::get(); ++ + #ifdef BUILD_BUNDLE + char exname[512] = {0}; + Glib::ustring exePath; +@@ -382,38 +373,35 @@ int main (int argc, char **argv) + strncpy (exname, argv[0], 511); + } + +-#endif ++#endif // _WIN32 + exePath = Glib::path_get_dirname (exname); + + // set paths + if (Glib::path_is_absolute (DATA_SEARCH_PATH)) { +- argv0 = DATA_SEARCH_PATH; ++ app.setArgv0(DATA_SEARCH_PATH); + } else { +- argv0 = Glib::build_filename (exePath, DATA_SEARCH_PATH); ++ app.setArgv0(Glib::build_filename(exePath, DATA_SEARCH_PATH)); + } + + if (Glib::path_is_absolute (CREDITS_SEARCH_PATH)) { +- creditsPath = CREDITS_SEARCH_PATH; ++ app.setCreditsPath(CREDITS_SEARCH_PATH); + } else { +- creditsPath = Glib::build_filename (exePath, CREDITS_SEARCH_PATH); ++ app.setCreditsPath(Glib::build_filename(exePath, CREDITS_SEARCH_PATH)); + } + + if (Glib::path_is_absolute (LICENCE_SEARCH_PATH)) { +- licensePath = LICENCE_SEARCH_PATH; ++ app.setLicensePath(LICENCE_SEARCH_PATH); + } else { +- licensePath = Glib::build_filename (exePath, LICENCE_SEARCH_PATH); ++ app.setLicensePath(Glib::build_filename(exePath, LICENCE_SEARCH_PATH)); + } +- +- options.rtSettings.lensfunDbDirectory = LENSFUN_DB_PATH; +- options.rtSettings.lensfunDbBundleDirectory = LENSFUN_DB_PATH; +- + #else +- argv0 = DATA_SEARCH_PATH; +- creditsPath = CREDITS_SEARCH_PATH; +- licensePath = LICENCE_SEARCH_PATH; +- options.rtSettings.lensfunDbDirectory = LENSFUN_DB_PATH; +- options.rtSettings.lensfunDbBundleDirectory = LENSFUN_DB_PATH; +-#endif ++ app.setArgv0(DATA_SEARCH_PATH); ++ app.setCreditsPath(CREDITS_SEARCH_PATH); ++ app.setLicensePath(LICENCE_SEARCH_PATH); ++#endif // BUILD_BUNDLE ++ ++ app.mut_options().rtSettings.lensfunDbDirectory = LENSFUN_DB_PATH; ++ app.mut_options().rtSettings.lensfunDbBundleDirectory = LENSFUN_DB_PATH; + + #ifdef _WIN32 + bool consoleOpened = false; +@@ -422,7 +410,7 @@ int main (int argc, char **argv) + SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + + if (argc > 1) { +- if (!remote && !Glib::file_test (argv1, Glib::FILE_TEST_EXISTS ) && !Glib::file_test (argv1, Glib::FILE_TEST_IS_DIR)) { ++ if (!app.isRemote() && !Glib::file_test (app.argv1(), Glib::FILE_TEST_EXISTS ) && !Glib::file_test (app.argv1(), Glib::FILE_TEST_IS_DIR)) { + const bool stdoutRedirecttoConsole = (GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)) == 0x0000); + // open console, if stdout is invalid + if (stdoutRedirecttoConsole) { +@@ -499,8 +487,8 @@ int main (int argc, char **argv) + fatalError = e.get_msg(); + } + +- if (gimpPlugin) { +- if (!Glib::file_test (argv1, Glib::FILE_TEST_EXISTS) || Glib::file_test (argv1, Glib::FILE_TEST_IS_DIR)) { ++ if (app.isGimpPlugin()) { ++ if (!Glib::file_test (app.argv1(), Glib::FILE_TEST_EXISTS) || Glib::file_test (app.argv1(), Glib::FILE_TEST_IS_DIR)) { + printf ("Error: argv1 doesn't exist\n"); + return 1; + } +@@ -509,8 +497,8 @@ int main (int argc, char **argv) + printf ("Error: -gimp requires two arguments\n"); + return 1; + } +- } else if (!remote && Glib::file_test(argv1, Glib::FILE_TEST_EXISTS) && !Glib::file_test(argv1, Glib::FILE_TEST_IS_DIR)) { +- simpleEditor = true; ++ } else if (!app.isRemote() && Glib::file_test(app.argv1(), Glib::FILE_TEST_EXISTS) && !Glib::file_test(app.argv1(), Glib::FILE_TEST_IS_DIR)) { ++ app.setIsSimpleEditor(true); + } + + int ret = 0; +@@ -519,13 +507,13 @@ int main (int argc, char **argv) + gdk_threads_init(); + gtk_init (&argc, &argv); // use the "--g-fatal-warnings" command line flag to make warnings fatal + +- if (fatalError.empty() && remote) { +- char *app_argv[2] = { const_cast (argv0.c_str()) }; ++ if (fatalError.empty() && app.isRemote()) { ++ char *app_argv[2] = { const_cast (app.argv0().c_str()) }; + int app_argc = 1; + +- if (!argv1.empty()) { ++ if (!app.argv1().empty()) { + app_argc = 2; +- app_argv[1] = const_cast (argv1.c_str()); ++ app_argv[1] = const_cast (app.argv1().c_str()); + } + + RTApplication app; +@@ -535,13 +523,13 @@ int main (int argc, char **argv) + Gtk::Main m (&argc, &argv); + gdk_threads_enter(); + const std::unique_ptr rtWindow (create_rt_window()); +- if (gimpPlugin) { ++ if (app.isGimpPlugin()) { + show_gimp_plugin_info_dialog(rtWindow.get()); + } + m.run (*rtWindow); + gdk_threads_leave(); + +- if (gimpPlugin && rtWindow->epanel && rtWindow->epanel->isRealized()) { ++ if (app.isGimpPlugin() && rtWindow->epanel && rtWindow->epanel->isRealized()) { + if (!rtWindow->epanel->saveImmediately(argv2, SaveFormat())) { + ret = -2; + } +diff --git a/rtgui/navigator.cc b/rtgui/navigator.cc +index cdff5f70b..8307708e8 100644 +--- a/rtgui/navigator.cc ++++ b/rtgui/navigator.cc +@@ -27,17 +27,10 @@ + + using namespace rtengine; + +-namespace +-{ +- +-const rtengine::procparams::ColorManagementParams DEFAULT_CMP; +- +-} +- + Navigator::Navigator() : + pointer_moved_delayed_call(50, 100), +- currentRGBUnit(options.navRGBUnit), +- currentHSVUnit(options.navHSVUnit) ++ currentRGBUnit(App::get().options().navRGBUnit), ++ currentHSVUnit(App::get().options().navHSVUnit) + { + pointer_moved_delayed_call.setFunction( + [this](bool validPos, const rtengine::procparams::ColorManagementParams *cmp, int x, int y, int r, int g, int b, bool isRaw) +@@ -74,7 +67,7 @@ Navigator::Navigator() : + static_cast(g), + static_cast(b), + LAB_l, LAB_a, LAB_b, +- cmp != nullptr ? *cmp : DEFAULT_CMP, ++ cmp != nullptr ? *cmp : App::get().fallbackColorCmp(), + true); + LAB_l /= 327.68f; + LAB_a /= 327.68f; +@@ -356,7 +349,7 @@ void Navigator::cycleUnitsRGB (GdkEventButton *event) { + if (v == (uint16_t)Options::NavigatorUnit::_COUNT) { + v = 0; + } +- options.navRGBUnit = currentRGBUnit = (Options::NavigatorUnit)v; ++ App::get().mut_options().navRGBUnit = currentRGBUnit = (Options::NavigatorUnit)v; + + switch (currentRGBUnit) { + case Options::NavigatorUnit::R0_1: +@@ -385,7 +378,7 @@ void Navigator::cycleUnitsHSV (GdkEventButton *event) { + if (v == (uint16_t)Options::NavigatorUnit::_COUNT) { + v = 0; + } +- options.navHSVUnit = currentHSVUnit = (Options::NavigatorUnit)v; ++ App::get().mut_options().navHSVUnit = currentHSVUnit = (Options::NavigatorUnit)v; + + switch (currentHSVUnit) { + case Options::NavigatorUnit::R0_1: +diff --git a/rtgui/options.cc b/rtgui/options.cc +index 46d5f4edb..7e3d4127f 100644 +--- a/rtgui/options.cc ++++ b/rtgui/options.cc +@@ -56,10 +56,6 @@ Glib::ustring Options::rtdir; + // User's cached data directory + Glib::ustring Options::cacheBaseDir; + +-Options options; +-Glib::ustring versionString = RTVERSION; +-Glib::ustring paramFileExtension = ".pp3"; +- + Options::Options() + { + +@@ -77,13 +73,13 @@ inline bool Options::checkProfilePath(Glib::ustring &path) + + Glib::ustring p = getUserProfilePath(); + +- if (!p.empty() && Glib::file_test(path + paramFileExtension, Glib::FILE_TEST_EXISTS)) { ++ if (!p.empty() && Glib::file_test(path + App::PARAM_FILE_EXTENSION, Glib::FILE_TEST_EXISTS)) { + return true; + } + + p = getGlobalProfilePath(); + +- return !p.empty() && Glib::file_test(path + paramFileExtension, Glib::FILE_TEST_EXISTS); ++ return !p.empty() && Glib::file_test(path + App::PARAM_FILE_EXTENSION, Glib::FILE_TEST_EXISTS); + } + + bool Options::checkDirPath(Glib::ustring &path, Glib::ustring errString) +@@ -120,7 +116,7 @@ void Options::updatePaths() + + if (checkDirPath(profilePath, "Error: the user's processing profile path doesn't point to a directory or doesn't exist!\n")) { + userProfilePath = profilePath; +- tmpPath = Glib::build_filename(argv0, "profiles"); ++ tmpPath = Glib::build_filename(App::get().argv0(), "profiles"); + + if (checkDirPath(tmpPath, "Error: the global's processing profile path doesn't point to a directory or doesn't exist!\n")) { + if (userProfilePath != tmpPath) { +@@ -128,7 +124,7 @@ void Options::updatePaths() + } + } + } else { +- tmpPath = Glib::build_filename(argv0, "profiles"); ++ tmpPath = Glib::build_filename(App::get().argv0(), "profiles"); + + if (checkDirPath(tmpPath, "Error: the global's processing profile path doesn't point to a directory or doesn't exist!\n")) { + globalProfilePath = tmpPath; +@@ -151,7 +147,7 @@ void Options::updatePaths() + userProfilePath = tmpPath; + } + +- tmpPath = Glib::build_filename(argv0, "profiles"); ++ tmpPath = Glib::build_filename(App::get().argv0(), "profiles"); + + if (checkDirPath(tmpPath, "Error: the user's processing profile path doesn't point to a directory or doesn't exist!\n")) { + globalProfilePath = tmpPath; +@@ -226,7 +222,7 @@ void Options::updatePaths() + } + } + +-Glib::ustring Options::getPreferredProfilePath() ++Glib::ustring Options::getPreferredProfilePath() const + { + if (!userProfilePath.empty()) { + return userProfilePath; +@@ -244,7 +240,7 @@ Glib::ustring Options::getPreferredProfilePath() + *@return Send back the absolute path of the given filename or "Neutral" if "Neutral" has been set to profName. Implementer 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. + */ +-Glib::ustring Options::findProfilePath(Glib::ustring &profName) ++Glib::ustring Options::findProfilePath(Glib::ustring &profName) const + { + if (profName.empty()) { + return ""; +@@ -263,7 +259,7 @@ Glib::ustring Options::findProfilePath(Glib::ustring &profName) + if (p == "${U}") { + // the path starts by the User virtual path + p = getUserProfilePath(); +- Glib::ustring fullPath = Glib::build_filename(p, profName.substr(5) + paramFileExtension); ++ Glib::ustring fullPath = Glib::build_filename(p, profName.substr(5) + App::PARAM_FILE_EXTENSION); + + if (!p.empty() && Glib::file_test(fullPath, Glib::FILE_TEST_EXISTS)) { + return Glib::path_get_dirname(fullPath); +@@ -271,7 +267,7 @@ Glib::ustring Options::findProfilePath(Glib::ustring &profName) + } else if (p == "${G}") { + // the path starts by the User virtual path + p = getGlobalProfilePath(); +- Glib::ustring fullPath = Glib::build_filename(p, profName.substr(5) + paramFileExtension); ++ Glib::ustring fullPath = Glib::build_filename(p, profName.substr(5) + App::PARAM_FILE_EXTENSION); + + if (!p.empty() && Glib::file_test(fullPath, Glib::FILE_TEST_EXISTS)) { + return Glib::path_get_dirname(fullPath); +@@ -279,7 +275,7 @@ Glib::ustring Options::findProfilePath(Glib::ustring &profName) + } else { + // compatibility case -> convert the path to the new format + p = getUserProfilePath(); +- Glib::ustring fullPath = Glib::build_filename(p, profName + paramFileExtension); ++ Glib::ustring fullPath = Glib::build_filename(p, profName + App::PARAM_FILE_EXTENSION); + + if (!p.empty() && Glib::file_test(fullPath, Glib::FILE_TEST_EXISTS)) { + // update the profile path +@@ -288,7 +284,7 @@ Glib::ustring Options::findProfilePath(Glib::ustring &profName) + } + + p = getGlobalProfilePath(); +- fullPath = Glib::build_filename(p, profName + paramFileExtension); ++ fullPath = Glib::build_filename(p, profName + App::PARAM_FILE_EXTENSION); + + if (!p.empty() && Glib::file_test(fullPath, Glib::FILE_TEST_EXISTS)) { + profName = Glib::build_filename("${G}", profName); +@@ -527,43 +523,6 @@ void Options::setDefaults() + ICCPC_copyright = Options::getICCProfileCopyright(); + ICCPC_appendParamsToDesc = false; + +- fastexport_bypass_sharpening = true; +- fastexport_bypass_sharpenEdge = true; +- fastexport_bypass_sharpenMicro = true; +- //fastexport_bypass_lumaDenoise = true; +- //fastexport_bypass_colorDenoise = true; +- fastexport_bypass_defringe = true; +- fastexport_bypass_dirpyrDenoise = true; +- fastexport_bypass_dirpyrequalizer = true; +- fastexport_bypass_wavelet = true; +- fastexport_raw_bayer_method = "fast"; +- //fastexport_bypass_raw_bayer_all_enhance = true; +- fastexport_bypass_raw_bayer_dcb_iterations = true; +- fastexport_bypass_raw_bayer_dcb_enhance = true; +- fastexport_bypass_raw_bayer_lmmse_iterations = true; +- fastexport_bypass_raw_bayer_linenoise = true; +- fastexport_bypass_raw_bayer_greenthresh = true; +- fastexport_raw_xtrans_method = "fast"; +- fastexport_bypass_raw_ccSteps = true; +- fastexport_bypass_raw_ca = true; +- fastexport_bypass_raw_df = true; +- fastexport_bypass_raw_ff = true; +- fastexport_icm_input_profile = "(camera)"; +- fastexport_icm_working_profile = "ProPhoto"; +- fastexport_icm_output_profile = options.rtSettings.srgb; +- fastexport_icm_outputIntent = rtengine::RI_RELATIVE; +- fastexport_icm_outputBPC = true; +- fastexport_resize_enabled = true; +- fastexport_resize_scale = 1; +- fastexport_resize_appliesTo = "Cropped area"; +- fastexport_resize_method = "Lanczos"; +- fastexport_resize_dataspec = 3; +- fastexport_resize_width = 900; +- fastexport_resize_height = 900; +- fastexport_resize_longedge = 900; +- fastexport_resize_shortedge = 900; +- fastexport_use_fast_pipeline = true; +- + clutsDir = "./cluts"; + + cutOverlayBrush = std::vector (4); +@@ -679,6 +638,45 @@ void Options::setDefaults() + // rtSettings.decaction =0.3; + // rtSettings.ciebadpixgauss=false; + rtSettings.rgbcurveslumamode_gamut = true; ++ ++ fastexport_bypass_sharpening = true; ++ fastexport_bypass_sharpenEdge = true; ++ fastexport_bypass_sharpenMicro = true; ++ //fastexport_bypass_lumaDenoise = true; ++ //fastexport_bypass_colorDenoise = true; ++ fastexport_bypass_defringe = true; ++ fastexport_bypass_dirpyrDenoise = true; ++ fastexport_bypass_dirpyrequalizer = true; ++ fastexport_bypass_wavelet = true; ++ fastexport_raw_bayer_method = "fast"; ++ //fastexport_bypass_raw_bayer_all_enhance = true; ++ fastexport_bypass_raw_bayer_dcb_iterations = true; ++ fastexport_bypass_raw_bayer_dcb_enhance = true; ++ fastexport_bypass_raw_bayer_lmmse_iterations = true; ++ fastexport_bypass_raw_bayer_linenoise = true; ++ fastexport_bypass_raw_bayer_greenthresh = true; ++ fastexport_raw_xtrans_method = "fast"; ++ fastexport_bypass_raw_ccSteps = true; ++ fastexport_bypass_raw_ca = true; ++ fastexport_bypass_raw_df = true; ++ fastexport_bypass_raw_ff = true; ++ fastexport_icm_input_profile = "(camera)"; ++ fastexport_icm_working_profile = "ProPhoto"; ++ // Must be set after options.rtSettings.srgb has already been initialized! ++ fastexport_icm_output_profile = rtSettings.srgb; ++ fastexport_icm_outputIntent = rtengine::RI_RELATIVE; ++ fastexport_icm_outputBPC = true; ++ fastexport_resize_enabled = true; ++ fastexport_resize_scale = 1; ++ fastexport_resize_appliesTo = "Cropped area"; ++ fastexport_resize_method = "Lanczos"; ++ fastexport_resize_dataspec = 3; ++ fastexport_resize_width = 900; ++ fastexport_resize_height = 900; ++ fastexport_resize_longedge = 900; ++ fastexport_resize_shortedge = 900; ++ fastexport_use_fast_pipeline = true; ++ + lastIccDir = rtSettings.iccDirectory; + lastDarkframeDir = rtSettings.darkFramesPath; + lastFlatfieldDir = rtSettings.flatFieldsPath; +@@ -969,7 +967,7 @@ void Options::readFromFile(Glib::ustring fname) + if (keyFile.has_key("External Editor", "GimpDir")) { + gimpDir = keyFile.get_string("External Editor", "GimpDir"); + } +- auto executable = Glib::build_filename(options.gimpDir, "bin", "gimp-win-remote"); ++ auto executable = Glib::build_filename(gimpDir, "bin", "gimp-win-remote"); + if (Glib::file_test(executable, Glib::FILE_TEST_IS_EXECUTABLE)) { + if (editorToSendTo == 1) { + externalEditorIndex = externalEditors.size(); +@@ -2375,7 +2373,7 @@ void Options::readFromFile(Glib::ustring fname) + } catch (Glib::Error &err) { + Glib::ustring msg = Glib::ustring::compose("Options::readFromFile / Error code %1 while reading values from \"%2\":\n%3", err.code(), fname, err.what()); + +- if (options.rtSettings.verbose) { ++ if (App::get().options().rtSettings.verbose) { + printf("%s\n", msg.c_str()); + } + +@@ -2383,7 +2381,7 @@ void Options::readFromFile(Glib::ustring fname) + } catch (...) { + Glib::ustring msg = Glib::ustring::compose("Options::readFromFile / Unknown exception while trying to load \"%1\"!", fname); + +- if (options.rtSettings.verbose) { ++ if (App::get().options().rtSettings.verbose) { + printf("%s\n", msg.c_str()); + } + +@@ -2873,7 +2871,6 @@ void Options::saveToFile(Glib::ustring fname) + + void Options::load(bool lightweight) + { +- + // Find the application data path + + const gchar* path; +@@ -2908,22 +2905,23 @@ void Options::load(bool lightweight) + #endif + } + ++ auto& options = App::get().mut_options(); + if (options.rtSettings.verbose) { + printf("Settings directory (rtdir) = %s\n", rtdir.c_str()); + } + + // Set the cache folder in RT's base folder +- cacheBaseDir = Glib::build_filename(argv0, "mycache"); ++ cacheBaseDir = Glib::build_filename(App::get().argv0(), "mycache"); + + // Read the global option file (the one located in the application's base folder) + try { +- options.readFromFile(Glib::build_filename(argv0, "options")); ++ options.readFromFile(Glib::build_filename(App::get().argv0(), "options")); + } catch (Options::Error &) { + // ignore errors here + } + + if (!options.multiUser && path == nullptr) { +- rtdir = Glib::build_filename(argv0, "mysettings"); ++ rtdir = Glib::build_filename(App::get().argv0(), "mysettings"); + } + + // Modify the path of the cache folder to the one provided in RT_CACHE environment variable. +@@ -3039,7 +3037,7 @@ void Options::load(bool lightweight) + // out which are the parent translations. Furthermore, there must be a file for each locale () -- you cannot have + // 'French (CA)' unless there is a file 'French'. + +- Glib::ustring defaultTranslation = Glib::build_filename(argv0, "languages", "default"); ++ Glib::ustring defaultTranslation = Glib::build_filename(App::get().argv0(), "languages", "default"); + Glib::ustring languageTranslation = ""; + Glib::ustring localeTranslation = ""; + +@@ -3051,29 +3049,29 @@ void Options::load(bool lightweight) + std::vector langPortions = Glib::Regex::split_simple(" ", options.language); + + if (langPortions.size() >= 1) { +- languageTranslation = Glib::build_filename(argv0, "languages", langPortions.at(0)); ++ languageTranslation = Glib::build_filename(App::get().argv0(), "languages", langPortions.at(0)); + } + + if (langPortions.size() >= 2) { +- localeTranslation = Glib::build_filename(argv0, "languages", options.language); ++ localeTranslation = Glib::build_filename(App::get().argv0(), "languages", options.language); + } + } + + langMgr.load(options.language, {localeTranslation, languageTranslation, defaultTranslation}); + +- rtengine::init(&options.rtSettings, argv0, rtdir, !lightweight); ++ rtengine::init(&options.rtSettings, App::get().argv0(), rtdir, !lightweight); + } + + void Options::save() + { + +- options.saveToFile(Glib::build_filename(rtdir, "options")); ++ App::get().mut_options().saveToFile(Glib::build_filename(rtdir, "options")); + } + + /* + * return true if ext is a parsed extension (retained or not) + */ +-bool Options::is_parse_extention(Glib::ustring fname) ++bool Options::is_parse_extention(Glib::ustring fname) const + { + Glib::ustring ext = getExtension(fname).lowercase(); + +@@ -3094,14 +3092,14 @@ bool Options::is_parse_extention(Glib::ustring fname) + /* + * return true if fname ends with one of the retained image file extensions + */ +-bool Options::has_retained_extention(const Glib::ustring& fname) ++bool Options::has_retained_extention(const Glib::ustring& fname) const + { + return parsedExtensionsSet.find(getExtension(fname).lowercase()) != parsedExtensionsSet.end(); + } + + // Pattern matches "5.1" from "5.1-23-g12345678", when comparing option.version to RTVERSION +-bool Options::is_new_version() { +- const std::string vs[] = {versionString, version}; ++bool Options::is_new_version() const { ++ const std::string vs[] = {App::VERSION, version}; + std::vector vMajor; + + for (const auto& v : vs) { +@@ -3114,26 +3112,26 @@ bool Options::is_new_version() { + /* + * return true if ext is an enabled extension + */ +-bool Options::is_extention_enabled(const Glib::ustring& ext) ++bool Options::is_extention_enabled(const Glib::ustring& ext) const + { + return parsedExtensionsSet.find(ext.lowercase()) != parsedExtensionsSet.end(); + } + +-Glib::ustring Options::getUserProfilePath() ++Glib::ustring Options::getUserProfilePath() const + { + return userProfilePath; + } + +-Glib::ustring Options::getGlobalProfilePath() ++Glib::ustring Options::getGlobalProfilePath() const + { + return globalProfilePath; + } + +-bool Options::is_defProfRawMissing() ++bool Options::is_defProfRawMissing() const + { + return defProfError & rtengine::toUnderlying(DefProfError::defProfRawMissing); + } +-bool Options::is_defProfImgMissing() ++bool Options::is_defProfImgMissing() const + { + return defProfError & rtengine::toUnderlying(DefProfError::defProfImgMissing); + } +@@ -3153,11 +3151,11 @@ void Options::setDefProfImgMissing(bool value) + defProfError &= ~rtengine::toUnderlying(DefProfError::defProfImgMissing); + } + } +-bool Options::is_bundledDefProfRawMissing() ++bool Options::is_bundledDefProfRawMissing() const + { + return defProfError & rtengine::toUnderlying(DefProfError::bundledDefProfRawMissing); + } +-bool Options::is_bundledDefProfImgMissing() ++bool Options::is_bundledDefProfImgMissing() const + { + return defProfError & rtengine::toUnderlying(DefProfError::bundledDefProfImgMissing); + } +diff --git a/rtgui/options.h b/rtgui/options.h +index 0abed72df..e13bf661d 100644 +--- a/rtgui/options.h ++++ b/rtgui/options.h +@@ -29,6 +29,7 @@ + #else + #include + #endif ++#include "rtengine/rtapp.h" + #include "rtengine/settings.h" + #include + +@@ -532,30 +533,21 @@ public: + static void save(); + + // if multiUser=false, send back the global profile path +- Glib::ustring getPreferredProfilePath(); +- Glib::ustring getUserProfilePath(); +- Glib::ustring getGlobalProfilePath(); +- Glib::ustring findProfilePath (Glib::ustring &profName); +- bool is_parse_extention (Glib::ustring fname); +- bool has_retained_extention (const Glib::ustring& fname); +- bool is_new_version(); +- bool is_extention_enabled (const Glib::ustring& ext); +- bool is_defProfRawMissing(); +- bool is_bundledDefProfRawMissing(); +- bool is_defProfImgMissing(); +- bool is_bundledDefProfImgMissing(); ++ Glib::ustring getPreferredProfilePath() const; ++ Glib::ustring getUserProfilePath() const; ++ Glib::ustring getGlobalProfilePath() const; ++ Glib::ustring findProfilePath (Glib::ustring &profName) const; ++ bool is_parse_extention (Glib::ustring fname) const; ++ bool has_retained_extention (const Glib::ustring& fname) const; ++ bool is_new_version() const; ++ bool is_extention_enabled (const Glib::ustring& ext) const; ++ bool is_defProfRawMissing() const; ++ bool is_bundledDefProfRawMissing() const; ++ bool is_defProfImgMissing() const; ++ bool is_bundledDefProfImgMissing() const; + void setDefProfRawMissing (bool value); + void setBundledDefProfRawMissing (bool value); + void setDefProfImgMissing (bool value); + void setBundledDefProfImgMissing (bool value); + static Glib::ustring getICCProfileCopyright(); + }; +- +-extern Options options; +-extern Glib::ustring argv0; +-extern Glib::ustring argv1; +-extern bool simpleEditor; +-extern bool gimpPlugin; +-extern bool remote; +-extern Glib::ustring versionString; +-extern Glib::ustring paramFileExtension; +diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc +index a5e5ae12e..028f64918 100644 +--- a/rtgui/paramsedited.cc ++++ b/rtgui/paramsedited.cc +@@ -84,6 +84,8 @@ void initFrom(FramingParamsEdited& edits, const ProcParams& params, const ProcPa + void combine(FramingParams& toEdit, const FramingParams& mod, const FramingParamsEdited& edits, + bool dontForceSet) + { ++ const auto& options = App::get().options(); ++ + if (edits.enabled) { + toEdit.enabled = mod.enabled; + } +@@ -2469,6 +2471,7 @@ void ParamsEdited::initFrom(const std::vector& + + void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rtengine::procparams::ProcParams& mods, bool forceSet) + { ++ const Options& options = App::get().options(); + + bool dontforceSet = !forceSet; + +diff --git a/rtgui/pdsharpening.cc b/rtgui/pdsharpening.cc +index 504644378..397b88d01 100644 +--- a/rtgui/pdsharpening.cc ++++ b/rtgui/pdsharpening.cc +@@ -83,6 +83,7 @@ PdSharpening::PdSharpening() : + dradiusOffset->setAdjusterListener(this); + diter->setAdjusterListener(this); + ++ const auto& options = App::get().options(); + contrast->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + dradius->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + dradiusOffset->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); +diff --git a/rtgui/placesbrowser.cc b/rtgui/placesbrowser.cc +index 1f7a47f15..d83680483 100644 +--- a/rtgui/placesbrowser.cc ++++ b/rtgui/placesbrowser.cc +@@ -106,6 +106,7 @@ void PlacesBrowser::refreshPlacesList () + { + placesModel->clear (); + ++ const auto& options = App::get().options(); + // append favorites + for (size_t i = 0; i < options.favoriteDirs.size(); i++) { + Glib::RefPtr fav = Gio::File::create_for_path (options.favoriteDirs[i]); +@@ -314,6 +315,7 @@ void PlacesBrowser::addPressed () + return; + } + ++ auto& options = App::get().mut_options(); + // check if the dirname is already in the list. If yes, return. + for (size_t i = 0; i < options.favoriteDirs.size(); i++) + if (options.favoriteDirs[i] == lastSelectedDir) { +@@ -340,6 +342,7 @@ void PlacesBrowser::delPressed () + Glib::RefPtr selection = treeView->get_selection(); + Gtk::TreeModel::iterator iter = selection->get_selected(); + ++ auto& options = App::get().mut_options(); + if (iter && iter->get_value (placesColumns.type) == 5) { + std::vector::iterator i = std::find (options.favoriteDirs.begin(), options.favoriteDirs.end(), iter->get_value (placesColumns.root)); + +diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc +index 3cc003bb0..28e67da5a 100644 +--- a/rtgui/preferences.cc ++++ b/rtgui/preferences.cc +@@ -60,7 +60,6 @@ void placeSpinBox(Gtk::Container* where, Gtk::SpinButton* &spin, const std::stri + } + } + +-extern Glib::ustring argv0; + Glib::RefPtr themecss; + Glib::RefPtr fontcss; + +@@ -76,8 +75,8 @@ Preferences::Preferences(RTWindow *rtwindow) + , toolLocationPreference(nullptr) + , swFavorites(nullptr) + { +- +- moptions.copyFrom(&options); ++ const auto& options = App::get().options(); ++ moptions.copyFrom(&App::get().mut_options()); + + set_size_request(650, -1); + set_default_size(options.preferencesWidth, options.preferencesHeight); +@@ -145,8 +144,8 @@ Preferences::Preferences(RTWindow *rtwindow) + + Preferences::~Preferences() + { +- + ProfileStore::getInstance()->removeListener(this); ++ auto& options = App::get().mut_options(); + get_size(options.preferencesWidth, options.preferencesHeight); + } + +@@ -1228,11 +1227,11 @@ Gtk::Widget* Preferences::getGeneralPanel() + setExpandAlignProperties(languages, false, false, Gtk::ALIGN_START, Gtk::ALIGN_BASELINE); + + std::vector langs; +- parseDir(argv0 + "/languages", langs, ""); ++ parseDir(App::get().argv0() + "/languages", langs, ""); + + for (const auto &lang : langs) { + if ("default" != lang && "README" != lang && "LICENSE" != lang) { +- auto lang_metadata = langMgr.getMetadata(Glib::build_filename(argv0 + "/languages", lang)); ++ auto lang_metadata = langMgr.getMetadata(Glib::build_filename(App::get().argv0() + "/languages", lang)); + const auto &display_name = + lang_metadata != nullptr + ? Glib::ustring(lang_metadata->getLanguageName(lang)) +@@ -1265,7 +1264,7 @@ Gtk::Widget* Preferences::getGeneralPanel() + + themeCBT = Gtk::manage(new Gtk::ComboBoxText()); + themeCBT->set_active(0); +- parseThemeDir(Glib::build_filename(argv0, "themes")); ++ parseThemeDir(Glib::build_filename(App::get().argv0(), "themes")); + for (size_t i = 0; i < themeNames.size(); i++) { + themeCBT->append(themeNames.at(i)); + } +@@ -1273,6 +1272,7 @@ Gtk::Widget* Preferences::getGeneralPanel() + Gtk::Label* mainFontLbl = Gtk::manage(new Gtk::Label(M("PREFERENCES_APPEARANCE_MAINFONT"))); + setExpandAlignProperties(mainFontLbl, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER); + ++ const auto& options = App::get().options(); + mainFontFB = Gtk::manage(new Gtk::FontButton()); + mainFontFB->set_use_size(true); + if (options.fontFamily == "default") { +@@ -2172,6 +2172,7 @@ void Preferences::fillPreferences() + navGuideColorCB->set_rgba (NavGuideCol); + navGuideColorCB->set_alpha ( (unsigned short) (moptions.navGuideBrush[3] * 65535.0)); + ++ const auto& options = App::get().options(); + if (options.fontFamily == "default") { + mainFontFB->set_font_name (Glib::ustring::compose ("%1, %2", initialFontFamily, initialFontSize)); + } else { +@@ -2389,7 +2390,7 @@ void Preferences::langAutoDetectToggled() + + void Preferences::okPressed() + { +- ++ auto& options = App::get().mut_options(); + storePreferences(); + workflowUpdate(); + options.copyFrom(&moptions); +@@ -2408,6 +2409,7 @@ void Preferences::okPressed() + + void Preferences::cancelPressed() + { ++ auto& options = App::get().mut_options(); + // set the initial theme back + if (themeNames.at (themeCBT->get_active_row_number ()) != options.theme) { + switchThemeTo(options.theme); +@@ -2508,6 +2510,7 @@ void Preferences::bundledProfilesChanged() + rpconn.block(true); + ipconn.block(true); + ++ auto& options = App::get().mut_options(); + // parseProfiles does use options.useBundledProfiles, so we temporarily change its value + bool currValue = options.useBundledProfiles; + options.useBundledProfiles = useBundledProfiles->get_active(); +@@ -2581,7 +2584,7 @@ void Preferences::restoreValue() + void Preferences::switchThemeTo(Glib::ustring newTheme) + { + +- Glib::ustring filename(Glib::build_filename(argv0, "themes", newTheme + ".css")); ++ Glib::ustring filename(Glib::build_filename(App::get().argv0(), "themes", newTheme + ".css")); + + if (!themecss) { + themecss = Gtk::CssProvider::create(); +@@ -2635,6 +2638,7 @@ void Preferences::switchFontTo(const Glib::ustring &newFontFamily, const int new + + void Preferences::workflowUpdate() + { ++ const auto& options = App::get().options(); + + if (moptions.tabbedUI != options.tabbedUI) { + parent->setEditorMode(moptions.tabbedUI); +diff --git a/rtgui/preprocess.cc b/rtgui/preprocess.cc +index 507ce6681..444a868c9 100644 +--- a/rtgui/preprocess.cc ++++ b/rtgui/preprocess.cc +@@ -30,7 +30,7 @@ using namespace rtengine::procparams; + + const Glib::ustring PreProcess::TOOL_NAME = "preprocess"; + +-PreProcess::PreProcess () : FoldableToolPanel(this, TOOL_NAME, M("TP_PREPROCESS_LABEL"), options.prevdemo != PD_Sidecar) ++PreProcess::PreProcess () : FoldableToolPanel(this, TOOL_NAME, M("TP_PREPROCESS_LABEL"), App::get().options().prevdemo != PD_Sidecar) + { + + Gtk::Box* hotdeadPixel = Gtk::manage( new Gtk::Box () ); +@@ -48,6 +48,7 @@ PreProcess::PreProcess () : FoldableToolPanel(this, TOOL_NAME, M("TP_PREPROCESS_ + hdThreshold->set_tooltip_markup (M("TP_RAW_HD_TOOLTIP")); + hdThreshold->setAdjusterListener (this); + ++ const auto& options = App::get().options(); + hdThreshold->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + hdThreshold->show(); +diff --git a/rtgui/previewmodepanel.cc b/rtgui/previewmodepanel.cc +index 25167ae3b..6729dd3ff 100644 +--- a/rtgui/previewmodepanel.cc ++++ b/rtgui/previewmodepanel.cc +@@ -33,6 +33,7 @@ PreviewModePanel::PreviewModePanel (ImageArea* ia) : + nBC2("square-toggle-white-on-narrow"), ngBC2("square-toggle-white-off-narrow"), + nBC3("square-toggle-gray-on-narrow"), ngBC3("square-toggle-gray-off-narrow") + { ++ const auto& options = App::get().options(); + backColor0 = Gtk::manage (new Gtk::ToggleButton ()); + backColor0->get_style_context()->add_class("narrowbutton"); + backColor0->set_relief(Gtk::RELIEF_NONE); +diff --git a/rtgui/previewwindow.cc b/rtgui/previewwindow.cc +index 92bb71a78..01ac638d5 100644 +--- a/rtgui/previewwindow.cc ++++ b/rtgui/previewwindow.cc +@@ -91,6 +91,7 @@ void PreviewWindow::updatePreviewImage () + + if (previewHandler->getCropParams().enabled) { + rtengine::procparams::CropParams cparams = previewHandler->getCropParams(); ++ const auto& options = App::get().options(); + switch (options.cropGuides) { + case Options::CROP_GUIDE_NONE: + cparams.guide = rtengine::procparams::CropParams::Guide::NONE; +@@ -169,6 +170,7 @@ bool PreviewWindow::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) + cr->rectangle (rectX + 1. * s, rectY + 1. * s, rectW - 2. * s, rectH - 2. * s); + cr->stroke (); + ++ const auto& options = App::get().options(); + // draw a "frame" line. Color of frame line can be set in preferences + cr->set_source_rgba(options.navGuideBrush[0], options.navGuideBrush[1], options.navGuideBrush[2], options.navGuideBrush[3]); //( 1.0, 1.0, 1.0, 1.0); + cr->rectangle (rectX, rectY, rectW, rectH); +diff --git a/rtgui/profilepanel.cc b/rtgui/profilepanel.cc +index 1bb567cb3..2303a2653 100644 +--- a/rtgui/profilepanel.cc ++++ b/rtgui/profilepanel.cc +@@ -47,13 +47,13 @@ void ProfilePanel::cleanup () + + ProfilePanel::ProfilePanel () : storedPProfile(nullptr), + modeOn("profile-filled"), modeOff("profile-partial"), +- profileFillImage(Gtk::manage(new RTImage(options.filledProfile ? modeOn : modeOff, Gtk::ICON_SIZE_LARGE_TOOLBAR))), ++ profileFillImage(Gtk::manage(new RTImage(App::get().options().filledProfile ? modeOn : modeOff, Gtk::ICON_SIZE_LARGE_TOOLBAR))), + lastSavedPSE(nullptr), customPSE(nullptr) + { + tpc = nullptr; + + fillMode = Gtk::manage (new Gtk::ToggleButton()); +- fillMode->set_active(options.filledProfile); ++ fillMode->set_active(App::get().options().filledProfile); + fillMode->add(*profileFillImage); + fillMode->signal_toggled().connect ( sigc::mem_fun(*this, &ProfilePanel::profileFillModeToggled) ); + fillMode->set_tooltip_text(M("PROFILEPANEL_MODE_TOOLTIP")); +@@ -311,6 +311,7 @@ void ProfilePanel::save_clicked (GdkEventButton* event) + } + } + ++ auto& options = App::get().mut_options(); + Gtk::FileChooserDialog dialog(getToplevelWindow(this), M("PROFILEPANEL_SAVEDLGLABEL"), Gtk::FILE_CHOOSER_ACTION_SAVE); + bindCurrentFolder(dialog, options.loadSaveProfilePath); + dialog.set_current_name(lastFilename); +@@ -332,7 +333,7 @@ void ProfilePanel::save_clicked (GdkEventButton* event) + //Add filters, so that only certain file types can be selected: + auto filter_pp = Gtk::FileFilter::create(); + filter_pp->set_name(M("FILECHOOSER_FILTER_PP")); +- filter_pp->add_pattern("*" + paramFileExtension); ++ filter_pp->add_pattern("*" + App::PARAM_FILE_EXTENSION); + dialog.add_filter(filter_pp); + + auto filter_any = Gtk::FileFilter::create(); +@@ -354,8 +355,8 @@ void ProfilePanel::save_clicked (GdkEventButton* event) + + auto fname = dialog.get_filename(); + +- if (("." + getExtension(fname)) != paramFileExtension) { +- fname += paramFileExtension; ++ if (("." + getExtension(fname)) != App::PARAM_FILE_EXTENSION) { ++ fname += App::PARAM_FILE_EXTENSION; + } + + if (!confirmOverwrite(dialog, fname)) { +@@ -467,6 +468,7 @@ void ProfilePanel::load_clicked (GdkEventButton* event) + } + + Gtk::FileChooserDialog dialog (getToplevelWindow (this), M("PROFILEPANEL_LOADDLGLABEL"), Gtk::FILE_CHOOSER_ACTION_OPEN); ++ auto& options = App::get().mut_options(); + bindCurrentFolder (dialog, options.loadSaveProfilePath); + + //Add the user's default (or global if multiuser=false) profile path to the Shortcut list +@@ -486,7 +488,7 @@ void ProfilePanel::load_clicked (GdkEventButton* event) + //Add filters, so that only certain file types can be selected: + Glib::RefPtr filter_pp = Gtk::FileFilter::create(); + filter_pp->set_name(M("FILECHOOSER_FILTER_PP")); +- filter_pp->add_pattern("*" + paramFileExtension); ++ filter_pp->add_pattern("*" + App::PARAM_FILE_EXTENSION); + dialog.add_filter(filter_pp); + + Glib::RefPtr filter_any = Gtk::FileFilter::create(); +@@ -903,7 +905,7 @@ void ProfilePanel::initProfile (const Glib::ustring& profileFullPath, ProcParams + + void ProfilePanel::setInitialFileName (const Glib::ustring& filename) + { +- lastFilename = Glib::path_get_basename(filename) + paramFileExtension; ++ lastFilename = Glib::path_get_basename(filename) + App::PARAM_FILE_EXTENSION; + imagePath = Glib::path_get_dirname(filename); + } + +@@ -920,6 +922,6 @@ void ProfilePanel::profileFillModeToggled() + + void ProfilePanel::writeOptions() + { +- options.filledProfile = fillMode->get_active(); ++ App::get().mut_options().filledProfile = fillMode->get_active(); + } + +diff --git a/rtgui/profilestorecombobox.cc b/rtgui/profilestorecombobox.cc +index 0eaf082d2..c7f99d853 100644 +--- a/rtgui/profilestorecombobox.cc ++++ b/rtgui/profilestorecombobox.cc +@@ -69,7 +69,7 @@ void ProfileStoreComboBox::refreshProfileList_ (Gtk::TreeModel::Row *parentRow, + if (entry->type == PSET_FOLDER) { + Glib::ustring folderPath ( ProfileStore::getInstance()->getPathFromId (entry->folderId) ); + +- if (options.useBundledProfiles || ((folderPath != "${G}" ) && (folderPath != "${U}" ))) { ++ if (App::get().options().useBundledProfiles || ((folderPath != "${G}" ) && (folderPath != "${U}" ))) { + // creating the new submenu + Gtk::TreeModel::Row newSubMenu; + +diff --git a/rtgui/rawcacorrection.cc b/rtgui/rawcacorrection.cc +index 2288c1b2f..d2fd7ef3e 100644 +--- a/rtgui/rawcacorrection.cc ++++ b/rtgui/rawcacorrection.cc +@@ -49,6 +49,7 @@ RAWCACorr::RAWCACorr () : FoldableToolPanel(this, TOOL_NAME, M("TP_RAWCACORR_LAB + caAutoiterations->setAdjusterListener (this); + caAutoiterations->set_tooltip_markup(M("TP_RAWCACORR_AUTOIT_TOOLTIP")); + ++ const auto& options = App::get().options(); + caAutoiterations->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + caRed = Gtk::manage(new Adjuster (M("TP_RAWCACORR_CARED"), -4.0, 4.0, 0.1, 0, icaredL, icaredR)); +diff --git a/rtgui/rawexposure.cc b/rtgui/rawexposure.cc +index 26f2dba74..b970f0af2 100644 +--- a/rtgui/rawexposure.cc ++++ b/rtgui/rawexposure.cc +@@ -35,6 +35,7 @@ RAWExposure::RAWExposure () : FoldableToolPanel(this, TOOL_NAME, M("TP_EXPOS_WHI + PexPos = Gtk::manage(new Adjuster (M("TP_RAWEXPOS_LINEAR"), 0.1, 16.0, 0.01, 1)); + PexPos->setAdjusterListener (this); + ++ const auto& options = App::get().options(); + PexPos->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + PexPos->show(); +diff --git a/rtgui/recentbrowser.cc b/rtgui/recentbrowser.cc +index 9d3cc1f12..971b190bc 100644 +--- a/rtgui/recentbrowser.cc ++++ b/rtgui/recentbrowser.cc +@@ -32,6 +32,7 @@ RecentBrowser::RecentBrowser () + frame->set_label_align(0.025, 0.5); + frame->add (*recentDirs); + ++ const auto& options = App::get().options(); + for(size_t i = 0; i < options.recentFolders.size(); i++) { + recentDirs->append (options.recentFolders[i]); + } +@@ -55,7 +56,7 @@ void RecentBrowser::selectionChanged () + + void RecentBrowser::dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile) + { +- ++ auto& options = App::get().mut_options(); + ssize_t numFolders = options.recentFolders.size(); + ssize_t i = -1; + +diff --git a/rtgui/retinex.cc b/rtgui/retinex.cc +index 73b5e819c..0702632e7 100644 +--- a/rtgui/retinex.cc ++++ b/rtgui/retinex.cc +@@ -162,6 +162,8 @@ Retinex::Retinex () : FoldableToolPanel (this, TOOL_NAME, M ("TP_RETINEX_LABEL") + maskGrid->attach (*mapgrid, 0, 0, 1, 1); + mapgrid->show(); + ++ auto& options = App::get().mut_options(); ++ + // Map Equalizer + curveEditormap = new CurveEditorGroup (options.lastRetinexDir, M ("TP_RETINEX_CONTEDIT_MAP")); + setExpandAlignProperties (curveEditormap, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START); +diff --git a/rtgui/rgbcurves.cc b/rtgui/rgbcurves.cc +index c8b51b70b..c263d27d6 100644 +--- a/rtgui/rgbcurves.cc ++++ b/rtgui/rgbcurves.cc +@@ -46,6 +46,7 @@ RGBCurves::RGBCurves () : FoldableToolPanel(this, TOOL_NAME, M("TP_RGBCURVES_LAB + + std::vector milestones; + ++ auto& options = App::get().mut_options(); + curveEditorG = new CurveEditorGroup (options.lastRgbCurvesDir, M("TP_RGBCURVES_CHANNEL")); + curveEditorG->setCurveListener (this); + +diff --git a/rtgui/rtscalable.cc b/rtgui/rtscalable.cc +index bb2991c51..bfbf7b2fb 100644 +--- a/rtgui/rtscalable.cc ++++ b/rtgui/rtscalable.cc +@@ -23,11 +23,10 @@ + #include + #include + ++#include "rtengine/rtapp.h" + #include "rtengine/settings.h" + #include "guiutils.h" + +-extern Glib::ustring argv0; +- + // Default static parameter values + double RTScalable::dpi = 96.; + int RTScalable::scale = 1; +@@ -119,7 +118,7 @@ Cairo::RefPtr RTScalable::loadSurfaceFromPNG(const Glib::us + path = fname; + } else { + // Look for PNG file in "images" folder +- Glib::ustring imagesFolder = Glib::build_filename(argv0, "images"); ++ Glib::ustring imagesFolder = Glib::build_filename(App::get().argv0(), "images"); + path = Glib::build_filename(imagesFolder, fname); + } + +@@ -146,7 +145,7 @@ Cairo::RefPtr RTScalable::loadSurfaceFromSVG(const Glib::us + path = fname; + } else { + // Look for SVG file in "images" folder +- Glib::ustring imagesFolder = Glib::build_filename(argv0, "images"); ++ Glib::ustring imagesFolder = Glib::build_filename(App::get().argv0(), "images"); + path = Glib::build_filename(imagesFolder, fname); + } + +diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc +index 8ecaa1184..84f562b3e 100755 +--- a/rtgui/rtwindow.cc ++++ b/rtgui/rtwindow.cc +@@ -74,7 +74,7 @@ osx_open_file_cb (GtkosxApplication *app, gchar *path_, gpointer data) + { + RTWindow *rtWin = static_cast(data); + +- if (!argv1.empty()) { ++ if (!App::get().argv1().empty()) { + // skip handling if we have a file argument or else we get double open of same file + return false; + } +@@ -107,6 +107,7 @@ RTWindow::RTWindow () + // ------- loading theme files + + Glib::RefPtr screen = Gdk::Screen::get_default(); ++ auto& options = App::get().mut_options(); + + if (screen) { + // Setting default theme and icon theme (bases for custom themes) +@@ -119,10 +120,10 @@ RTWindow::RTWindow () + + // Look for theme and set it + // Check if the current theme name in options exists, otherwise set it to default one (i.e. "RawTherapee.css") +- auto filename = Glib::build_filename(argv0, "themes", options.theme + ".css"); ++ auto filename = Glib::build_filename(App::get().argv0(), "themes", options.theme + ".css"); + if (!Glib::file_test(filename, Glib::FILE_TEST_EXISTS)) { + options.theme = "RawTherapee"; +- filename = Glib::build_filename(argv0, "themes", options.theme + ".css"); ++ filename = Glib::build_filename(App::get().argv0(), "themes", options.theme + ".css"); + } + + cssRT = Gtk::CssProvider::create(); +@@ -214,7 +215,7 @@ RTWindow::RTWindow () + gtkosx_application_ready (osxApp); + } + #endif +- versionStr = "RawTherapee " + versionString; ++ versionStr = "RawTherapee " + App::VERSION; + + set_title_decorated (""); + set_resizable (true); +@@ -231,7 +232,7 @@ RTWindow::RTWindow () + signal_key_press_event().connect ( sigc::mem_fun (*this, &RTWindow::keyPressed) ); + signal_key_release_event().connect(sigc::mem_fun(*this, &RTWindow::keyReleased)); + +- if (simpleEditor) { ++ if (App::get().isSimpleEditor()) { + epanel = Gtk::manage ( new EditorPanel (nullptr) ); + epanel->setParent (this); + epanel->setParentWindow (this); +@@ -241,11 +242,11 @@ RTWindow::RTWindow () + pldBridge = nullptr; // No progress listener + + CacheManager* cm = CacheManager::getInstance(); +- Thumbnail* thm = cm->getEntry ( argv1 ); ++ Thumbnail* thm = cm->getEntry ( App::get().argv1() ); + + if (thm) { + int error; +- rtengine::InitialImage *ii = rtengine::InitialImage::load (argv1, thm->getType() == FT_Raw, &error, nullptr); ++ rtengine::InitialImage *ii = rtengine::InitialImage::load (App::get().argv1(), thm->getType() == FT_Raw, &error, nullptr); + epanel->open ( thm, ii ); + } + } else { +@@ -372,8 +373,8 @@ RTWindow::RTWindow () + + bpanel->init (this); + +- if (!argv1.empty() && !remote) { +- Thumbnail* thm = cacheMgr->getEntry (argv1); ++ if (!App::get().argv1().empty() && !App::get().isRemote()) { ++ Thumbnail* thm = cacheMgr->getEntry (App::get().argv1()); + + if (thm) { + fpanel->fileCatalog->openRequested ({thm}); +@@ -384,7 +385,7 @@ RTWindow::RTWindow () + + RTWindow::~RTWindow() + { +- if (!simpleEditor) { ++ if (!App::get().isSimpleEditor()) { + delete pldBridge; + } + +@@ -406,7 +407,7 @@ void RTWindow::on_realize () + fpanel->setAspect(); + } + +- if (simpleEditor) { ++ if (App::get().isSimpleEditor()) { + epanel->setAspect(); + } + +@@ -414,9 +415,10 @@ void RTWindow::on_realize () + + // Display release notes only if new major version. + bool waitForSplash = false; ++ auto& options = App::get().mut_options(); + if (options.is_new_version()) { + // Update the version parameter with the right value +- options.version = versionString; ++ options.version = App::VERSION; + + splash = new Splash (*this); + splash->set_transient_for (*this); +@@ -439,6 +441,7 @@ void RTWindow::on_realize () + + void RTWindow::showErrors() + { ++ auto& options = App::get().mut_options(); + // alerting users if the default raw and image profiles are missing + if (options.is_defProfRawMissing()) { + options.defProfRaw = DEFPROFILE_RAW; +@@ -465,6 +468,7 @@ void RTWindow::showErrors() + + bool RTWindow::on_configure_event (GdkEventConfigure* event) + { ++ auto& options = App::get().mut_options(); + if (!options.windowMaximized && !is_fullscreen && !is_minimized) { + get_size (options.windowWidth, options.windowHeight); + get_position (options.windowX, options.windowY); +@@ -479,7 +483,7 @@ bool RTWindow::on_configure_event (GdkEventConfigure* event) + bool RTWindow::on_window_state_event (GdkEventWindowState* event) + { + // Retrieve RT window states +- options.windowMaximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; ++ App::get().mut_options().windowMaximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; + is_minimized = event->new_window_state & GDK_WINDOW_STATE_ICONIFIED; + is_fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; + +@@ -519,7 +523,7 @@ void RTWindow::on_mainNB_switch_page (Gtk::Widget* widget, guint page_num) + + void RTWindow::addEditorPanel (EditorPanel* ep, const std::string &name) + { +- if (options.multiDisplayMode > 0) { ++ if (App::get().options().multiDisplayMode > 0) { + EditWindow * wndEdit = EditWindow::getInstance (this); + wndEdit->addEditorPanel (ep, name); + wndEdit->show_all(); +@@ -573,7 +577,7 @@ void RTWindow::remEditorPanel (EditorPanel* ep) + return; // Will crash if destroyed while loading + } + +- if (options.multiDisplayMode > 0) { ++ if (App::get().options().multiDisplayMode > 0) { + EditWindow * wndEdit = EditWindow::getInstance (this); + wndEdit->remEditorPanel (ep); + } else { +@@ -602,7 +606,7 @@ void RTWindow::remEditorPanel (EditorPanel* ep) + + bool RTWindow::selectEditorPanel (const std::string &name) + { +- if (options.multiDisplayMode > 0) { ++ if (App::get().options().multiDisplayMode > 0) { + EditWindow * wndEdit = EditWindow::getInstance (this); + + if (wndEdit->selectEditorPanel (name)) { +@@ -658,7 +662,7 @@ bool RTWindow::keyPressed (GdkEventKey* event) + toggle_fullscreen(); + } + +- if (simpleEditor) ++ if (App::get().isSimpleEditor()) + // in simpleEditor mode, there's no other tab that can handle pressed keys, so we can send the event to editor panel then return + { + return epanel->handleShortcutKey (event); +@@ -739,8 +743,9 @@ bool RTWindow::on_delete_event (GdkEventAny* event) + // Check if any editor is still processing, and do NOT quit if so. Otherwise crashes and inconsistent caches + bool isProcessing = false; + EditWindow* editWindow = nullptr; ++ auto& options = App::get().mut_options(); + +- if (isSingleTabMode() || simpleEditor) { ++ if (isSingleTabMode() || App::get().isSimpleEditor()) { + isProcessing = epanel->getIsProcessing(); + } else if (options.multiDisplayMode > 0) { + editWindow = EditWindow::getInstance (this); +@@ -767,7 +772,7 @@ bool RTWindow::on_delete_event (GdkEventAny* event) + bpanel->saveOptions (); + } + +- if ((isSingleTabMode() || simpleEditor) && epanel->isRealized()) { ++ if ((isSingleTabMode() || App::get().isSimpleEditor()) && epanel->isRealized()) { + epanel->saveProfile(); + epanel->writeOptions (); + } else { +@@ -840,7 +845,7 @@ bool RTWindow::on_delete_event (GdkEventAny* event) + + void RTWindow::writeToolExpandedStatus (std::vector &tpOpen) + { +- if ((isSingleTabMode() || gimpPlugin) && epanel->isRealized()) { ++ if ((isSingleTabMode() || App::get().isGimpPlugin()) && epanel->isRealized()) { + epanel->writeToolExpandedStatus (tpOpen); + } else { + // Storing the options of the last EditorPanel before Gtk destroys everything +@@ -881,6 +886,7 @@ void RTWindow::showICCProfileCreator () + + fpanel->optionsChanged (); + ++ const auto& options = App::get().options(); + if (epanel) { + epanel->defaultMonitorProfileChanged (options.rtSettings.monitorProfile, options.rtSettings.autoMonitorProfile); + } +@@ -898,6 +904,7 @@ void RTWindow::showPreferences () + + fpanel->optionsChanged (); + ++ const auto& options = App::get().options(); + if (epanel) { + epanel->defaultMonitorProfileChanged (options.rtSettings.monitorProfile, options.rtSettings.autoMonitorProfile); + } +@@ -914,7 +921,7 @@ void RTWindow::setProgress(double p) + + void RTWindow::setProgressStr(const Glib::ustring& str) + { +- if (!options.mainNBVertical) { ++ if (!App::get().options().mainNBVertical) { + prProgBar.set_text(str); + } + } +@@ -985,7 +992,7 @@ void RTWindow::MoveFileBrowserToEditor() + fpanel->ribbonPane->remove (*fCatalog); + fCatalog->disableInspector(); + epanel->catalogPane->add (*fCatalog); +- epanel->showTopPanel (options.editorFilmStripOpened); ++ epanel->showTopPanel (App::get().options().editorFilmStripOpened); + fCatalog->enableTabMode (true); + fCatalog->refreshHeight(); + fCatalog->tbLeftPanel_1_visible (false); +@@ -1003,7 +1010,7 @@ void RTWindow::updateExternalEditorWidget(int selectedIndex, const std::vectorupdateExternalEditorWidget(selectedIndex, editors); + } + +- if (options.multiDisplayMode > 0) { ++ if (App::get().options().multiDisplayMode > 0) { + EditWindow::getInstance(this) + ->updateExternalEditorWidget(selectedIndex, editors); + } +@@ -1080,7 +1087,7 @@ void RTWindow::updateToolPanelToolLocations( + panel.second->updateToolPanelToolLocations(favorites, cloneFavoriteTools); + } + +- if (options.multiDisplayMode > 0) { ++ if (App::get().options().multiDisplayMode > 0) { + EditWindow::getInstance(this) + ->updateToolPanelToolLocations(favorites, cloneFavoriteTools); + } +@@ -1098,6 +1105,7 @@ void RTWindow::setWindowSize () + { + onConfEventConn.block(true); // Avoid getting size and position while window is being moved, maximized, ... + ++ const auto& options = App::get().options(); + Gdk::Rectangle lMonitorRect; + const auto display = get_screen()->get_display(); + display->get_monitor (std::min (options.windowMonitor, display->get_n_monitors() - 1))->get_geometry(lMonitorRect); +@@ -1223,6 +1231,7 @@ void RTWindow::createSetmEditor() + setExpandAlignProperties (editorLabelGrid, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER); + Gtk::Label* const el = Gtk::manage (new Gtk::Label ( Glib::ustring (" ") + M ("MAIN_FRAME_EDITOR") )); + ++ const auto& options = App::get().options(); + const auto pos = options.mainNBVertical ? Gtk::POS_TOP : Gtk::POS_RIGHT; + + if (options.mainNBVertical) { +@@ -1241,5 +1250,6 @@ void RTWindow::createSetmEditor() + + bool RTWindow::isSingleTabMode() const + { ++ const auto& options = App::get().options(); + return !options.tabbedUI && ! (options.multiDisplayMode > 0); + } +diff --git a/rtgui/saveasdlg.cc b/rtgui/saveasdlg.cc +index b1a217553..3bda96c81 100644 +--- a/rtgui/saveasdlg.cc ++++ b/rtgui/saveasdlg.cc +@@ -75,6 +75,7 @@ SaveAsDialog::SaveAsDialog (const Glib::ustring &initialDir, Gtk::Window* parent + filter_png->add_pattern("*.png"); + filter_png->add_pattern("*.PNG"); + ++ const auto& options = App::get().options(); + formatChanged (options.saveFormat.format); + + // Output Options +@@ -130,7 +131,7 @@ SaveAsDialog::SaveAsDialog (const Glib::ustring &initialDir, Gtk::Window* parent + Gtk::Box* vbox_bottomRight = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + + // There is no queue in simple mode, so no need to choose +- if (!simpleEditor) { ++ if (!App::get().isSimpleEditor()) { + vbox_bottomRight->pack_start (*saveMethod[0], Gtk::PACK_SHRINK, 2); + vbox_bottomRight->pack_start (*saveMethod[1], Gtk::PACK_SHRINK, 2); + vbox_bottomRight->pack_start (*saveMethod[2], Gtk::PACK_SHRINK, 2); +@@ -190,7 +191,7 @@ bool SaveAsDialog::getAutoSuffix () + bool SaveAsDialog::getImmediately () + { + +- return simpleEditor ? true : saveMethod[0]->get_active (); ++ return App::get().isSimpleEditor() ? true : saveMethod[0]->get_active(); + } + + bool SaveAsDialog::getToHeadOfQueue () +@@ -207,7 +208,7 @@ bool SaveAsDialog::getToTailOfQueue () + + int SaveAsDialog::getSaveMethodNum () + { +- if (simpleEditor) { ++ if (App::get().isSimpleEditor()) { + return 0; + } + +diff --git a/rtgui/saveformatpanel.cc b/rtgui/saveformatpanel.cc +index d9b04e8fe..39d3977c0 100644 +--- a/rtgui/saveformatpanel.cc ++++ b/rtgui/saveformatpanel.cc +@@ -125,7 +125,7 @@ SaveFormatPanel::SaveFormatPanel () : listener (nullptr) + + SaveFormatPanel::~SaveFormatPanel () = default; + +-void SaveFormatPanel::init (SaveFormat &sf) ++void SaveFormatPanel::init (const SaveFormat &sf) + { + FormatChangeListener* const tmp = listener; + listener = nullptr; +diff --git a/rtgui/saveformatpanel.h b/rtgui/saveformatpanel.h +index ba08c98f6..af6c4a72f 100644 +--- a/rtgui/saveformatpanel.h ++++ b/rtgui/saveformatpanel.h +@@ -58,7 +58,7 @@ public: + listener = l; + } + +- void init (SaveFormat& sf); ++ void init (const SaveFormat& sf); + SaveFormat getFormat (); + + void formatChanged (); +diff --git a/rtgui/sharpenedge.cc b/rtgui/sharpenedge.cc +index 3104b641e..f7a1d2891 100644 +--- a/rtgui/sharpenedge.cc ++++ b/rtgui/sharpenedge.cc +@@ -36,6 +36,7 @@ SharpenEdge::SharpenEdge () : FoldableToolPanel(this, TOOL_NAME, M("TP_SHARPENED + passes = Gtk::manage(new Adjuster (M("TP_SHARPENEDGE_PASSES"), 1, 4, 1, 2)); + passes->setAdjusterListener (this); + ++ const auto& options = App::get().options(); + passes->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + amount = Gtk::manage(new Adjuster (M("TP_SHARPENEDGE_AMOUNT"), 0, 100, 1, 50)); +diff --git a/rtgui/soundman.cc b/rtgui/soundman.cc +index 5552bd12d..864e5e377 100644 +--- a/rtgui/soundman.cc ++++ b/rtgui/soundman.cc +@@ -49,7 +49,7 @@ void SoundManager::init() + // param is either file name or name of the system event on Windows (e.g. "SystemAsterisk" or "SystemDefault"). + void SoundManager::playSoundAsync(const Glib::ustring &sound) + { +- if (sound.empty() || !options.sndEnable) { ++ if (sound.empty() || !App::get().options().sndEnable) { + return; + } + +diff --git a/rtgui/splash.cc b/rtgui/splash.cc +index 0015c4864..2f075dbf0 100644 +--- a/rtgui/splash.cc ++++ b/rtgui/splash.cc +@@ -21,10 +21,7 @@ + #include + + #include "multilangmgr.h" +- +-extern Glib::ustring creditsPath; +-extern Glib::ustring licensePath; +-extern Glib::ustring versionString; ++#include "rtengine/rtapp.h" + + SplashImage::SplashImage () : surface(new RTSurface("splash.svg")) + { +@@ -50,7 +47,7 @@ bool SplashImage::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) + context->set_font_description (fontd); + + int w, h; +- Glib::ustring versionStr(versionString); ++ Glib::ustring versionStr(App::VERSION); + + version = create_pango_layout (versionStr); + version->set_text(versionStr); +@@ -120,7 +117,7 @@ Splash::Splash (Gtk::Window& parent) : Gtk::Dialog(M("GENERAL_ABOUT"), parent, t + splashImage->show (); + + // Tab 2: the information about the current version +- std::string buildFileName = Glib::build_filename (creditsPath, "AboutThisBuild.txt"); ++ std::string buildFileName = Glib::build_filename (App::get().creditsPath(), "AboutThisBuild.txt"); + + if ( Glib::file_test(buildFileName, (Glib::FILE_TEST_EXISTS)) ) { + FILE *f = g_fopen (buildFileName.c_str (), "rt"); +@@ -151,7 +148,7 @@ Splash::Splash (Gtk::Window& parent) : Gtk::Dialog(M("GENERAL_ABOUT"), parent, t + } + + // Tab 3: the credits +- std::string creditsFileName = Glib::build_filename (creditsPath, "AUTHORS.txt"); ++ std::string creditsFileName = Glib::build_filename (App::get().creditsPath(), "AUTHORS.txt"); + + if ( Glib::file_test(creditsFileName, (Glib::FILE_TEST_EXISTS)) ) { + FILE *f = g_fopen (creditsFileName.c_str (), "rt"); +@@ -183,7 +180,7 @@ Splash::Splash (Gtk::Window& parent) : Gtk::Dialog(M("GENERAL_ABOUT"), parent, t + } + + // Tab 4: the license +- std::string licenseFileName = Glib::build_filename (licensePath, "LICENSE"); ++ std::string licenseFileName = Glib::build_filename (App::get().licensePath(), "LICENSE"); + + if ( Glib::file_test(licenseFileName, (Glib::FILE_TEST_EXISTS)) ) { + FILE *f = g_fopen (licenseFileName.c_str (), "rt"); +@@ -216,7 +213,7 @@ Splash::Splash (Gtk::Window& parent) : Gtk::Dialog(M("GENERAL_ABOUT"), parent, t + } + + // Tab 5: the Release Notes +- std::string releaseNotesFileName = Glib::build_filename (creditsPath, "RELEASE_NOTES.txt"); ++ std::string releaseNotesFileName = Glib::build_filename (App::get().creditsPath(), "RELEASE_NOTES.txt"); + + if ( Glib::file_test(releaseNotesFileName, (Glib::FILE_TEST_EXISTS)) ) { + FILE *f = g_fopen (releaseNotesFileName.c_str (), "rt"); +diff --git a/rtgui/thresholdadjuster.cc b/rtgui/thresholdadjuster.cc +index 5545a9f43..950e2462f 100644 +--- a/rtgui/thresholdadjuster.cc ++++ b/rtgui/thresholdadjuster.cc +@@ -80,7 +80,7 @@ void ThresholdAdjuster::initObject (Glib::ustring label, bool editedcb) + + addMode = false; + +- delay = options.adjusterMinDelay; ++ delay = App::get().options().adjusterMinDelay; + + set_name("ThresholdAdjuster"); + +diff --git a/rtgui/thumbbrowserbase.cc b/rtgui/thumbbrowserbase.cc +index 15ad8831e..649476232 100644 +--- a/rtgui/thumbbrowserbase.cc ++++ b/rtgui/thumbbrowserbase.cc +@@ -30,7 +30,7 @@ + using namespace std; + + ThumbBrowserBase::ThumbBrowserBase () +- : location(THLOC_FILEBROWSER), inspector(nullptr), isInspectorActive(false), eventTime(0), lastClicked(nullptr), anchor(nullptr), previewHeight(options.thumbSize), numOfCols(1), lastRowHeight(0), arrangement(TB_Horizontal) ++ : location(THLOC_FILEBROWSER), inspector(nullptr), isInspectorActive(false), eventTime(0), lastClicked(nullptr), anchor(nullptr), previewHeight(App::get().options().thumbSize), numOfCols(1), lastRowHeight(0), arrangement(TB_Horizontal) + { + inW = -1; + inH = -1; +@@ -1101,10 +1101,11 @@ void ThumbBrowserBase::resort () + { + MYWRITERLOCK(l, entryRW); + ++ const auto& options = App::get().options(); + std::sort( + fd.begin(), + fd.end(), +- [](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b) ++ [&](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b) + { + bool lt = a->compare(*b, options.sortMethod); + return options.sortDescending ? !lt : lt; +@@ -1129,6 +1130,7 @@ void ThumbBrowserBase::zoomChanged (bool zoomIn) + int newHeight = 0; + int optThumbSize = getThumbnailHeight(); + ++ const auto& options = App::get().options(); + if (zoomIn) + for (size_t i = 0; i < options.thumbnailZoomRatios.size(); i++) { + newHeight = (int)(options.thumbnailZoomRatios[i] * getMaxThumbnailHeight()); +@@ -1212,6 +1214,7 @@ void ThumbBrowserBase::enableTabMode(bool enable) + location = enable ? THLOC_EDITOR : THLOC_FILEBROWSER; + arrangement = enable ? ThumbBrowserBase::TB_Horizontal : ThumbBrowserBase::TB_Vertical; + ++ const auto& options = App::get().options(); + if ((!options.sameThumbSize && (options.thumbSizeTab != options.thumbSize)) || (options.showFileNames || options.filmStripShowFileNames)) { + + MYWRITERLOCK(l, entryRW); +@@ -1248,12 +1251,13 @@ void ThumbBrowserBase::insertEntry (ThumbBrowserEntryBase* entry) + { + MYWRITERLOCK(l, entryRW); + ++ const auto& options = App::get().options(); + fd.insert( + std::lower_bound( + fd.begin(), + fd.end(), + entry, +- [](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b) ++ [&](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b) + { + bool lt = a->compare(*b, options.sortMethod); + return options.sortDescending ? !lt : lt; +diff --git a/rtgui/thumbbrowserbase.h b/rtgui/thumbbrowserbase.h +index 8c1ec49c8..9b3a927d9 100644 +--- a/rtgui/thumbbrowserbase.h ++++ b/rtgui/thumbbrowserbase.h +@@ -110,7 +110,7 @@ public: + protected: + virtual int getMaxThumbnailHeight() const + { +- return options.maxThumbnailHeight; // Differs between batch and file ++ return App::get().options().maxThumbnailHeight; // Differs between batch and file + } + virtual void saveThumbnailHeight (int height) = 0; + virtual int getThumbnailHeight () = 0; +diff --git a/rtgui/thumbbrowserentrybase.cc b/rtgui/thumbbrowserentrybase.cc +index fd3f7d111..a47c0ca83 100644 +--- a/rtgui/thumbbrowserentrybase.cc ++++ b/rtgui/thumbbrowserentrybase.cc +@@ -263,6 +263,7 @@ void ThumbBrowserEntryBase::updateBackBuffer () + int istartx = prex; + int istarty = prey; + ++ const auto& options = App::get().options(); + if ((parent->getLocation() != ThumbBrowserBase::THLOC_EDITOR && options.showFileNames && options.overlayedFileNames) + || (parent->getLocation() == ThumbBrowserBase::THLOC_EDITOR && options.filmStripShowFileNames && options.filmStripOverlayedFileNames)) { + cc->begin_new_path (); +@@ -453,6 +454,8 @@ void ThumbBrowserEntryBase::getTextSizes (int& infow, int& infoh) + infow = 0; + + if (withFilename == WFNAME_FULL) { ++ const auto& options = App::get().options(); ++ + // datetime + fontd.set_weight (Pango::WEIGHT_NORMAL); + context->set_font_description (fontd); +@@ -503,6 +506,7 @@ void ThumbBrowserEntryBase::resize (int h) + buttonSet->getMinimalDimensions (bsw, bsh); + } + ++ const auto& options = App::get().options(); + if (parent->getLocation() == ThumbBrowserBase::THLOC_FILEBROWSER) { + if (options.showFileNames) { + withFilename = WFNAME_FULL; +@@ -738,6 +742,7 @@ std::tuple ThumbBrowserEntryBase::getToolTip (int x, int y) + if (inside(x, y) && tooltip.empty()) { + tooltip = dispname; + ++ const auto& options = App::get().options(); + if (withFilename < WFNAME_FULL) { + if (options.fbShowDateTime && !datetimeline.empty()) { + tooltip += Glib::ustring("\n") + datetimeline; +diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc +index e7f117101..0f63dae5b 100644 +--- a/rtgui/thumbnail.cc ++++ b/rtgui/thumbnail.cc +@@ -74,6 +74,7 @@ bool CPBDump( + return false; + } + ++ const auto& options = App::get().options(); + try { + kf->set_string ("RT General", "CachePath", options.cacheBaseDir); + kf->set_string ("RT General", "AppVersion", RTVERSION); +@@ -361,6 +362,7 @@ void Thumbnail::_generateThumbnailImage() + tpp = nullptr; + delete[] lastImg; + lastImg = nullptr; ++ const auto& options = App::get().options(); + tw = options.maxThumbnailWidth; + th = options.maxThumbnailHeight; + imgRatio = -1.; +@@ -477,6 +479,7 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu + // try to load the last saved parameters from the cache or from the paramfile file + ProcParams* ldprof = nullptr; + ++ const auto& options = App::get().options(); + Glib::ustring defProf = getType() == FT_Raw ? options.defProfRaw : options.defProfImg; + + const CacheImageData* cfs = getCacheImageData(); +@@ -486,8 +489,8 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu + + const Glib::ustring outFName = + (options.paramsLoadLocation == PLL_Input && options.saveParamsFile) ? +- fname + paramFileExtension : +- getCacheFileName("profiles", paramFileExtension); ++ fname + App::PARAM_FILE_EXTENSION : ++ getCacheFileName("profiles", App::PARAM_FILE_EXTENSION); + + if (!run_cpb) { + if (defProf == DEFPROFILE_DYNAMIC && create && cfs && cfs->exifValid) { +@@ -519,7 +522,7 @@ rtengine::procparams::ProcParams* Thumbnail::createProcParamsForUpdate(bool retu + Glib::ustring tmpFileName( Glib::build_filename(options.cacheBaseDir, Glib::ustring::compose("CPB_temp_%1.txt", index++)) ); + + CPBDump(tmpFileName, fname, outFName, +- defaultPparamsPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(defaultPparamsPath, Glib::path_get_basename(defProf) + paramFileExtension), cfs, flaggingMode); ++ defaultPparamsPath == DEFPROFILE_INTERNAL ? DEFPROFILE_INTERNAL : Glib::build_filename(defaultPparamsPath, Glib::path_get_basename(defProf) + App::PARAM_FILE_EXTENSION), cfs, flaggingMode); + + // For the filename etc. do NOT use streams, since they are not UTF8 safe + Glib::ustring cmdLine = options.CPBPath + Glib::ustring(" \"") + tmpFileName + Glib::ustring("\""); +@@ -567,22 +570,22 @@ void Thumbnail::loadProcParams() + pparamsValid = false; + pparams->setDefaults(); + +- if (options.paramsLoadLocation == PLL_Input) { ++ if (App::get().options().paramsLoadLocation == PLL_Input) { + // try to load it from params file next to the image file +- const int ppres = pparams->load(fname + paramFileExtension); ++ const int ppres = pparams->load(fname + App::PARAM_FILE_EXTENSION); + pparamsValid = !ppres && pparams->ppVersion >= 220; + + // if no success, try to load the cached version of the procparams + if (!pparamsValid) { +- pparamsValid = !pparams->load(getCacheFileName("profiles", paramFileExtension)); ++ pparamsValid = !pparams->load(getCacheFileName("profiles", App::PARAM_FILE_EXTENSION)); + } + } else { + // try to load it from cache +- pparamsValid = !pparams->load(getCacheFileName("profiles", paramFileExtension)); ++ pparamsValid = !pparams->load(getCacheFileName("profiles", App::PARAM_FILE_EXTENSION)); + + // if no success, try to load it from params file next to the image file + if (!pparamsValid) { +- const int ppres = pparams->load(fname + paramFileExtension); ++ const int ppres = pparams->load(fname + App::PARAM_FILE_EXTENSION); + pparamsValid = !ppres && pparams->ppVersion >= 220; + } + } +@@ -620,17 +623,17 @@ void Thumbnail::clearProcParams (int whoClearedIt) + updateCache(); + } else { + // remove param file from cache +- Glib::ustring fname_ = getCacheFileName ("profiles", paramFileExtension); ++ Glib::ustring fname_ = getCacheFileName ("profiles", App::PARAM_FILE_EXTENSION); + g_remove (fname_.c_str ()); + + // remove param file located next to the file +- fname_ = fname + paramFileExtension; ++ fname_ = fname + App::PARAM_FILE_EXTENSION; + g_remove (fname_.c_str ()); + +- fname_ = removeExtension(fname) + paramFileExtension; ++ fname_ = removeExtension(fname) + App::PARAM_FILE_EXTENSION; + g_remove (fname_.c_str ()); + +- if (cfs.format == FT_Raw && options.internalThumbIfUntouched && cfs.thumbImgType != CacheImageData::QUICK_THUMBNAIL) { ++ if (cfs.format == FT_Raw && App::get().options().internalThumbIfUntouched && cfs.thumbImgType != CacheImageData::QUICK_THUMBNAIL) { + // regenerate thumbnail, ie load the quick thumb again. For the rare formats not supporting quick thumbs this will + // be a bit slow as a new full thumbnail will be generated unnecessarily, but currently there is no way to pre-check + // if the format supports quick thumbs. +@@ -748,8 +751,8 @@ void Thumbnail::imageDeveloped () + cfs.recentlySaved = true; + cfs.save (getCacheFileName ("data", ".txt")); + +- if (options.saveParamsCache) { +- pparams->save (getCacheFileName ("profiles", paramFileExtension)); ++ if (App::get().options().saveParamsCache) { ++ pparams->save (getCacheFileName ("profiles", App::PARAM_FILE_EXTENSION)); + } + } + +@@ -839,6 +842,7 @@ void Thumbnail::getThumbnailSize(int &w, int &h, const rtengine::procparams::Pro + w = tw_ * h / th_; + } + ++ const auto& options = App::get().options(); + if (w > options.maxThumbnailWidth) { + const float s = static_cast(options.maxThumbnailWidth) / w; + w = options.maxThumbnailWidth; +@@ -931,6 +935,7 @@ rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::Pro + + void Thumbnail::generateExifDateTimeStrings () + { ++ const auto& options = App::get().options(); + if (cfs.timeValid) { + std::string dateFormat = options.dateFormat; + std::ostringstream ostr; +@@ -1100,7 +1105,7 @@ void Thumbnail::_loadThumbnail(bool firstTrial) + { + + tw = -1; +- th = options.maxThumbnailHeight; ++ th = App::get().options().maxThumbnailHeight; + delete tpp; + tpp = new rtengine::Thumbnail (); + tpp->isRaw = (cfs.format == (int) FT_Raw); +@@ -1195,9 +1200,10 @@ void Thumbnail::updateCache (bool updatePParams, bool updateCacheImageData) + updateProcParamsProperties(); + + if (updatePParams && pparamsValid) { ++ const auto& options = App::get().options(); + pparams->save ( +- options.saveParamsFile ? fname + paramFileExtension : "", +- options.saveParamsCache ? getCacheFileName ("profiles", paramFileExtension) : "", ++ options.saveParamsFile ? fname + App::PARAM_FILE_EXTENSION : "", ++ options.saveParamsCache ? getCacheFileName ("profiles", App::PARAM_FILE_EXTENSION) : "", + true + ); + } +@@ -1292,6 +1298,7 @@ bool Thumbnail::openDefaultViewer(int destination) + #ifdef _WIN32 + Glib::ustring openFName; + ++ const auto& options = App::get().options(); + if (destination == 1) { + openFName = Glib::ustring::compose ("%1.%2", BatchQueue::calcAutoFileNameBase(fname), options.saveFormatBatch.format); + +@@ -1382,6 +1389,8 @@ void Thumbnail::loadProperties() + // get initial rank from cache or image metadata + getRankFromMetadata(cfs, fname, properties.rank.value); + ++ const auto& options = App::get().options(); ++ + // update rank and color from procparams or xmp sidecar + // load trash from procparams + if (pparamsValid) { +@@ -1423,6 +1432,8 @@ void Thumbnail::updateProcParamsProperties(bool forceUpdate) + return rtengine::Exiv2Metadata::getXmpSidecar(fname); + }); + ++ const auto& options = App::get().options(); ++ + // save procparams rank and color also when options.thumbnailRankColorMode == Options::ThumbnailPropertyMode::XMP + // so they'll be kept in sync + // Rank can be -1 to prioritize the rank in the metadata. If the metadata +@@ -1465,7 +1476,7 @@ void Thumbnail::saveXMPSidecarProperties() + return; + } + +- if (options.thumbnailRankColorMode != Options::ThumbnailPropertyMode::XMP) { ++ if (App::get().options().thumbnailRankColorMode != Options::ThumbnailPropertyMode::XMP) { + return; + } + +@@ -1490,6 +1501,7 @@ void Thumbnail::saveXMPSidecarProperties() + + void Thumbnail::saveMetadata() + { ++ const auto& options = App::get().options(); + if (options.rtSettings.metadata_xmp_sync != rtengine::Settings::MetadataXmpSync::READ_WRITE) { + return; + } +diff --git a/rtgui/tonecurve.cc b/rtgui/tonecurve.cc +index 9fb6e17e1..c88d111cb 100644 +--- a/rtgui/tonecurve.cc ++++ b/rtgui/tonecurve.cc +@@ -178,6 +178,7 @@ ToneCurve::ToneCurve() : FoldableToolPanel(this, TOOL_NAME, M("TP_EXPOSURE_LABEL + toneCurveMode->set_active(0); + toneCurveMode->set_tooltip_text(M("TP_EXPOSURE_TCMODE_LABEL1")); + ++ auto& options = App::get().mut_options(); + curveEditorG = new CurveEditorGroup(options.lastToneCurvesDir, M("TP_EXPOSURE_CURVEEDITOR1")); + curveEditorG->setCurveListener(this); + +diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc +index dd9c00af5..32c04f417 100644 +--- a/rtgui/toolpanelcoord.cc ++++ b/rtgui/toolpanelcoord.cc +@@ -470,6 +470,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit + vbPanelEnd[i]->pack_start(*imgPanelEnd[i], Gtk::PACK_SHRINK); + vbPanelEnd[i]->show_all(); + } ++ const auto& options = App::get().options(); + updateVScrollbars(options.hideTPVScrollbar); + + Gtk::Box *favoritePanelContainer = +@@ -763,6 +764,7 @@ bool ToolPanelCoordinator::isFavoritable(Tool tool) + + void ToolPanelCoordinator::notebookPageChanged(Gtk::Widget* page, guint page_num) + { ++ const auto& options = App::get().options(); + updatePanelTools(page, options.favorites, options.cloneFavoriteTools); + + // Locallab spot curves are set visible if at least one photo has been loaded (to avoid +@@ -1425,6 +1427,7 @@ void ToolPanelCoordinator::closeImage() + + void ToolPanelCoordinator::closeAllTools() + { ++ const auto& options = App::get().options(); + for (size_t i = 0; i < options.tpOpen.size(); ++i) { + if (i < expList.size()) { + expList[i]->set_expanded(false); +@@ -1434,6 +1437,7 @@ void ToolPanelCoordinator::closeAllTools() + + void ToolPanelCoordinator::openAllTools() + { ++ const auto& options = App::get().options(); + for (size_t i = 0; i < options.tpOpen.size(); ++i) { + if (i < expList.size()) { + expList[i]->set_expanded(true); +@@ -1443,6 +1447,7 @@ void ToolPanelCoordinator::openAllTools() + + void ToolPanelCoordinator::updateToolState() + { ++ const auto& options = App::get().options(); + if (options.tpOpen.empty()) { + for (auto expander : expList) { + expander->set_expanded(false); +@@ -1485,6 +1490,7 @@ void ToolPanelCoordinator::writeOptions() + + crop->writeOptions(); + ++ auto& options = App::get().mut_options(); + if (options.autoSaveTpOpen) { + writeToolExpandedStatus(options.tpOpen); + } +@@ -1956,6 +1962,7 @@ void ToolPanelCoordinator::toolSelected(ToolMode tool) + break; + } + ++ const auto& options = App::get().options(); + updateToolLocations(options.favorites, options.cloneFavoriteTools); + + notebookconn.block(false); +diff --git a/rtgui/vibrance.cc b/rtgui/vibrance.cc +index c928519c5..b554d2a34 100644 +--- a/rtgui/vibrance.cc ++++ b/rtgui/vibrance.cc +@@ -67,6 +67,7 @@ Vibrance::Vibrance () : FoldableToolPanel(this, TOOL_NAME, M("TP_VIBRANCE_LABEL" + pastSatTog->set_active (true); + pack_start(*pastSatTog, Gtk::PACK_SHRINK, 0); + ++ auto& options = App::get().mut_options(); + curveEditorGG = new CurveEditorGroup (options.lastVibranceCurvesDir, M("TP_VIBRANCE_CURVEEDITOR_SKINTONES_LABEL")); + curveEditorGG->setCurveListener (this); + +diff --git a/rtgui/wavelet.cc b/rtgui/wavelet.cc +index f1be5d17d..50217cd4a 100644 +--- a/rtgui/wavelet.cc ++++ b/rtgui/wavelet.cc +@@ -65,22 +65,22 @@ std::vector makeWholeHueRange() + + Wavelet::Wavelet() : + FoldableToolPanel(this, TOOL_NAME, M("TP_WAVELET_LABEL"), true, true), +- curveEditorG(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_CONTEDIT"))), +- //curveEditorC(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_CONTRASTEDIT"))), +- CCWcurveEditorG(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_CCURVE"))), +- curveEditorbl(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_BLCURVE"))), +- curveEditorRES(new CurveEditorGroup(options.lastWaveletCurvesDir)), +- curveEditorGAM(new CurveEditorGroup(options.lastWaveletCurvesDir)), ++ curveEditorG(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_CONTEDIT"))), ++ //curveEditorC(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_CONTRASTEDIT"))), ++ CCWcurveEditorG(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_CCURVE"))), ++ curveEditorbl(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_BLCURVE"))), ++ curveEditorRES(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir)), ++ curveEditorGAM(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir)), + separatorNeutral(Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL))), + separatoredge(Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL))), +- opaCurveEditorG(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_COLORT"))), +- opacityCurveEditorG(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_OPACITY"))), +- CurveEditorwavnoise(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_DENOISE"))), +- CurveEditorwavnoiseh(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_DENOISEH"))), +- CurveEditorwavguid(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_DENOISEGUID"))), +- CurveEditorwavhue(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_DENOISEHUE"))), +- opacityCurveEditorW(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_OPACITYW"))), +- opacityCurveEditorWL(new CurveEditorGroup(options.lastWaveletCurvesDir, M("TP_WAVELET_OPACITYWL"))), ++ opaCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_COLORT"))), ++ opacityCurveEditorG(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_OPACITY"))), ++ CurveEditorwavnoise(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_DENOISE"))), ++ CurveEditorwavnoiseh(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_DENOISEH"))), ++ CurveEditorwavguid(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_DENOISEGUID"))), ++ CurveEditorwavhue(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_DENOISEHUE"))), ++ opacityCurveEditorW(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_OPACITYW"))), ++ opacityCurveEditorWL(new CurveEditorGroup(App::get().mut_options().lastWaveletCurvesDir, M("TP_WAVELET_OPACITYWL"))), + median(Gtk::manage(new Gtk::CheckButton(M("TP_WAVELET_MEDI")))), + medianlev(Gtk::manage(new Gtk::CheckButton(M("TP_WAVELET_MEDILEV")))), + linkedg(Gtk::manage(new Gtk::CheckButton(M("TP_WAVELET_LINKEDG")))), +diff --git a/rtgui/whitebalance.cc b/rtgui/whitebalance.cc +index c0a65f7f4..c8428f257 100644 +--- a/rtgui/whitebalance.cc ++++ b/rtgui/whitebalance.cc +@@ -281,6 +281,7 @@ WhiteBalance::WhiteBalance () : FoldableToolPanel(this, TOOL_NAME, M("TP_WBALANC + setExpandAlignProperties(spotsize, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER); + spotsize->append ("2"); + ++ const auto& options = App::get().options(); + if (options.whiteBalanceSpotSize == 2) { + spotsize->set_active(0); + } +@@ -745,7 +746,7 @@ void WhiteBalance::spotPressed () + + void WhiteBalance::spotSizeChanged () + { +- options.whiteBalanceSpotSize = getSize(); ++ App::get().mut_options().whiteBalanceSpotSize = getSize(); + + if (wblistener) { + wblistener->spotWBRequested (getSize()); +@@ -785,7 +786,7 @@ void WhiteBalance::read (const ProcParams* pp, const ParamsEdited* pedited) + + + +- if(options.rtSettings.itcwb_enable) { ++ if(App::get().options().rtSettings.itcwb_enable) { + itcwb_green->show(); + itcwb_alg->show(); + itcwb_prim->show(); +diff --git a/rtgui/xtransprocess.cc b/rtgui/xtransprocess.cc +index de2e14725..2b2beac24 100644 +--- a/rtgui/xtransprocess.cc ++++ b/rtgui/xtransprocess.cc +@@ -29,7 +29,7 @@ using namespace rtengine::procparams; + + const Glib::ustring XTransProcess::TOOL_NAME = "xtransprocess"; + +-XTransProcess::XTransProcess () : FoldableToolPanel(this, TOOL_NAME, M("TP_RAW_LABEL"), options.prevdemo != PD_Sidecar) ++XTransProcess::XTransProcess () : FoldableToolPanel(this, TOOL_NAME, M("TP_RAW_LABEL"), App::get().options().prevdemo != PD_Sidecar) + { + auto m = ProcEventMapper::getInstance(); + EvDemosaicBorder = m->newEvent(DEMOSAIC, "HISTORY_MSG_RAW_BORDER"); +@@ -74,6 +74,7 @@ XTransProcess::XTransProcess () : FoldableToolPanel(this, TOOL_NAME, M("TP_RAW_L + hb1->pack_end (*method, Gtk::PACK_EXPAND_WIDGET, 4); + pack_start( *hb1, Gtk::PACK_SHRINK, 4); + ++ const auto& options = App::get().options(); + dualDemosaicOptions = Gtk::manage (new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + + dualDemosaicContrast = Gtk::manage(new Adjuster (M("TP_RAW_DUALDEMOSAICCONTRAST"), 0, 100, 1, 20)); +diff --git a/rtgui/xtransrawexposure.cc b/rtgui/xtransrawexposure.cc +index e4c1c55d0..496d0d308 100644 +--- a/rtgui/xtransrawexposure.cc ++++ b/rtgui/xtransrawexposure.cc +@@ -38,6 +38,7 @@ XTransRAWExposure::XTransRAWExposure () : FoldableToolPanel(this, TOOL_NAME, M(" + PexBlackRed = Gtk::manage(new Adjuster (M("TP_RAWEXPOS_BLACK_RED"), -2048, 2048, 1.0, 0)); //black level + PexBlackRed->setAdjusterListener (this); + ++ const auto& options = App::get().options(); + PexBlackRed->setDelay(std::max(options.adjusterMinDelay, options.adjusterMaxDelay)); + + PexBlackRed->show(); +-- +2.50.1 + diff --git a/0001-Fix-typos.patch b/0001-Fix-typos.patch new file mode 100644 index 0000000..bd087fa --- /dev/null +++ b/0001-Fix-typos.patch @@ -0,0 +1,39 @@ +From d12ca94d42c1370d70fb14cc185d46aaca9c05ac Mon Sep 17 00:00:00 2001 +From: Daniel Gao +Date: Sun, 24 Aug 2025 11:37:06 -0400 +Subject: [PATCH] Fix typos + +--- + rtgui/filepanel.cc | 2 +- + rtgui/main.cc | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/rtgui/filepanel.cc b/rtgui/filepanel.cc +index f069f099e..28bee9839 100644 +--- a/rtgui/filepanel.cc ++++ b/rtgui/filepanel.cc +@@ -208,7 +208,7 @@ void FilePanel::init () + dirBrowser->fillDirTree (); + placesBrowser->refreshPlacesList (); + +- if (!App::get().argv0().empty() && Glib::file_test (App::get().argv1(), Glib::FILE_TEST_EXISTS)) { ++ if (!App::get().argv1().empty() && Glib::file_test (App::get().argv1(), Glib::FILE_TEST_EXISTS)) { + Glib::ustring d(App::get().argv1()); + if (!Glib::file_test(d, Glib::FILE_TEST_IS_DIR)) { + d = Glib::path_get_dirname(d); +diff --git a/rtgui/main.cc b/rtgui/main.cc +index ded7076be..180e43f5d 100644 +--- a/rtgui/main.cc ++++ b/rtgui/main.cc +@@ -139,7 +139,7 @@ int processLineParams ( int argc, char **argv ) + if (currParam == "-gimp") { + app.setIsGimpPlugin(true); + app.setIsSimpleEditor(true); +- app.setIsRemote(true); ++ app.setIsRemote(false); + break; + } + +-- +2.51.1 + diff --git a/RT_5.8_fix_canon_sraw.patch b/RT_5.8_fix_canon_sraw.patch deleted file mode 100644 index 285bfef..0000000 --- a/RT_5.8_fix_canon_sraw.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 4221fdf31e43658cef98ad660ad398b8ae81b8d7 Mon Sep 17 00:00:00 2001 -From: Ingo Weyrich -Date: Fri, 7 Feb 2020 13:52:25 +0100 -Subject: [PATCH] fix regression in Canon sRAW/mRAW black level computation, - taken from ART, fixes #5638 - ---- - rtengine/dcraw.cc | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc -index 812f122b3..0284b3f1d 100644 ---- a/rtengine/dcraw.cc -+++ b/rtengine/dcraw.cc -@@ -9868,6 +9868,7 @@ void CLASS identify() - filters = 0; - tiff_samples = colors = 3; - load_raw = &CLASS canon_sraw_load_raw; -+ FORC4 cblack[c] = 0; // ALB - } else if (!strcmp(model,"PowerShot 600")) { - height = 613; - width = 854; diff --git a/RT_5.8_fix_crop.patch b/RT_5.8_fix_crop.patch deleted file mode 100644 index 24cbfdc..0000000 --- a/RT_5.8_fix_crop.patch +++ /dev/null @@ -1,221 +0,0 @@ -From 15db8cdb57161d57af8307bf85aeda02ea69ddde Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fl=C3=B6ssie?= -Date: Sun, 5 Apr 2020 11:26:47 +0200 -Subject: [PATCH] Guard accesses to `Crop::crop_ratios` (#5701) - ---- - rtgui/crop.cc | 125 +++++++++++++++++++++++++++++++++++--------------- - rtgui/crop.h | 11 ++--- - 2 files changed, 92 insertions(+), 44 deletions(-) - -diff --git a/rtgui/crop.cc b/rtgui/crop.cc -index d9d496523..f092f07a5 100644 ---- a/rtgui/crop.cc -+++ b/rtgui/crop.cc -@@ -16,6 +16,8 @@ - * You should have received a copy of the GNU General Public License - * along with RawTherapee. If not, see . - */ -+#include -+ - #include "crop.h" - - #include "options.h" -@@ -46,39 +48,82 @@ inline void get_custom_ratio(int w, int h, double &rw, double &rh) - - } // namespace - -+class Crop::CropRatios final -+{ -+public: -+ CropRatios() : -+ ratios{ -+ {M("GENERAL_ASIMAGE"), 0.0}, -+ {M("GENERAL_CURRENT"), -1.0}, -+ {"3:2", 3.0 / 2.0}, // L1.5, P0.666... -+ {"4:3", 4.0 / 3.0}, // L1.333..., P0.75 -+ {"16:9", 16.0 / 9.0}, // L1.777..., P0.5625 -+ {"16:10", 16.0 / 10.0}, // L1.6, P0.625 -+ {"1:1", 1.0 / 1.0}, // L1, P1 -+ {"2:1", 2.0 / 1.0}, // L2, P0.5 -+ {"3:1", 3.0 / 1.0}, // L3, P0.333... -+ {"4:1", 4.0 / 1.0}, // L4, P0.25 -+ {"5:1", 5.0 / 1.0}, // L5, P0.2 -+ {"6:1", 6.0 / 1.0}, // L6, P0.1666... -+ {"7:1", 7.0 / 1.0}, // L7, P0.142... -+ {"4:5", 4.0 / 5.0}, // L1.25, P0.8 -+ {"5:7", 5.0 / 7.0}, // L1.4, P0.714... -+ {"6:7", 6.0 / 7.0}, // L1.166..., P0.857... -+ {"6:17", 6.0 / 17.0}, // L2.833..., P0.352... -+ {"24:65 - XPAN", 24.0 / 65.0}, // L2.708..., P0.369... -+ {"1.414 - DIN EN ISO 216", 1.414}, // L1.414, P0.707... -+ {"3.5:5", 3.5 / 5.0}, // L1.428..., P0.7 -+ {"8.5:11 - US Letter", 8.5 / 11.0}, // L1.294..., P0.772... -+ {"9.5:12", 9.5 / 12.0}, // L1.263..., P0.791... -+ {"10:12", 10.0 / 12.0}, // L1.2, P0.833... -+ {"11:14", 11.0 / 14.0}, // L1.272..., P0.785... -+ {"11:17 - Tabloid", 11.0 / 17.0}, // L1.545..., P0.647... -+ {"13:19", 13.0 / 19.0}, // L1.461..., P0.684... -+ {"17:22", 17.0 / 22.0}, // L1.294..., P0.772... -+ {"45:35 - ePassport", 45.0 / 35.0}, // L1.285,... P0.777... -+ {"64:27", 64.0 / 27.0}, // L2.370..., P0.421... -+ } -+ { -+ } -+ -+ std::vector getLabels() const -+ { -+ std::vector res; -+ -+ res.reserve(ratios.size()); -+ -+ for (const auto& ratio : ratios) { -+ res.push_back(ratio.label); -+ } -+ -+ return res; -+ } -+ -+ double getValue(std::size_t index) const -+ { -+ return -+ index < ratios.size() -+ ? ratios[index].value -+ : ratios[0].value; -+ } -+ -+ void updateCurrentRatio(double value) -+ { -+ ratios[1].value = value; -+ } -+ -+private: -+ struct CropRatio { -+ Glib::ustring label; -+ double value; -+ }; -+ -+ std::vector ratios; -+}; -+ - Crop::Crop(): - FoldableToolPanel(this, "crop", M("TP_CROP_LABEL"), false, true), -- crop_ratios{ -- {M("GENERAL_ASIMAGE"), 0.0}, -- {M("GENERAL_CURRENT"), -1.0}, -- {"3:2", 3.0 / 2.0}, // L1.5, P0.666... -- {"4:3", 4.0 / 3.0}, // L1.333..., P0.75 -- {"16:9", 16.0 / 9.0}, // L1.777..., P0.5625 -- {"16:10", 16.0 / 10.0}, // L1.6, P0.625 -- {"1:1", 1.0 / 1.0}, // L1, P1 -- {"2:1", 2.0 / 1.0}, // L2, P0.5 -- {"3:1", 3.0 / 1.0}, // L3, P0.333... -- {"4:1", 4.0 / 1.0}, // L4, P0.25 -- {"5:1", 5.0 / 1.0}, // L5, P0.2 -- {"6:1", 6.0 / 1.0}, // L6, P0.1666... -- {"7:1", 7.0 / 1.0}, // L7, P0.142... -- {"4:5", 4.0 / 5.0}, // L1.25, P0.8 -- {"5:7", 5.0 / 7.0}, // L1.4, P0.714... -- {"6:7", 6.0 / 7.0}, // L1.166..., P0.857... -- {"6:17", 6.0 / 17.0}, // L2.833..., P0.352... -- {"24:65 - XPAN", 24.0 / 65.0}, // L2.708..., P0.369... -- {"1.414 - DIN EN ISO 216", 1.414}, // L1.414, P0.707... -- {"3.5:5", 3.5 / 5.0}, // L1.428..., P0.7 -- {"8.5:11 - US Letter", 8.5 / 11.0}, // L1.294..., P0.772... -- {"9.5:12", 9.5 / 12.0}, // L1.263..., P0.791... -- {"10:12", 10.0 / 12.0}, // L1.2, P0.833... -- {"11:14", 11.0 / 14.0}, // L1.272..., P0.785... -- {"11:17 - Tabloid", 11.0 / 17.0}, // L1.545..., P0.647... -- {"13:19", 13.0 / 19.0}, // L1.461..., P0.684... -- {"17:22", 17.0 / 22.0}, // L1.294..., P0.772... -- {"45:35 - ePassport", 45.0 / 35.0}, // L1.285,... P0.777... -- {"64:27", 64.0 / 27.0}, // L2.370..., P0.421... -- }, -+ crop_ratios(new CropRatios), - opt(0), - wDirty(true), - hDirty(true), -@@ -229,8 +274,8 @@ Crop::Crop(): - // ppigrid END - - // Populate the combobox -- for (const auto& crop_ratio : crop_ratios) { -- ratio->append (crop_ratio.label); -+ for (const auto& label : crop_ratios->getLabels()) { -+ ratio->append (label); - } - - ratio->set_active (0); -@@ -354,7 +399,10 @@ void Crop::read (const ProcParams* pp, const ParamsEdited* pedited) - setDimensions (pp->crop.x + pp->crop.w, pp->crop.y + pp->crop.h); - } - -- const bool flip_orientation = pp->crop.fixratio && crop_ratios[ratio->get_active_row_number()].value > 0 && crop_ratios[ratio->get_active_row_number()].value < 1.0; -+ const bool flip_orientation = -+ pp->crop.fixratio -+ && crop_ratios->getValue(ratio->get_active_row_number()) > 0 -+ && crop_ratios->getValue(ratio->get_active_row_number()) < 1.0; - - if (pp->crop.orientation == "Landscape") { - orientation->set_active (flip_orientation ? 1 : 0); -@@ -469,7 +517,10 @@ void Crop::write (ProcParams* pp, ParamsEdited* pedited) - } - - // for historical reasons we store orientation different if ratio is written as 2:3 instead of 3:2, but in GUI 'landscape' is always long side horizontal regardless of the ratio is written short or long side first. -- const bool flip_orientation = fixr->get_active() && crop_ratios[ratio->get_active_row_number()].value > 0 && crop_ratios[ratio->get_active_row_number()].value < 1.0; -+ const bool flip_orientation = -+ fixr->get_active() -+ && crop_ratios->getValue(ratio->get_active_row_number()) > 0 -+ && crop_ratios->getValue(ratio->get_active_row_number()) < 1.0; - - if (orientation->get_active_row_number() == 0) { - pp->crop.orientation = flip_orientation ? "Portrait" : "Landscape"; -@@ -1501,7 +1552,7 @@ double Crop::getRatio () const - return r; - } - -- r = crop_ratios[ratio->get_active_row_number()].value; -+ r = crop_ratios->getValue(ratio->get_active_row_number()); - if (!r) { - r = maxh <= maxw ? float(maxh)/float(maxw) : float(maxw)/float(maxh); - } -@@ -1539,5 +1590,5 @@ void Crop::updateCurrentRatio() - double rw, rh; - get_custom_ratio(w->get_value(), h->get_value(), rw, rh); - customRatioLabel->set_text(Glib::ustring::compose("%1:%2", rw, rh)); -- crop_ratios[1].value = double(w->get_value())/double(h->get_value()); -+ crop_ratios->updateCurrentRatio(static_cast(w->get_value()) / static_cast(h->get_value())); - } -diff --git a/rtgui/crop.h b/rtgui/crop.h -index b9221a803..c6636b917 100644 ---- a/rtgui/crop.h -+++ b/rtgui/crop.h -@@ -18,7 +18,7 @@ - */ - #pragma once - --#include -+#include - - #include - -@@ -91,16 +91,13 @@ class Crop final : - void rotateCrop (int deg, bool hflip, bool vflip); - - private: -- struct CropRatio { -- Glib::ustring label; -- double value; -- }; -- -- std::vector crop_ratios; -+ class CropRatios; - - void adjustCropToRatio(); - void updateCurrentRatio(); - -+ const std::unique_ptr crop_ratios; -+ - Gtk::CheckButton* fixr; - MyComboBoxText* ratio; - MyComboBoxText* orientation; diff --git a/RT_5.8_fix_nikon_z_exif.patch b/RT_5.8_fix_nikon_z_exif.patch deleted file mode 100644 index 524868c..0000000 --- a/RT_5.8_fix_nikon_z_exif.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 84b59f187aa8418e0b5f0986caa6a5a7e5f08136 Mon Sep 17 00:00:00 2001 -From: Anders Bennehag -Date: Sun, 21 Jun 2020 14:08:45 -0400 -Subject: [PATCH] createJPEGMarker(): Removes MakerNotes.ShotInfo from jpeg - export - -as it is 52Kb large on Nikon Z-series cameras, which results in -jpeg exif tags being larger than allowed 65Kb limit. - -Fixes https://github.com/Beep6581/RawTherapee/issues/5698 ---- - rtexif/rtexif.cc | 38 +++++++++++++++++++++++++++++++++++++- - 1 file changed, 37 insertions(+), 1 deletion(-) - -diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc -index c0038c067..f32a9feb1 100644 ---- a/rtexif/rtexif.cc -+++ b/rtexif/rtexif.cc -@@ -3248,7 +3248,43 @@ int ExifManager::createJPEGMarker (const TagDirectory* root, const rtengine::pro - TagDirectory* cl; - - if (root) { -- cl = (const_cast (root))->clone (nullptr); -+ cl = root->clone(nullptr); -+ -+ // Drop unwanted tags before exporting -+ // For example, Nikon Z-series has a 52Kb MakerNotes->ShotInfo tag -+ // which does not fit into the 65Kb limit on JPEG exif tags -+ const Tag* const make_tag = cl->getTag(271); -+ if (make_tag && !std::strncmp((const char*)make_tag->getValue(), "NIKON CORPORATION", 17)) { -+ [cl]() -+ { -+ Tag* const exif_tag = cl->getTag(34665); -+ if (!exif_tag) { -+ return; -+ } -+ -+ TagDirectory* const exif_dir = exif_tag->getDirectory(); -+ if (!exif_dir) { -+ return; -+ } -+ -+ Tag* const make_notes_tag = exif_dir->getTag(37500); -+ if (!make_notes_tag) { -+ return; -+ } -+ -+ TagDirectory* const maker_notes_dir = make_notes_tag->getDirectory(); -+ if (!maker_notes_dir) { -+ return; -+ } -+ -+ Tag* const shot_info_tag = maker_notes_dir->getTag(145); -+ if (!shot_info_tag) { -+ return; -+ } -+ -+ shot_info_tag->setKeep(false); -+ }(); -+ } - } else { - cl = new TagDirectory (nullptr, ifdAttribs, INTEL); - } diff --git a/RT_5.8_glibc234.patch b/RT_5.8_glibc234.patch deleted file mode 100644 index 791183e..0000000 --- a/RT_5.8_glibc234.patch +++ /dev/null @@ -1,683 +0,0 @@ -diff -udHrN -- a/RT_5.8_glibc234.patch b/RT_5.8_glibc234.patch ---- a/RT_5.8_glibc234.patch 1970-01-01 01:00:00.000000000 +0100 -+++ b/RT_5.8_glibc234.patch 2021-08-04 18:50:22.142646367 +0200 -@@ -0,0 +1,334 @@ -+diff --git a/rtengine/canon_cr3_decoder.cc b/rtengine/canon_cr3_decoder.cc -+index ddd4b6172..1132b4e01 100644 -+--- a/rtengine/canon_cr3_decoder.cc -++++ b/rtengine/canon_cr3_decoder.cc -+@@ -670,7 +670,7 @@ std::uint32_t _byteswap_ulong(std::uint32_t x) -+ #endif -+ -+ struct LibRaw_abstract_datastream { -+- IMFILE* ifp; -++ rtengine::IMFILE* ifp; -+ -+ void lock() -+ { -+diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc -+index ef0b4e8dc..13a6b054a 100644 -+--- a/rtengine/dcraw.cc -++++ b/rtengine/dcraw.cc -+@@ -1963,7 +1963,7 @@ void CLASS phase_one_load_raw_c() -+ #endif -+ { -+ int len[2], pred[2]; -+- IMFILE ifpthr = *ifp; -++ rtengine::IMFILE ifpthr = *ifp; -+ ifpthr.plistener = nullptr; -+ -+ #ifdef _OPENMP -+@@ -3317,7 +3317,7 @@ void CLASS sony_arw2_load_raw() -+ { -+ uchar *data = new (std::nothrow) uchar[raw_width + 1]; -+ merror(data, "sony_arw2_load_raw()"); -+- IMFILE ifpthr = *ifp; -++ rtengine::IMFILE ifpthr = *ifp; -+ int pos = ifpthr.pos; -+ ushort pix[16]; -+ -+@@ -6331,7 +6331,7 @@ int CLASS parse_tiff_ifd (int base) -+ unsigned sony_curve[] = { 0,0,0,0,0,4095 }; -+ unsigned *buf, sony_offset=0, sony_length=0, sony_key=0; -+ struct jhead jh; -+-/*RT*/ IMFILE *sfp; -++/*RT*/ rtengine::IMFILE *sfp; -+ /*RT*/ int pana_raw = 0; -+ -+ if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0]) -+@@ -6895,7 +6895,7 @@ it under the terms of the one of two licenses as you choose: -+ fread (buf, sony_length, 1, ifp); -+ sony_decrypt (buf, sony_length/4, 1, sony_key); -+ sfp = ifp; -+-/*RT*/ ifp = fopen (buf, sony_length); -++/*RT*/ ifp = rtengine::fopen (buf, sony_length); -+ // if ((ifp = tmpfile())) { -+ // fwrite (buf, sony_length, 1, ifp); -+ // fseek (ifp, 0, SEEK_SET); -+@@ -7203,7 +7203,7 @@ void CLASS parse_external_jpeg() -+ { -+ const char *file, *ext; -+ char *jname, *jfile, *jext; -+-/*RT*/ IMFILE *save=ifp; -++/*RT*/ rtengine::IMFILE *save=ifp; -+ -+ ext = strrchr (ifname, '.'); -+ file = strrchr (ifname, '/'); -+@@ -7231,7 +7231,7 @@ void CLASS parse_external_jpeg() -+ *jext = '0'; -+ } -+ if (strcmp (jname, ifname)) { -+-/*RT*/ if ((ifp = fopen (jname))) { -++/*RT*/ if ((ifp = rtengine::fopen (jname))) { -+ // if ((ifp = fopen (jname, "rb"))) { -+ if (verbose) -+ fprintf (stderr,_("Reading metadata from %s ...\n"), jname); -+diff --git a/rtengine/dcraw.h b/rtengine/dcraw.h -+index 10c7b9ba6..bc009e67c 100644 -+--- a/rtengine/dcraw.h -++++ b/rtengine/dcraw.h -+@@ -73,7 +73,7 @@ public: -+ -+ protected: -+ int exif_base, ciff_base, ciff_len; -+- IMFILE *ifp; -++ rtengine::IMFILE *ifp; -+ FILE *ofp; -+ short order; -+ const char *ifname; -+@@ -125,7 +125,7 @@ protected: -+ int cur_buf_size; // buffer size -+ uchar *cur_buf; // currently read block -+ int fillbytes; // Counter to add extra byte for block size N*16 -+- IMFILE *input; -++ rtengine::IMFILE *input; -+ struct int_pair grad_even[3][41]; // tables of gradients -+ struct int_pair grad_odd[3][41]; -+ ushort *linealloc; -+@@ -278,7 +278,7 @@ void parse_redcine(); -+ class getbithuff_t -+ { -+ public: -+- getbithuff_t(DCraw *p,IMFILE *&i, unsigned &z):parent(p),bitbuf(0),vbits(0),reset(0),ifp(i),zero_after_ff(z){} -++ getbithuff_t(DCraw *p,rtengine::IMFILE *&i, unsigned &z):parent(p),bitbuf(0),vbits(0),reset(0),ifp(i),zero_after_ff(z){} -+ unsigned operator()(int nbits, ushort *huff); -+ -+ private: -+@@ -288,7 +288,7 @@ private: -+ DCraw *parent; -+ unsigned bitbuf; -+ int vbits, reset; -+- IMFILE *&ifp; -++ rtengine::IMFILE *&ifp; -+ unsigned &zero_after_ff; -+ }; -+ getbithuff_t getbithuff; -+@@ -296,7 +296,7 @@ getbithuff_t getbithuff; -+ class nikbithuff_t -+ { -+ public: -+- explicit nikbithuff_t(IMFILE *&i):bitbuf(0),errors(0),vbits(0),ifp(i){} -++ explicit nikbithuff_t(rtengine::IMFILE *&i):bitbuf(0),errors(0),vbits(0),ifp(i){} -+ void operator()() {bitbuf = vbits = 0;}; -+ unsigned operator()(int nbits, ushort *huff); -+ unsigned errorCount() { return errors; } -+@@ -309,7 +309,7 @@ private: -+ } -+ unsigned bitbuf, errors; -+ int vbits; -+- IMFILE *&ifp; -++ rtengine::IMFILE *&ifp; -+ }; -+ nikbithuff_t nikbithuff; -+ -+@@ -377,7 +377,7 @@ void parse_qt (int end); -+ // ph1_bithuff(int nbits, ushort *huff); -+ class ph1_bithuff_t { -+ public: -+- ph1_bithuff_t(DCraw *p, IMFILE *i, short &o):order(o),ifp(i),bitbuf(0),vbits(0){} -++ ph1_bithuff_t(DCraw *p, rtengine::IMFILE *i, short &o):order(o),ifp(i),bitbuf(0),vbits(0){} -+ unsigned operator()(int nbits, ushort *huff); -+ unsigned operator()(int nbits); -+ unsigned operator()(); -+@@ -411,7 +411,7 @@ private: -+ } -+ -+ short ℴ -+- IMFILE* const ifp; -++ rtengine::IMFILE* const ifp; -+ UINT64 bitbuf; -+ int vbits; -+ }; -+@@ -429,11 +429,11 @@ void nokia_load_raw(); -+ -+ class pana_bits_t{ -+ public: -+- pana_bits_t(IMFILE *i, unsigned &u, unsigned enc): -++ pana_bits_t(rtengine::IMFILE *i, unsigned &u, unsigned enc): -+ ifp(i), load_flags(u), vbits(0), encoding(enc) {} -+ unsigned operator()(int nbits, unsigned *bytes=nullptr); -+ private: -+- IMFILE *ifp; -++ rtengine::IMFILE *ifp; -+ unsigned &load_flags; -+ uchar buf[0x4000]; -+ int vbits; -+diff --git a/rtengine/dfmanager.cc b/rtengine/dfmanager.cc -+index 7dde668eb..e551c9aad 100644 -+--- a/rtengine/dfmanager.cc -++++ b/rtengine/dfmanager.cc -+@@ -540,7 +540,7 @@ std::vector *DFManager::getHotPixels ( const std::string &mak, const std -+ -+ int DFManager::scanBadPixelsFile( Glib::ustring filename ) -+ { -+- FILE *file = fopen( filename.c_str(), "r" ); -++ FILE *file = ::fopen( filename.c_str(), "r" ); -+ -+ if( !file ) { -+ return false; -+diff --git a/rtengine/myfile.cc b/rtengine/myfile.cc -+index 842766dcf..2321d18bb 100644 -+--- a/rtengine/myfile.cc -++++ b/rtengine/myfile.cc -+@@ -70,7 +70,7 @@ int munmap(void *start, size_t length) -+ -+ #ifdef MYFILE_MMAP -+ -+-IMFILE* fopen (const char* fname) -++rtengine::IMFILE* rtengine::fopen (const char* fname) -+ { -+ int fd; -+ -+@@ -123,13 +123,13 @@ IMFILE* fopen (const char* fname) -+ return mf; -+ } -+ -+-IMFILE* gfopen (const char* fname) -++rtengine::IMFILE* rtengine::gfopen (const char* fname) -+ { -+ return fopen(fname); -+ } -+ #else -+ -+-IMFILE* fopen (const char* fname) -++rtengine::IMFILE* rtengine::fopen (const char* fname) -+ { -+ -+ FILE* f = g_fopen (fname, "rb"); -+@@ -152,7 +152,7 @@ IMFILE* fopen (const char* fname) -+ return mf; -+ } -+ -+-IMFILE* gfopen (const char* fname) -++rtengine::IMFILE* rtengine::gfopen (const char* fname) -+ { -+ -+ FILE* f = g_fopen (fname, "rb"); -+@@ -176,7 +176,7 @@ IMFILE* gfopen (const char* fname) -+ } -+ #endif //MYFILE_MMAP -+ -+-IMFILE* fopen (unsigned* buf, int size) -++rtengine::IMFILE* rtengine::fopen (unsigned* buf, int size) -+ { -+ -+ IMFILE* mf = new IMFILE; -+@@ -190,7 +190,7 @@ IMFILE* fopen (unsigned* buf, int size) -+ return mf; -+ } -+ -+-void fclose (IMFILE* f) -++void rtengine::fclose (IMFILE* f) -+ { -+ #ifdef MYFILE_MMAP -+ -+@@ -207,7 +207,7 @@ void fclose (IMFILE* f) -+ delete f; -+ } -+ -+-int fscanf (IMFILE* f, const char* s ...) -++int rtengine::fscanf (IMFILE* f, const char* s ...) -+ { -+ // fscanf not easily wrapped since we have no terminating \0 at end -+ // of file data and vsscanf() won't tell us how many characters that -+@@ -253,7 +253,7 @@ int fscanf (IMFILE* f, const char* s ...) -+ } -+ -+ -+-char* fgets (char* s, int n, IMFILE* f) -++char* rtengine::fgets (char* s, int n, IMFILE* f) -+ { -+ -+ if (f->pos >= f->size) { -+@@ -270,7 +270,7 @@ char* fgets (char* s, int n, IMFILE* f) -+ return s; -+ } -+ -+-void imfile_set_plistener(IMFILE *f, rtengine::ProgressListener *plistener, double progress_range) -++void rtengine::imfile_set_plistener(IMFILE *f, rtengine::ProgressListener *plistener, double progress_range) -+ { -+ f->plistener = plistener; -+ f->progress_range = progress_range; -+@@ -278,7 +278,7 @@ void imfile_set_plistener(IMFILE *f, rtengine::ProgressListener *plistener, doub -+ f->progress_current = 0; -+ } -+ -+-void imfile_update_progress(IMFILE *f) -++void rtengine::imfile_update_progress(IMFILE *f) -+ { -+ if (!f->plistener || f->progress_current < f->progress_next) { -+ return; -+diff --git a/rtengine/myfile.h b/rtengine/myfile.h -+index 34b90c525..ae14ce31a 100644 -+--- a/rtengine/myfile.h -++++ b/rtengine/myfile.h -+@@ -30,8 +30,6 @@ namespace rtengine -+ -+ class ProgressListener; -+ -+-} -+- -+ struct IMFILE { -+ int fd; -+ ssize_t pos; -+@@ -141,3 +139,5 @@ inline unsigned char* fdata(int offset, IMFILE* f) -+ -+ int fscanf (IMFILE* f, const char* s ...); -+ char* fgets (char* s, int n, IMFILE* f); -++ -++} -+diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc -+index 8dc998862..cf500474f 100644 -+--- a/rtengine/rtthumbnail.cc -++++ b/rtengine/rtthumbnail.cc -+@@ -1965,7 +1965,7 @@ bool Thumbnail::writeImage (const Glib::ustring& fname) -+ -+ Glib::ustring fullFName = fname + ".rtti"; -+ -+- FILE* f = g_fopen (fullFName.c_str (), "wb"); -++ FILE* f = ::g_fopen (fullFName.c_str (), "wb"); -+ -+ if (!f) { -+ return false; -+@@ -2008,7 +2008,7 @@ bool Thumbnail::readImage (const Glib::ustring& fname) -+ return false; -+ } -+ -+- FILE* f = g_fopen(fullFName.c_str (), "rb"); -++ FILE* f = ::g_fopen(fullFName.c_str (), "rb"); -+ -+ if (!f) { -+ return false; -+@@ -2239,7 +2239,7 @@ bool Thumbnail::writeData (const Glib::ustring& fname) -+ return false; -+ } -+ -+- FILE *f = g_fopen (fname.c_str (), "wt"); -++ FILE *f = ::g_fopen (fname.c_str (), "wt"); -+ -+ if (!f) { -+ if (settings->verbose) { -+@@ -2262,7 +2262,7 @@ bool Thumbnail::readEmbProfile (const Glib::ustring& fname) -+ embProfile = nullptr; -+ embProfileLength = 0; -+ -+- FILE* f = g_fopen (fname.c_str (), "rb"); -++ FILE* f = ::g_fopen (fname.c_str (), "rb"); -+ -+ if (f) { -+ if (!fseek (f, 0, SEEK_END)) { -+@@ -2290,7 +2290,7 @@ bool Thumbnail::writeEmbProfile (const Glib::ustring& fname) -+ { -+ -+ if (embProfileData) { -+- FILE* f = g_fopen (fname.c_str (), "wb"); -++ FILE* f = ::g_fopen (fname.c_str (), "wb"); -+ -+ if (f) { -+ fwrite (embProfileData, 1, embProfileLength, f); -diff -udHrN -- a/rtengine/canon_cr3_decoder.cc b/rtengine/canon_cr3_decoder.cc ---- a/rtengine/canon_cr3_decoder.cc 2020-02-04 07:39:52.000000000 +0100 -+++ b/rtengine/canon_cr3_decoder.cc 2021-08-07 11:43:50.725202017 +0200 -@@ -662,7 +662,7 @@ - #endif - - struct LibRaw_abstract_datastream { -- IMFILE* ifp; -+ rtengine::IMFILE* ifp; - - void lock() - { -diff -udHrN -- a/rtengine/dcraw.cc b/rtengine/dcraw.cc ---- a/rtengine/dcraw.cc 2020-02-04 07:39:52.000000000 +0100 -+++ b/rtengine/dcraw.cc 2021-08-07 11:43:50.736202141 +0200 -@@ -2025,7 +2025,7 @@ - #endif - { - int len[2], pred[2]; -- IMFILE ifpthr = *ifp; -+ rtengine::IMFILE ifpthr = *ifp; - ifpthr.plistener = nullptr; - - #ifdef _OPENMP -@@ -3380,7 +3380,7 @@ - { - uchar *data = new (std::nothrow) uchar[raw_width + 1]; - merror(data, "sony_arw2_load_raw()"); -- IMFILE ifpthr = *ifp; -+ rtengine::IMFILE ifpthr = *ifp; - int pos = ifpthr.pos; - ushort pix[16]; - -@@ -6394,7 +6394,7 @@ - unsigned sony_curve[] = { 0,0,0,0,0,4095 }; - unsigned *buf, sony_offset=0, sony_length=0, sony_key=0; - struct jhead jh; --/*RT*/ IMFILE *sfp; -+/*RT*/ rtengine::IMFILE *sfp; - /*RT*/ int pana_raw = 0; - - if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0]) -@@ -6958,7 +6958,7 @@ - fread (buf, sony_length, 1, ifp); - sony_decrypt (buf, sony_length/4, 1, sony_key); - sfp = ifp; --/*RT*/ ifp = fopen (buf, sony_length); -+/*RT*/ ifp = rtengine::fopen (buf, sony_length); - // if ((ifp = tmpfile())) { - // fwrite (buf, sony_length, 1, ifp); - // fseek (ifp, 0, SEEK_SET); -@@ -7264,7 +7264,7 @@ - { - const char *file, *ext; - char *jname, *jfile, *jext; --/*RT*/ IMFILE *save=ifp; -+/*RT*/ rtengine::IMFILE *save=ifp; - - ext = strrchr (ifname, '.'); - file = strrchr (ifname, '/'); -@@ -7292,7 +7292,7 @@ - *jext = '0'; - } - if (strcmp (jname, ifname)) { --/*RT*/ if ((ifp = fopen (jname))) { -+/*RT*/ if ((ifp = rtengine::fopen (jname))) { - // if ((ifp = fopen (jname, "rb"))) { - if (verbose) - fprintf (stderr,_("Reading metadata from %s ...\n"), jname); -diff -udHrN -- a/rtengine/dcraw.h b/rtengine/dcraw.h ---- a/rtengine/dcraw.h 2020-02-04 07:39:52.000000000 +0100 -+++ b/rtengine/dcraw.h 2021-08-07 11:43:50.737202152 +0200 -@@ -73,7 +73,7 @@ - - protected: - int exif_base, ciff_base, ciff_len; -- IMFILE *ifp; -+ rtengine::IMFILE *ifp; - FILE *ofp; - short order; - const char *ifname; -@@ -125,7 +125,7 @@ - int cur_buf_size; // buffer size - uchar *cur_buf; // currently read block - int fillbytes; // Counter to add extra byte for block size N*16 -- IMFILE *input; -+ rtengine::IMFILE *input; - struct int_pair grad_even[3][41]; // tables of gradients - struct int_pair grad_odd[3][41]; - ushort *linealloc; -@@ -278,7 +278,7 @@ - class getbithuff_t - { - public: -- getbithuff_t(DCraw *p,IMFILE *&i, unsigned &z):parent(p),bitbuf(0),vbits(0),reset(0),ifp(i),zero_after_ff(z){} -+ getbithuff_t(DCraw *p,rtengine::IMFILE *&i, unsigned &z):parent(p),bitbuf(0),vbits(0),reset(0),ifp(i),zero_after_ff(z){} - unsigned operator()(int nbits, ushort *huff); - - private: -@@ -288,7 +288,7 @@ - DCraw *parent; - unsigned bitbuf; - int vbits, reset; -- IMFILE *&ifp; -+ rtengine::IMFILE *&ifp; - unsigned &zero_after_ff; - }; - getbithuff_t getbithuff; -@@ -296,7 +296,7 @@ - class nikbithuff_t - { - public: -- explicit nikbithuff_t(IMFILE *&i):bitbuf(0),errors(0),vbits(0),ifp(i){} -+ explicit nikbithuff_t(rtengine::IMFILE *&i):bitbuf(0),errors(0),vbits(0),ifp(i){} - void operator()() {bitbuf = vbits = 0;}; - unsigned operator()(int nbits, ushort *huff); - unsigned errorCount() { return errors; } -@@ -309,7 +309,7 @@ - } - unsigned bitbuf, errors; - int vbits; -- IMFILE *&ifp; -+ rtengine::IMFILE *&ifp; - }; - nikbithuff_t nikbithuff; - -@@ -378,7 +378,7 @@ - // ph1_bithuff(int nbits, ushort *huff); - class ph1_bithuff_t { - public: -- ph1_bithuff_t(DCraw *p, IMFILE *i, short &o):order(o),ifp(i),bitbuf(0),vbits(0){} -+ ph1_bithuff_t(DCraw *p, rtengine::IMFILE *i, short &o):order(o),ifp(i),bitbuf(0),vbits(0){} - unsigned operator()(int nbits, ushort *huff); - unsigned operator()(int nbits); - unsigned operator()(); -@@ -412,7 +412,7 @@ - } - - short ℴ -- IMFILE* const ifp; -+ rtengine::IMFILE* const ifp; - UINT64 bitbuf; - int vbits; - }; -@@ -430,11 +430,11 @@ - - class pana_bits_t{ - public: -- pana_bits_t(IMFILE *i, unsigned &u, unsigned enc): -+ pana_bits_t(rtengine::IMFILE *i, unsigned &u, unsigned enc): - ifp(i), load_flags(u), vbits(0), encoding(enc) {} - unsigned operator()(int nbits, unsigned *bytes=nullptr); - private: -- IMFILE *ifp; -+ rtengine::IMFILE *ifp; - unsigned &load_flags; - uchar buf[0x4000]; - int vbits; -diff -udHrN -- a/rtengine/dfmanager.cc b/rtengine/dfmanager.cc ---- a/rtengine/dfmanager.cc 2020-02-04 07:39:52.000000000 +0100 -+++ b/rtengine/dfmanager.cc 2021-08-07 11:43:50.738202163 +0200 -@@ -540,7 +540,7 @@ - - int DFManager::scanBadPixelsFile( Glib::ustring filename ) - { -- FILE *file = fopen( filename.c_str(), "r" ); -+ FILE *file = ::fopen( filename.c_str(), "r" ); - - if( !file ) { - return false; -diff -udHrN -- a/rtengine/myfile.cc b/rtengine/myfile.cc ---- a/rtengine/myfile.cc 2020-02-04 07:39:52.000000000 +0100 -+++ b/rtengine/myfile.cc 2021-08-07 11:43:50.738202163 +0200 -@@ -70,7 +70,7 @@ - - #ifdef MYFILE_MMAP - --IMFILE* fopen (const char* fname) -+rtengine::IMFILE* rtengine::fopen (const char* fname) - { - int fd; - -@@ -123,13 +123,13 @@ - return mf; - } - --IMFILE* gfopen (const char* fname) -+rtengine::IMFILE* rtengine::gfopen (const char* fname) - { - return fopen(fname); - } - #else - --IMFILE* fopen (const char* fname) -+rtengine::IMFILE* rtengine::fopen (const char* fname) - { - - FILE* f = g_fopen (fname, "rb"); -@@ -152,7 +152,7 @@ - return mf; - } - --IMFILE* gfopen (const char* fname) -+rtengine::IMFILE* rtengine::gfopen (const char* fname) - { - - FILE* f = g_fopen (fname, "rb"); -@@ -176,7 +176,7 @@ - } - #endif //MYFILE_MMAP - --IMFILE* fopen (unsigned* buf, int size) -+rtengine::IMFILE* rtengine::fopen (unsigned* buf, int size) - { - - IMFILE* mf = new IMFILE; -@@ -190,7 +190,7 @@ - return mf; - } - --void fclose (IMFILE* f) -+void rtengine::fclose (IMFILE* f) - { - #ifdef MYFILE_MMAP - -@@ -207,7 +207,7 @@ - delete f; - } - --int fscanf (IMFILE* f, const char* s ...) -+int rtengine::fscanf (IMFILE* f, const char* s ...) - { - // fscanf not easily wrapped since we have no terminating \0 at end - // of file data and vsscanf() won't tell us how many characters that -@@ -253,7 +253,7 @@ - } - - --char* fgets (char* s, int n, IMFILE* f) -+char* rtengine::fgets (char* s, int n, IMFILE* f) - { - - if (f->pos >= f->size) { -@@ -270,7 +270,7 @@ - return s; - } - --void imfile_set_plistener(IMFILE *f, rtengine::ProgressListener *plistener, double progress_range) -+void rtengine::imfile_set_plistener(IMFILE *f, rtengine::ProgressListener *plistener, double progress_range) - { - f->plistener = plistener; - f->progress_range = progress_range; -@@ -278,7 +278,7 @@ - f->progress_current = 0; - } - --void imfile_update_progress(IMFILE *f) -+void rtengine::imfile_update_progress(IMFILE *f) - { - if (!f->plistener || f->progress_current < f->progress_next) { - return; -diff -udHrN -- a/rtengine/myfile.h b/rtengine/myfile.h ---- a/rtengine/myfile.h 2020-02-04 07:39:52.000000000 +0100 -+++ b/rtengine/myfile.h 2021-08-07 11:43:50.739202175 +0200 -@@ -30,8 +30,6 @@ - - class ProgressListener; - --} -- - struct IMFILE { - int fd; - ssize_t pos; -@@ -141,3 +139,5 @@ - - int fscanf (IMFILE* f, const char* s ...); - char* fgets (char* s, int n, IMFILE* f); -+ -+} -diff -udHrN -- a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc ---- a/rtengine/rtthumbnail.cc 2020-02-04 07:39:52.000000000 +0100 -+++ b/rtengine/rtthumbnail.cc 2021-08-07 12:02:02.091503230 +0200 -@@ -1922,7 +1922,7 @@ - - Glib::ustring fullFName = fname + ".rtti"; - -- FILE* f = g_fopen (fullFName.c_str (), "wb"); -+ FILE* f = ::g_fopen (fullFName.c_str (), "wb"); - - if (!f) { - return false; -@@ -1965,7 +1965,7 @@ - return false; - } - -- FILE* f = g_fopen(fullFName.c_str (), "rb"); -+ FILE* f = ::g_fopen(fullFName.c_str (), "rb"); - - if (!f) { - return false; -@@ -2191,7 +2191,7 @@ - return false; - } - -- FILE *f = g_fopen (fname.c_str (), "wt"); -+ FILE *f = ::g_fopen (fname.c_str (), "wt"); - - if (!f) { - if (settings->verbose) { -@@ -2214,7 +2214,7 @@ - embProfile = nullptr; - embProfileLength = 0; - -- FILE* f = g_fopen (fname.c_str (), "rb"); -+ FILE* f = ::g_fopen (fname.c_str (), "rb"); - - if (f) { - if (!fseek (f, 0, SEEK_END)) { -@@ -2242,7 +2242,7 @@ - { - - if (embProfileData) { -- FILE* f = g_fopen (fname.c_str (), "wb"); -+ FILE* f = ::g_fopen (fname.c_str (), "wb"); - - if (f) { - fwrite (embProfileData, 1, embProfileLength, f); -@@ -2257,7 +2257,7 @@ - bool Thumbnail::readAEHistogram (const Glib::ustring& fname) - { - -- FILE* f = g_fopen(fname.c_str(), "rb"); -+ FILE* f = ::g_fopen(fname.c_str(), "rb"); - - if (!f) { - aeHistogram.reset(); -@@ -2280,7 +2280,7 @@ - { - - if (aeHistogram) { -- FILE* f = g_fopen (fname.c_str (), "wb"); -+ FILE* f = ::g_fopen (fname.c_str (), "wb"); - - if (f) { - fwrite (&aeHistogram[0], 1, (65536 >> aeHistCompression)*sizeof (aeHistogram[0]), f); diff --git a/RT_5.8_system_klt.patch b/RT_5.8_system_klt.patch deleted file mode 100644 index d6b12b6..0000000 --- a/RT_5.8_system_klt.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 9205b506b50c941ee13c78243f51a2e15d9fe16f Mon Sep 17 00:00:00 2001 -From: Mattia Verga -Date: Sun, 24 Nov 2019 13:48:12 +0100 -Subject: [PATCH] Fix linking against system klt - ---- - rtengine/CMakeLists.txt | 6 ++++++ - rtengine/calc_distort.cc | 4 ++-- - 2 files changed, 8 insertions(+), 2 deletions(-) - -diff --git a/rtengine/CMakeLists.txt b/rtengine/CMakeLists.txt -index f58afde5e..711818a6d 100644 ---- a/rtengine/CMakeLists.txt -+++ b/rtengine/CMakeLists.txt -@@ -12,6 +12,11 @@ include_directories(${EXTRA_INCDIR} - ${LENSFUN_INCLUDE_DIRS} - ${RSVG_INCLUDE_DIRS} - ) -+if(NOT WITH_SYSTEM_KLT) -+ include_directories("${CMAKE_SOURCE_DIR}/rtengine/klt") -+else() -+ include_directories(${KLT_INCLUDE_DIRS}) -+endif() - - link_directories("${PROJECT_SOURCE_DIR}/rtexif" - ${EXPAT_LIBRARY_DIRS} -@@ -184,6 +189,7 @@ target_link_libraries(rtengine rtexif - ${ZLIB_LIBRARIES} - ${LENSFUN_LIBRARIES} - ${RSVG_LIBRARIES} -+ ${KLT_LIBRARIES} - ) - - install(FILES ${CAMCONSTSFILE} DESTINATION "${DATADIR}" PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -diff --git a/rtengine/calc_distort.cc b/rtengine/calc_distort.cc -index 7af47adb2..3a7a4a1c4 100644 ---- a/rtengine/calc_distort.cc -+++ b/rtengine/calc_distort.cc -@@ -5,8 +5,8 @@ locations (before and after tracking) to text files and to PPM files, - and prints the features to the screen. - **********************************************************************/ - --#include "klt/pnmio.h" --#include "klt/klt.h" -+#include -+#include - #include - #include - diff --git a/ReleaseInfo.cmake b/ReleaseInfo.cmake new file mode 100644 index 0000000..6b0573f --- /dev/null +++ b/ReleaseInfo.cmake @@ -0,0 +1,7 @@ +set(GIT_DESCRIBE 5.9) +set(GIT_BRANCH dev) +set(GIT_COMMIT 4059ae5) +set(GIT_COMMIT_DATE 2022-10-02) +set(GIT_COMMITS_SINCE_TAG 0) +set(GIT_COMMITS_SINCE_BRANCH 0) +set(GIT_VERSION_NUMERIC_BS 0.0.0) diff --git a/create_ReleaseInfo.sh b/create_ReleaseInfo.sh new file mode 100755 index 0000000..77079f7 --- /dev/null +++ b/create_ReleaseInfo.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# example usage: ./create_ReleaseInfo.sh 5.9 68ec1a5 2022-05-25 + +VERSION=$1 +SHORTCOMMIT=$2 +DATE=$3 + +echo "set(GIT_DESCRIBE $VERSION)" > ReleaseInfo.cmake +echo "set(GIT_BRANCH dev)" >> ReleaseInfo.cmake +echo "set(GIT_COMMIT $SHORTCOMMIT)" >> ReleaseInfo.cmake +echo "set(GIT_COMMIT_DATE $DATE)" >> ReleaseInfo.cmake +echo "set(GIT_COMMITS_SINCE_TAG 0)" >> ReleaseInfo.cmake +echo "set(GIT_COMMITS_SINCE_BRANCH 0)" >> ReleaseInfo.cmake +echo "set(GIT_VERSION_NUMERIC_BS 0.0.0)" >> ReleaseInfo.cmake diff --git a/rawtherapee.spec b/rawtherapee.spec index 75a5797..5069323 100644 --- a/rawtherapee.spec +++ b/rawtherapee.spec @@ -1,32 +1,40 @@ %global link_tcmalloc 1 +%global development 0 + +%if 0%{?development} +%global commit 4059ae5bcad8e18443bf8f37bcff84de1dcc0d03 +%global shortcommit %(c=%{commit}; echo ${c:0:7}) +%endif + +# https://fedoraproject.org/wiki/Changes/EncourageI686LeafRemoval +ExcludeArch: %{ix86} Name: rawtherapee -Version: 5.8 +%if 0%{?development} +Version: 5.9~20221002git%{shortcommit} +%else +Version: 5.12 +%endif Release: %autorelease Summary: Raw image processing software -License: GPLv3 and MIT and IJG +License: GPL-3.0-or-later and AGPL-3.0-or-later and MIT-open-group and IJG and BSL-1.0 and Apache-2.0 URL: http://www.rawtherapee.com/ -Source0: https://github.com/Beep6581/RawTherapee/releases/download/%{version}/%{name}-%{version}.tar.xz -# Fix linking against system's KLT -# Upstream PR https://github.com/Beep6581/RawTherapee/pull/5539 -Patch: RT_5.8_system_klt.patch +%if 0%{?development} +Source0: https://github.com/Beep6581/RawTherapee/archive/%{commit}/RawTherapee-%{commit}.tar.gz +# File created with ./create_ReleaseInfo.sh 5.9 4059ae5 2022-10-02 +Source1: create_ReleaseInfo.sh +Source2: ReleaseInfo.cmake +%else +#Source0: https://rawtherapee.com/shared/source/%%{name}-%%{version}.tar.xz +Source0: https://github.com/Beep6581/RawTherapee/releases/download/%{version}/rawtherapee-%{version}.tar.xz +%endif -# Backport patch for fixing rhbz#1820907 -Patch: RT_5.8_fix_crop.patch - -# Backport patch for fixing build with glibc 2.34 -# https://github.com/Beep6581/RawTherapee/issues/6324 -Patch: RT_5.8_glibc234.patch - -# Backport patch for fixing Canon sRAW/mRAW -# https://github.com/Beep6581/RawTherapee/issues/5898 -Patch: RT_5.8_fix_canon_sraw.patch - -# Backport patch for fixing Nikon Z EXIF information -# https://github.com/Beep6581/RawTherapee/issues/5808 -Patch: RT_5.8_fix_nikon_z_exif.patch +# Patch to fix segfault at startup +# See upstream ticket https://github.com/RawTherapee/RawTherapee/issues/7532 +Patch: 0001-Fix-static-init-order-fiasco-crashes.patch +Patch: 0001-Fix-typos.patch BuildRequires: cmake BuildRequires: desktop-file-utils @@ -35,20 +43,22 @@ BuildRequires: klt-devel BuildRequires: libappstream-glib BuildRequires: libatomic -BuildRequires: pkgconfig(exiv2) -BuildRequires: pkgconfig(expat) +BuildRequires: pkgconfig(exiv2) >= 0.24 +BuildRequires: pkgconfig(expat) >= 2.1 BuildRequires: pkgconfig(fftw3f) -BuildRequires: pkgconfig(glib-2.0) -BuildRequires: pkgconfig(glibmm-2.4) -BuildRequires: pkgconfig(gtk+-3.0) +BuildRequires: pkgconfig(glib-2.0) >= 2.48 +BuildRequires: pkgconfig(glibmm-2.4) >= 2.48 +BuildRequires: pkgconfig(gtk+-3.0) >= 3.24.3 BuildRequires: pkgconfig(gtkmm-3.0) -BuildRequires: pkgconfig(lcms2) -BuildRequires: pkgconfig(lensfun) +BuildRequires: pkgconfig(lcms2) >= 2.6 +BuildRequires: pkgconfig(lensfun) >= 0.2 BuildRequires: pkgconfig(libcanberra) BuildRequires: pkgconfig(libiptcdata) BuildRequires: pkgconfig(libjpeg) +BuildRequires: pkgconfig(libjxl) BuildRequires: pkgconfig(libpng) -BuildRequires: pkgconfig(librsvg-2.0) +BuildRequires: pkgconfig(libraw) +BuildRequires: pkgconfig(librsvg-2.0) >= 2.52 %if 0%{?link_tcmalloc} BuildRequires: pkgconfig(libtcmalloc) %endif @@ -69,30 +79,32 @@ many parameters to enhance the raw picture before finally exporting it to some common image format. %prep +%if 0%{?development} +%autosetup -p1 -n RawTherapee-%{commit} +cp -p %SOURCE2 . +%else %autosetup -p1 -n %{name}-%{version} - -# fix wrong line endings -sed -i "s|\r||g" LICENSE.txt +%endif # remove bundled KLT, so we're sure to use system provided KLT rm -rf rtengine/klt/ +# remove bundled libraw, so we're sure to use system provided libraw +rm -rf rtengine/libraw/ + %build # do not build shared libs # https://github.com/Beep6581/RawTherapee/pull/5479 -%{cmake} %{?BUILD_TESTS} \ - -DCMAKE_INSTALL_PREFIX=%{_prefix} \ - -DLIBDIR=%{_libdir} \ +%{cmake} \ -DCMAKE_BUILD_TYPE=release \ + -DLIBDIR=%{_libdir} \ -DBUILD_SHARED_LIBS:BOOL=OFF \ - -DAUTOMATED_BUILD_SYSTEM:BOOL=ON \ - -DCACHE_NAME_SUFFIX="" \ - -DCMAKE_CXX_FLAGS="$RPM_OPT_FLAGS" \ - -DCMAKE_C_FLAGS="$RPM_OPT_FLAGS" \ %if 0%{?link_tcmalloc} -DENABLE_TCMALLOC=ON \ %endif - -DWITH_SYSTEM_KLT=ON + -DWITH_SYSTEM_KLT=ON \ + -DWITH_SYSTEM_LIBRAW="ON" \ + -DWITH_JXL="ON" %cmake_build @@ -111,7 +123,8 @@ appstream-util validate-relax --nonet %{buildroot}/%{_datadir}/metainfo/com.%{na %files -%doc AUTHORS.txt LICENSE.txt RELEASE_NOTES.txt +%doc AUTHORS.txt RELEASE_NOTES.txt +%license LICENSE licenses/DroidSansMonoDotted.txt licenses/sleef_LICENSE.txt licenses/jdatasrc %{_mandir}/man1/%{name}.1.gz %{_bindir}/%{name} %{_bindir}/%{name}-cli @@ -119,6 +132,7 @@ appstream-util validate-relax --nonet %{buildroot}/%{_datadir}/metainfo/com.%{na %{_datadir}/applications/%{name}.desktop %{_datadir}/metainfo/com.%{name}.RawTherapee.appdata.xml %{_datadir}/icons/hicolor/*/apps/%{name}.png +%{_datadir}/icons/hicolor/scalable/apps/%{name}.svg %changelog diff --git a/sources b/sources index 90ce232..56607ae 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (rawtherapee-5.8.tar.xz) = bc79586cd937dcaefc053e65faa2162de887d514e44980956d937c157c6d55c4f6e6a7a9b8f91f2d657ed2054814737b96d5d741674c1e5a961d7177433efe4e +SHA512 (rawtherapee-5.12.tar.xz) = 0590a0852385bc85be2a33e5f978904f3a05385b5702f8960adb5aade67d549037245506c84a541d8a49bcc49fe7b71c765be875336cb86deb5c03fd4e3f4c23