Commit 6e092b0f authored by Jens Georg's avatar Jens Georg

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
parent f062469a
<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>
......
<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>
......
/* 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();
}
}
}
}
......@@ -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);
......
......@@ -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];
......
......@@ -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);
......
......@@ -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',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment