[shotwell: 2/3] Introduce profiles



commit 6e092b0f15bce7b9ddccb065833d1a663fbb554e
Author: Jens Georg <mail jensge org>
Date:   Sat Jun 8 23:38:39 2019 +0200

    Introduce profiles
    
    Shotwell can be launched with "--profile/-i" which will then use a
    custom data dir and separate settings
    
    To achive this, the GSettings schema was made relocatable and the path
    for the default profile matches the old paths so nothing should be lost.
    
    Minor caveat: The default library still points to XDG_USER_PICTURES
    which might conflict with another profile
    
    If no --datadir option is given, the data dir is derived from the
    default datadir + the profile name
    
    Implements https://bugzilla.gnome.org/show_bug.cgi?id=716418

 .../org.yorba.shotwell-extras.gschema.xml          |  6 +-
 data/gsettings/org.yorba.shotwell.gschema.xml      | 38 ++++-----
 src/Profiles.vala                                  | 99 ++++++++++++++++++++++
 src/config/Config.vala                             |  2 +-
 src/config/GSettingsEngine.vala                    | 16 +++-
 src/main.vala                                      | 19 ++++-
 src/meson.build                                    |  1 +
 7 files changed, 151 insertions(+), 30 deletions(-)
---
diff --git a/data/gsettings/org.yorba.shotwell-extras.gschema.xml 
b/data/gsettings/org.yorba.shotwell-extras.gschema.xml
index 0ba803dd..a4558538 100644
--- a/data/gsettings/org.yorba.shotwell-extras.gschema.xml
+++ b/data/gsettings/org.yorba.shotwell-extras.gschema.xml
@@ -1,6 +1,6 @@
 <schemalist gettext-domain="shotwell">
 
-<schema id="org.yorba.shotwell.sharing.org-yorba-shotwell-publishing-yandex-fotki" 
path="/org/yorba/shotwell/sharing/org-yorba-shotwell-publishing-yandex-fotki/">
+<schema id="org.yorba.shotwell.sharing.org-yorba-shotwell-publishing-yandex-fotki">
     <key name="auth-token" type="s">
         <default>""</default>
         <summary>authentication token</summary>
@@ -8,7 +8,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.org-yorba-shotwell-publishing-tumblr" 
path="/org/yorba/shotwell/sharing/org-yorba-shotwell-publishing-tumblr/">
+<schema id="org.yorba.shotwell.sharing.org-yorba-shotwell-publishing-tumblr">
     <key name="token" type="s">
         <default>""</default>
         <summary>Token</summary>
@@ -34,7 +34,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.org-yorba-shotwell-publishing-rajce" 
path="/org/yorba/shotwell/sharing/org-yorba-shotwell-publishing-rajce/">
+<schema id="org.yorba.shotwell.sharing.org-yorba-shotwell-publishing-rajce">
     <key name="url" type="s">
         <default>"http://rajce.net/";</default>
         <summary>Rajce URL</summary>
diff --git a/data/gsettings/org.yorba.shotwell.gschema.xml b/data/gsettings/org.yorba.shotwell.gschema.xml
index 6ac75cf5..fa83b97a 100644
--- a/data/gsettings/org.yorba.shotwell.gschema.xml
+++ b/data/gsettings/org.yorba.shotwell.gschema.xml
@@ -1,6 +1,6 @@
 <schemalist gettext-domain="shotwell">
 
-<schema id="org.yorba.shotwell" path="/org/yorba/shotwell/">
+  <schema id="org.yorba.shotwell" path="/org/yorba/shotwell/">
     <child name="preferences" schema="org.yorba.shotwell.preferences" />
     <child name="sharing" schema="org.yorba.shotwell.sharing" />
     <child name="video" schema="org.yorba.shotwell.video" />
@@ -17,7 +17,7 @@
     <child name="export" schema="org.yorba.shotwell.preferences.export" />
 </schema>
 
-<schema id="org.yorba.shotwell.preferences.ui" path="/org/yorba/shotwell/preferences/ui/">
+<schema id="org.yorba.shotwell.preferences.ui">
     <key name="display-basic-properties" type="b">
         <default>true</default>
         <summary>display basic properties</summary>
@@ -187,7 +187,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.preferences.slideshow" path="/org/yorba/shotwell/preferences/slideshow/">
+<schema id="org.yorba.shotwell.preferences.slideshow">
     <key name="delay" type="d">
         <default>3.0</default>
         <summary>delay</summary>
@@ -213,7 +213,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.preferences.window" path="/org/yorba/shotwell/preferences/window/">
+<schema id="org.yorba.shotwell.preferences.window">
     <key name="library-maximize" type="b">
         <default>false</default>
         <summary>maximize library window</summary>
@@ -257,7 +257,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.preferences.files" path="/org/yorba/shotwell/preferences/files/">
+<schema id="org.yorba.shotwell.preferences.files">
     <key name="import-dir" type="s">
         <default>""</default>
         <summary>import directory</summary>
@@ -301,7 +301,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.crop-settings" path="/org/yorba/shotwell/crop-settings/">
+<schema id="org.yorba.shotwell.crop-settings">
     <key name="last-crop-menu-choice" type="i">
         <default>1</default>
         <summary>Most-recently-used crop aspect ratio menu choice.</summary>
@@ -319,7 +319,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.preferences.editing" path="/org/yorba/shotwell/preferences/editing/">
+<schema id="org.yorba.shotwell.preferences.editing">
     <key name="external-photo-editor" type="s">
         <default>""</default>
         <summary>external photo editor</summary>
@@ -364,7 +364,7 @@
     <value value="100" nick="MAXIMUM" />
 </enum>
 
-<schema id="org.yorba.shotwell.preferences.export" path="/org/yorba/shotwell/preferences/export/">
+<schema id="org.yorba.shotwell.preferences.export">
     <key name="constraint" enum="org.yorba.shotwell.ScaleConstraint">
         <default>'ORIGINAL'</default>
         <summary>Setting in export dialog: how to trim images</summary>
@@ -402,7 +402,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing" path="/org/yorba/shotwell/sharing/">
+<schema id="org.yorba.shotwell.sharing">
     <key name="last-used-service" type="s">
         <default>""</default>
         <summary>last used publishing service</summary>
@@ -422,7 +422,7 @@
     <child name="youtube" schema="org.yorba.shotwell.sharing.youtube" />
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.facebook" path="/org/yorba/shotwell/sharing/facebook/">
+<schema id="org.yorba.shotwell.sharing.facebook">
     <key name="access-token" type="s">
         <default>""</default>
         <summary>access token</summary>
@@ -454,7 +454,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.flickr" path="/org/yorba/shotwell/sharing/flickr/">
+<schema id="org.yorba.shotwell.sharing.flickr">
     <key name="access-phase-token" type="s">
         <default>""</default>
         <summary>OAuth Access Phase Token</summary>
@@ -492,7 +492,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.org-gnome-shotwell-publishing-google-photos" 
path="/org/yorba/shotwell/sharing/org-gnome-shotwell-publishing-google-photos/">
+<schema id="org.yorba.shotwell.sharing.org-gnome-shotwell-publishing-google-photos">
     <key name="refresh-token" type="s">
         <default>""</default>
         <summary>refresh token</summary>
@@ -518,7 +518,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.org-yorba-shotwell-publishing-piwigo" 
path="/org/yorba/shotwell/sharing/org-yorba-shotwell-publishing-piwigo/">
+<schema id="org.yorba.shotwell.sharing.org-yorba-shotwell-publishing-piwigo">
     <key name="url" type="s">
         <default>""</default>
         <summary>Piwigo URL</summary>
@@ -580,7 +580,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.publishing-gallery3" path="/org/yorba/shotwell/sharing/gallery3/">
+<schema id="org.yorba.shotwell.sharing.publishing-gallery3">
     <key name="username" type="s">
         <default>""</default>
         <summary>username</summary>
@@ -624,7 +624,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.sharing.youtube" path="/org/yorba/shotwell/sharing/youtube/">
+<schema id="org.yorba.shotwell.sharing.youtube">
     <key name="refresh-token" type="s">
         <default>""</default>
         <summary>refresh token</summary>
@@ -632,7 +632,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.dataimports" path="/org/yorba/shotwell/dataimports/">
+<schema id="org.yorba.shotwell.dataimports">
     <key name="last-used-dataimports-service" type="s">
         <default>""</default>
         <summary>last used import service</summary>
@@ -640,7 +640,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.video" path="/org/yorba/shotwell/video/">
+<schema id="org.yorba.shotwell.video">
     <key name="interpreter-state-cookie" type="i">
         <default>-1</default>
         <summary>interpreter state cookie</summary>
@@ -648,7 +648,7 @@
     </key>
 </schema>
 
-<schema id="org.yorba.shotwell.printing" path="/org/yorba/shotwell/printing/">
+<schema id="org.yorba.shotwell.printing">
     <key name="content-layout" type="i">
         <default>3</default>
         <summary>content layout mode</summary>
@@ -714,7 +714,7 @@
     <child name="enable-state" schema="org.yorba.shotwell.plugins.enable-state" />
 </schema>
 
-<schema id="org.yorba.shotwell.plugins.enable-state" path="/org/yorba/shotwell/plugins/enable-state/" >
+<schema id="org.yorba.shotwell.plugins.enable-state">
     <key name="publishing-facebook" type="b">
         <default>true</default>
         <summary>enable facebook publishing plugin</summary>
diff --git a/src/Profiles.vala b/src/Profiles.vala
new file mode 100644
index 00000000..af2fab26
--- /dev/null
+++ b/src/Profiles.vala
@@ -0,0 +1,99 @@
+/* Copyright 2019 Jens Georg.
+ *
+ * This software is licensed under the GNU LGPL (version 2.1 or later).
+ * See the COPYING file in this distribution.
+ */
+
+namespace Shotwell {
+    class ProfileManager : Object {
+        private static ProfileManager instance;
+        public static ProfileManager get_instance() {
+            if (instance == null)
+                instance = new ProfileManager();
+
+            return instance;
+        }
+
+        private ProfileManager() {
+            Object();
+        }
+
+        private void write() {
+            try {
+                profiles.save_to_file(path);
+            } catch (Error error) {
+                critical("Failed to write profiles: %s", error.message);
+            }
+        }
+
+        private KeyFile profiles;
+        private string profile = null;
+        private string path;
+        private string group_name;
+
+        public override void constructed() {
+            profiles = new KeyFile();
+            path = Path.build_filename(Environment.get_user_config_dir(), "shotwell");
+            DirUtils.create_with_parents(path, 0700);
+            path = Path.build_filename(path, "profiles.ini");
+
+            try {
+                profiles.load_from_file(path, KeyFileFlags.KEEP_COMMENTS);
+            } catch (Error error) {
+                debug("Could not read profiles: %s", error.message);
+            }
+        }
+
+        public void set_profile(string profile) {
+            assert(this.profile == null);
+
+            this.profile = profile;
+            group_name = Base64.encode(profile.data);
+            if (profiles.has_group(group_name))
+                return;
+
+            try {
+                profiles.set_string(group_name, "Name", profile);
+                profiles.set_string(group_name, "Id", Uuid.string_random());
+
+                // Need to set comment after setting keys since it does not create the group
+                profiles.set_comment(group_name, null, "Profile settings for \"%s\"".printf(profile));
+            } catch (Error err) {
+                error("Failed to create profile: %s", err.message);
+            }
+            write();
+        }
+
+        public string derive_data_dir(string? data_dir) {
+            if (data_dir != null) {
+                debug ("Using user-provided data dir %s", data_dir);
+
+                try {
+                    profiles.get_string(group_name, "DataDir");
+                } catch (Error error) {
+                    if (profile != null && profile != "") {
+                        profiles.set_string(group_name ,"DataDir", data_dir);
+                        debug("Using %s as data dir for profile %s", data_dir, profile);
+                        write();
+                    }
+                }
+
+                return data_dir;
+            }
+
+            return Path.build_filename(Environment.get_user_data_dir(), "profiles", id());
+        }
+
+        public string id() {
+            // We are not running on any profile
+            if (profile == null)
+                return "";
+
+            try {
+                return profiles.get_string(group_name, "Id");
+            } catch (Error error) {
+                assert_not_reached();
+            }
+        }
+    }
+}
diff --git a/src/config/Config.vala b/src/config/Config.vala
index 5675567f..f26370f2 100644
--- a/src/config/Config.vala
+++ b/src/config/Config.vala
@@ -28,7 +28,7 @@ public class Facade : ConfigurationFacade {
     public signal void colors_changed();
 
     private Facade() {
-        base(new GSettingsConfigurationEngine());
+        base(new GSettingsConfigurationEngine(Shotwell.ProfileManager.get_instance().id()));
 
         transparent_background_type_changed.connect(on_color_name_changed);
         transparent_background_color_changed.connect(on_color_name_changed);
diff --git a/src/config/GSettingsEngine.vala b/src/config/GSettingsEngine.vala
index dcc05c6d..8541f853 100644
--- a/src/config/GSettingsEngine.vala
+++ b/src/config/GSettingsEngine.vala
@@ -26,8 +26,11 @@ public class GSettingsConfigurationEngine : ConfigurationEngine, GLib.Object {
     private string[] schema_names;
     private string[] key_names;
     private Gee.HashMap<string, Settings> settings_cache = new Gee.HashMap<string, Settings>();
-    
-    public GSettingsConfigurationEngine() {
+
+    private string profile = "";
+
+    public GSettingsConfigurationEngine(string? profile) {
+        this.profile = profile == null ? "" : profile;
         schema_names = new string[ConfigurableProperty.NUM_PROPERTIES];
 
         schema_names[ConfigurableProperty.AUTO_IMPORT_FROM_LIBRARY] = FILES_PREFS_SCHEMA_NAME;
@@ -181,7 +184,14 @@ public class GSettingsConfigurationEngine : ConfigurationEngine, GLib.Object {
 
     private Settings get_settings(string schema) {
         if (!this.settings_cache.has_key(schema)) {
-            this.settings_cache[schema] = new Settings(schema);
+            if (schema.has_prefix (ROOT_SCHEMA_NAME)) {
+                var path = schema.replace(ROOT_SCHEMA_NAME, "");
+                path = "/org/yorba/shotwell/%s%s/".printf(profile == null ? "" : "profiles/" + profile, 
path.replace(".", "/"));
+                path = path.replace("//", "/");
+                this.settings_cache[schema] = new Settings.with_path (schema, path);
+            } else {
+                this.settings_cache[schema] = new Settings(schema);
+            }
         }
 
         return this.settings_cache[schema];
diff --git a/src/main.vala b/src/main.vala
index 4add1df6..6835df04 100644
--- a/src/main.vala
+++ b/src/main.vala
@@ -327,11 +327,12 @@ void editing_exec(string filename, bool fullscreen) {
 namespace CommandlineOptions {
 
 bool no_startup_progress = false;
-string data_dir = null;
+string? data_dir = null;
 bool show_version = false;
 bool no_runtime_monitoring = false;
 bool fullscreen = false;
 bool show_metadata = false;
+string? profile = null;
 
 const OptionEntry[] entries = {
     { "datadir", 'd', 0, OptionArg.FILENAME, ref data_dir, N_("Path to Shotwell’s private data"), 
N_("DIRECTORY") },
@@ -340,6 +341,7 @@ const OptionEntry[] entries = {
     { "version", 'V', 0, OptionArg.NONE, ref show_version, N_("Show the application’s version") },
     { "fullscreen", 'f', 0, OptionArg.NONE, ref fullscreen, N_("Start the application in fullscreen mode"), 
null },
     { "show-metadata", 'p', 0, OptionArg.NONE, ref show_metadata, N_("Print the metadata of the image 
file"), null },
+    { "profile", 'i', 0, OptionArg.STRING, ref profile, N_("Name for a custom profile"), N_("PROFILE") },
     { null, 0, 0, 0, null, null, null }
 };
 }
@@ -367,14 +369,12 @@ void main(string[] args) {
         GLib.Environment.set_variable("GSETTINGS_SCHEMA_DIR", AppDirs.get_lib_dir().get_path() +
             "/data/gsettings", true);
     }
-    
+
     // init GTK (valac has already called g_threads_init())
     try {
         GtkClutter.init_with_args(ref args, _("[FILE]"), CommandlineOptions.entries,
             Resources.APP_GETTEXT_PACKAGE);
 
-        var use_dark = Config.Facade.get_instance().get_gtk_theme_variant();
-        Gtk.Settings.get_default().gtk_application_prefer_dark_theme = use_dark;
     } catch (Error e) {
         print(e.message + "\n");
         print(_("Run “%s --help” to see a full list of available command line options.\n"), args[0]);
@@ -382,6 +382,17 @@ void main(string[] args) {
         return;
     }
 
+    // Setup profile manager
+    if (CommandlineOptions.profile != null) {
+        var manager = Shotwell.ProfileManager.get_instance();
+        manager.set_profile(CommandlineOptions.profile);
+        CommandlineOptions.data_dir = manager.derive_data_dir(CommandlineOptions.data_dir);
+    }
+
+    // Need to set this before anything else, but _after_ setting the profile
+    var use_dark = Config.Facade.get_instance().get_gtk_theme_variant();
+    Gtk.Settings.get_default().gtk_application_prefer_dark_theme = use_dark;
+
     if (CommandlineOptions.show_version) {
         if (Resources.GIT_VERSION != "")
             print("%s %s (%s)\n", Resources.APP_TITLE, Resources.APP_VERSION, Resources.GIT_VERSION);
diff --git a/src/meson.build b/src/meson.build
index 69e721f1..5a38cf91 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -239,6 +239,7 @@ executable('shotwell',
             'dialogs/SetBackground.vala',
             'dialogs/TextEntry.vala',
             'dialogs/WelcomeDialog.vala',
+            'Profiles.vala',
             '.unitize/_UnitInternals.vala',
             '.unitize/_UtilInternals.vala',
             '.unitize/_ThreadsInternals.vala',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]