From 9ce6a4dd297d47044c15936c89cb27f3d5cf94bf Mon Sep 17 00:00:00 2001
From: Daniel Gao <daniel.gao.work@gmail.com>
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::HaldCLUT> 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<Glib::ustring> dirs = {
-        rt_profile_dir,
-        Glib::build_filename(options.rtdir, "dcpprofiles")
-    };
+    std::deque<Glib::ustring> 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::badPix>* 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<float> &rawData, array2D<float> &red, array2D<float> &green, array2D<float> &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 <daniel.gao.work@gmail.com>
+ *
+ *  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 <https://www.gnu.org/licenses/>.
+ */
+
+#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 <daniel.gao.work@gmail.com>
+ *
+ *  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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <glibmm/ustring.h>
+
+#include <memory>
+
+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<Options> 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<int>(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<BatchQueueEntry*>& 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<float>(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<Gtk::TextBuffer> 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<u><b>", text, "</b></u>\n"));
@@ -403,6 +407,7 @@ void BatchQueuePanel::populateTemplateHelpBuffer(Glib::RefPtr<Gtk::TextBuffer> 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<pathElementCount; n++) {
             auto path1 = Glib::ustring::format("%", letter, offset1+n*mult1);
             auto path2 = Glib::ustring::format("%", letter, offset2+n*mult2);
@@ -465,6 +470,7 @@ void BatchQueuePanel::addBatchQueueJobs(const std::vector<BatchQueueEntry*>& 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<FlatCurveEditor*>(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<GradientMilestone> 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<Cairo::Context> 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<Cairo::Context> 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<FlatCurveEditor*>(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<Gtk::TreeIter> 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<int> *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<int> *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<Gtk::Window*> (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<unsigned>(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<rtengine::IImagefloat*> *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<unsigned>(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 <gtk/gtk.h>
 #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<Glib::ustring> 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<FileBrowserEntry*> 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<FileBrowserEntry*> mselected)
 {
     std::vector<Thumbnail*> 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<Cairo::Context> 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<Glib::ustring> &file_names,
+    std::vector<Glib::RefPtr<Gio::File>> *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<Glib::ustring> FileCatalog::getFileList(std::vector<Glib::RefPtr<Gio::File>> *dirs_explored)
 {
-
     std::vector<Glib::ustring> names;
 
-    const std::set<std::string>& extensions = options.parsedExtensionsSet;
-
-    static void (*getFilesRecursively)(const Glib::ustring &, int, int &, std::vector<Glib::ustring> &, std::vector<Glib::RefPtr<Gio::File>> *) = [](const Glib::ustring &dir_path, int max_depth, int &dir_quota, std::vector<Glib::ustring> &file_names, std::vector<Glib::RefPtr<Gio::File>> * 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<Thumbnail*>& 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<FileBrowserEntry*>& 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<FileBrowserEntry*>& 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<FileBrowserEntry*>& 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<FileBrowserEntry*>& tbe, b
 
 
                     // attempt to copy/move paramFile only if it exist next to the src
-                    Glib::RefPtr<Gio::File> scr_param = Gio::File::create_for_path (  src_fPath + paramFileExtension );
+                    Glib::RefPtr<Gio::File> 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<Gio::File> 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<FileBrowserEntry*>& 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<FileBrowserEntry*>& 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<FileBrowserEntry*>& 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<Gio::File>& file, const Glib::RefPtr<Gio::File>& 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<rtengine::Initial
         }
     }
 
+    const auto& options = App::get().options();
+
     // The purpose of the pendingLoads vector is to open tabs in the same order as the loads where initiated. It has no effect on single editor mode.
     while (pendingLoads.size() > 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<Gtk::TreeStore> &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<Gtk::TreeStore> &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<Cairo::Context> 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<std::uint8_t>(g),
             static_cast<std::uint8_t>(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<Cairo::Context> &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<Cairo::Context> &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<std::uint8_t>(g),
             static_cast<std::uint8_t>(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<FlatCurveEditor*>(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("&lt;") == Glib::ustring::npos && tt.find("&gt;") == 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<DiagonalCurveEditor*>(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<DiagonalCurveEditor*>(llCurveEditorG->addCurve(CT_Diagonal, "L(L)"))),
     ccshape(static_cast<DiagonalCurveEditor*>(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<DiagonalCurveEditor*>(clCurveEditorG->addCurve(CT_Diagonal, "C(L)"))),
     lcshape(static_cast<DiagonalCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(maskCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmaskshape(static_cast<FlatCurveEditor*>(maskCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmaskshape(static_cast<FlatCurveEditor *>(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<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(maskexpCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmaskexpshape(static_cast<FlatCurveEditor*>(maskexpCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmaskexpshape(static_cast<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(maskSHCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmaskSHshape(static_cast<FlatCurveEditor*>(maskSHCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmaskSHshape(static_cast<FlatCurveEditor*>(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<DiagonalCurveEditor*>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(maskvibCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmaskvibshape(static_cast<FlatCurveEditor*>(maskvibCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmaskvibshape(static_cast<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(maskblCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmaskblshape(static_cast<FlatCurveEditor*>(maskblCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmaskblshape(static_cast<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(masktmCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmasktmshape(static_cast<FlatCurveEditor*>(masktmCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmasktmshape(static_cast<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(maskretiCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmaskretishape(static_cast<FlatCurveEditor*>(maskretiCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmaskretishape(static_cast<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(masklcCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmasklcshape(static_cast<FlatCurveEditor*>(masklcCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmasklcshape(static_cast<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(maskcbCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
 LLmaskcbshape(static_cast<FlatCurveEditor*>(maskcbCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
 HHmaskcbshape(static_cast<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(maskCurveEditorL->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmaskshapeL(static_cast<FlatCurveEditor*>(maskCurveEditorL->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmaskshapeL(static_cast<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(mask_CurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmask_shape(static_cast<FlatCurveEditor*>(mask_CurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmask_shape(static_cast<FlatCurveEditor *>(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<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(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<FlatCurveEditor*>(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<DiagonalCurveEditor*>(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<DiagonalCurveEditor*>(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<DiagonalCurveEditor*>(jz1CurveEditorG->addCurve(CT_Diagonal, "Jz(J)"))),
     shapecz(static_cast<DiagonalCurveEditor*>(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<DiagonalCurveEditor*>(jz1CurveEditorG->addCurve(CT_Diagonal, "Cz(J)"))),
     HHshapejz(static_cast<FlatCurveEditor*>(jz3CurveEditorG->addCurve(CT_Flat, "Hz(Hz)", nullptr, false, true))),
     CHshapejz(static_cast<FlatCurveEditor*>(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<FlatCurveEditor*>(maskcieCurveEditorG->addCurve(CT_Flat, "C", nullptr, false, false))),
     LLmaskcieshape(static_cast<FlatCurveEditor*>(maskcieCurveEditorG->addCurve(CT_Flat, "L", nullptr, false, false))),
     HHmaskcieshape(static_cast<FlatCurveEditor*>(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<FlatCurveEditor *>(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<DiagonalCurveEditor*>(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<FlatCurveEditor*>(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<Pango::Context> 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<std::uint8_t>(255 * g),
         static_cast<std::uint8_t>(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<Gtk::IconTheme> 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<char *> (argv0.c_str()) };
+    if (fatalError.empty() && app.isRemote()) {
+        char *app_argv[2] = { const_cast<char *> (app.argv0().c_str()) };
         int app_argc = 1;
 
-        if (!argv1.empty()) {
+        if (!app.argv1().empty()) {
             app_argc = 2;
-            app_argv[1] = const_cast<char *> (argv1.c_str());
+            app_argv[1] = const_cast<char *> (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> 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<std::uint8_t>(g),
                         static_cast<std::uint8_t>(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<double> (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 <Language> for each locale <Language> (<LC>) -- 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<Glib::ustring> 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<std::string> 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 <gtkmm/enums.h>
 #endif
+#include "rtengine/rtapp.h"
 #include "rtengine/settings.h"
 #include <exception>
 
@@ -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<rtengine::procparams::ProcParams>&
 
 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<Gio::File> 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<Gtk::TreeSelection> 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<Glib::ustring>::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<Gtk::CssProvider> themecss;
 Glib::RefPtr<Gtk::CssProvider> 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<Glib::ustring> 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<Gtk::FileFilter> 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<Gtk::FileFilter> 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<GradientMilestone> 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 <iostream>
 #include <librsvg/rsvg.h>
 
+#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<Cairo::ImageSurface> 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<Cairo::ImageSurface> 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<RTWindow*>(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<Gdk::Screen> 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<int> &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::vector<E
         panel.second->updateExternalEditorWidget(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 <glib/gstdio.h>
 
 #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<Glib::ustring, bool> 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<float>(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<GradientMilestone> 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

