[geary/wip/775956-dbus-activation: 4/16] Make Geary DBus-activatable. Bug 775956.



commit 9acf5d344d7f55be454adc1d7cb192c266843e62
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Tue Dec 20 18:21:19 2016 +0100

    Make Geary DBus-activatable. Bug 775956.
    
    * org.gnome.Geary.service.in: create, and let it be installed by CMake.
    
    * org.gnome.Geary.desktop.in: add `DBusActivatable=true`.
    
    * geary-autostart.desktop.in: use `--gapplication-service` instead of
    hidden.
    
    * Change the way arguments are parsed:
      * Use the _command-line_ and _handle-local-options_ signals instead
        of _local-command-line_.
      * Remove the `--hidden` option (replaced by `--gapplication-service`)
      * Use VariantDict (which is a little cleaner)
      * Don't use global variables in Arg, but set them in
      the config instead.
      * We can no longer set a global summary due to the new option handling
      in GApplication. On IRC, I got the feedback that info like this should
      be going into a manpage.
    
    * Since the QUIT-action can now be called without ever activating the
    app, make the necessary changes to the `GearyController`:
      * `main_window` and `current_conversations` can be null.
      * use `pending_mailtos()` for the compose action as well.
      * Don't update the UNDO action if we're closing down.
    
    * More instance variables prefixed with this (we're changing the lines
    anyway, might as well do it properly).
    
    * `Environment.set_prgname()` is already executed in GApplication.run(),
    so no more need for `GearyApplication.PRGNAME`.
    
    Signed-off-by: Niels De Graef <nielsdegraef gmail com>

 desktop/geary-autostart.desktop.in                 |   2 +-
 desktop/meson.build                                |  15 ++
 desktop/org.gnome.Geary.desktop.in                 |   1 +
 desktop/org.gnome.Geary.service.in                 |   3 +
 meson.build                                        |   1 +
 src/client/application/geary-application.vala      | 104 ++++++------
 src/client/application/geary-args.vala             | 175 +++++++++++----------
 src/client/application/geary-config.vala           |   9 ++
 src/client/application/geary-controller.vala       |  80 +++++-----
 src/client/components/client-web-view.vala         |   7 +-
 src/client/composer/composer-widget.vala           |   4 +-
 .../conversation-viewer/conversation-message.vala  |   4 +-
 src/client/notification/libnotify.vala             |   2 +-
 .../components/client-web-view-test-case.vala      |   4 +-
 test/client/components/client-web-view-test.vala   |   4 +-
 15 files changed, 217 insertions(+), 198 deletions(-)
---
diff --git a/desktop/geary-autostart.desktop.in b/desktop/geary-autostart.desktop.in
index b226ce3a..56c2f42c 100644
--- a/desktop/geary-autostart.desktop.in
+++ b/desktop/geary-autostart.desktop.in
@@ -7,7 +7,7 @@ Keywords=Email;E-mail;Mail;
 # Translators: Do NOT translate or transliterate this text (this is an icon file name)!
 Icon=org.gnome.Geary
 TryExec=geary
-Exec=geary --hidden
+Exec=geary --gapplication-service
 Type=Application
 Terminal=false
 Categories=GNOME;GTK;Network;Email;
diff --git a/desktop/meson.build b/desktop/meson.build
index 12d1644f..96c71893 100644
--- a/desktop/meson.build
+++ b/desktop/meson.build
@@ -87,3 +87,18 @@ geary_compiled_schema = gnome.compile_schemas(
 install_data('org.gnome.Geary.gschema.xml',
   install_dir: join_paths(datadir, 'glib-2.0', 'schemas'),
 )
+
+#
+# DBus services
+#
+
+service_conf = configuration_data()
+service_conf.set('bindir', bindir)
+
+configure_file(
+  input: 'org.gnome.Geary.service.in',
+  output: 'org.gnome.Geary.service',
+  configuration: service_conf,
+  install: true,
+  install_dir: dbus_services_dir
+)
diff --git a/desktop/org.gnome.Geary.desktop.in b/desktop/org.gnome.Geary.desktop.in
index 8a8f838b..58c36a20 100644
--- a/desktop/org.gnome.Geary.desktop.in
+++ b/desktop/org.gnome.Geary.desktop.in
@@ -13,6 +13,7 @@ Terminal=false
 Categories=GNOME;GTK;Network;Email;
 MimeType=x-scheme-handler/mailto;
 StartupNotify=true
+DBusActivatable=true
 X-GNOME-UsesNotifications=true
 Actions=Compose;
 
diff --git a/desktop/org.gnome.Geary.service.in b/desktop/org.gnome.Geary.service.in
new file mode 100644
index 00000000..75044a85
--- /dev/null
+++ b/desktop/org.gnome.Geary.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Geary
+Exec=@bindir@/geary" --gapplication-service
diff --git a/meson.build b/meson.build
index 23bfa02d..80c73f12 100644
--- a/meson.build
+++ b/meson.build
@@ -27,6 +27,7 @@ locale_dir = join_paths(geary_prefix, get_option('localedir'))
 po_dir = join_paths(meson.source_root(), 'po')
 vapi_dir = join_paths(meson.source_root(), 'bindings', 'vapi')
 metadata_dir = join_paths(meson.source_root(), 'bindings', 'metadata')
+dbus_services_dir = join_paths(datadir, 'dbus-1', 'services')
 web_extensions_dir = join_paths(libdir, 'geary', 'web-extensions')
 
 # Make sure Meson can find our custom VAPI's
diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala
index 6d04b1cd..ae383d73 100644
--- a/src/client/application/geary-application.vala
+++ b/src/client/application/geary-application.vala
@@ -18,7 +18,6 @@ extern const string GETTEXT_PACKAGE;
 public class GearyApplication : Gtk.Application {
 
     public const string NAME = "Geary";
-    public const string PRGNAME = "geary";
     public const string APP_ID = "org.gnome.Geary";
     public const string DESCRIPTION = _("Send and receive email");
     public const string COPYRIGHT_1 = _("Copyright 2016 Software Freedom Conservancy Inc.");
@@ -52,14 +51,14 @@ public class GearyApplication : Gtk.Application {
     public const string ACTION_UNDO = "undo";
 
     // App-wide actions
-    private const string ACTION_ABOUT = "about";
-    private const string ACTION_ACCOUNTS = "accounts";
-    private const string ACTION_COMPOSE = "compose";
-    private const string ACTION_INSPECT = "inspect";
-    private const string ACTION_HELP = "help";
-    private const string ACTION_MAILTO = "mailto";
-    private const string ACTION_PREFERENCES = "preferences";
-    private const string ACTION_QUIT = "quit";
+    public const string ACTION_ABOUT = "about";
+    public const string ACTION_ACCOUNTS = "accounts";
+    public const string ACTION_COMPOSE = "compose";
+    public const string ACTION_INSPECT = "inspect";
+    public const string ACTION_HELP = "help";
+    public const string ACTION_MAILTO = "mailto";
+    public const string ACTION_PREFERENCES = "preferences";
+    public const string ACTION_QUIT = "quit";
 
     private const ActionEntry[] action_entries = {
         {ACTION_ABOUT, on_activate_about},
@@ -127,10 +126,10 @@ public class GearyApplication : Gtk.Application {
      *
      * If this returns `true`, then the primary application instance
      * will continue to run in the background after the last window is
-     * closed, instead of existing as usual.
+     * closed, instead of exiting as usual.
      */
     public bool is_background_service {
-        get { return Args.hidden_startup || this.config.startup_notifications; }
+        get { return (this.flags & ApplicationFlags.IS_SERVICE) != 0; }
     }
 
     private string bin;
@@ -238,57 +237,45 @@ public class GearyApplication : Gtk.Application {
 
     public GearyApplication() {
         Object(
-            application_id: APP_ID
+            application_id: APP_ID,
+            flags: ApplicationFlags.HANDLES_COMMAND_LINE
         );
+        this.add_main_option_entries(Args.OPTION_ENTRIES);
         _instance = this;
     }
 
-    // Application.run() calls this as an entry point.
-    public override bool local_command_line(ref unowned string[] args, out int exit_status) {
-        bin = args[0];
-        exec_dir = (File.new_for_path(Posix.realpath(Environment.find_program_in_path(bin)))).get_parent();
+    public override bool local_command_line(ref unowned string[] args,
+                                            out int exit_status) {
+        this.bin = args[0];
+        string current_path = Posix.realpath(Environment.find_program_in_path(this.bin));
+        this.exec_dir = File.new_for_path(current_path).get_parent();
 
-        try {
-            register();
-        } catch (Error e) {
-            error("Error registering GearyApplication: %s", e.message);
-        }
+        return base.local_command_line(ref args, out exit_status);
+    }
 
-        if (!Args.parse(args)) {
-            exit_status = 1;
-            return true;
-        }
+    public override int handle_local_options(VariantDict options) {
+        return Args.handle_local_options(options);
+    }
 
-        if (!Args.quit) {
-            // Normal application startup or activation
-            activate();
-            foreach (unowned string arg in args) {
-                if (arg != null) {
-                    if (arg == Geary.ComposedEmail.MAILTO_SCHEME)
-                        activate_action(ACTION_COMPOSE, null);
-                    else if (arg.has_prefix(Geary.ComposedEmail.MAILTO_SCHEME))
-                        activate_action(ACTION_MAILTO, new Variant.string(arg));
-                }
-            }
-        } else {
-            // User requested quit, only try to if we aren't running
-            // already.
-            if (this.is_remote) {
-                activate_action(ACTION_QUIT, null);
-            }
-        }
+    public override int command_line(ApplicationCommandLine command_line) {
+        int exit_value = Args.handle_general_options(this.config, command_line.get_options_dict());
+        if (exit_value != -1)
+            return exit_value;
 
-        exit_status = 0;
-        return true;
+        exit_value = Args.handle_arguments(this, command_line.get_arguments());
+        if (exit_value != -1)
+            return exit_value;
+
+        activate();
+
+        return -1;
     }
 
     public override void startup() {
-        Configuration.init(is_installed(), GSETTINGS_DIR);
-
         Environment.set_application_name(NAME);
-        Environment.set_prgname(PRGNAME);
-        International.init(GETTEXT_PACKAGE, bin);
+        International.init(GETTEXT_PACKAGE, this.bin);
 
+        Configuration.init(is_installed(), GSETTINGS_DIR);
         Geary.Logging.init();
         Geary.Logging.log_to(stderr);
         GLib.Log.set_default_handler(Geary.Logging.default_handler);
@@ -301,7 +288,12 @@ public class GearyApplication : Gtk.Application {
         // Ensure all geary windows have an icon
         Gtk.Window.set_default_icon_name(APP_ID);
 
+        this.config = new Configuration(APP_ID);
+
         add_action_entries(action_entries, this);
+
+        // Use a hold() here (if started as a service, we will shutdown after 10s).
+        hold();
     }
 
     public override void activate() {
@@ -340,8 +332,6 @@ public class GearyApplication : Gtk.Application {
         message("%s %s prefix=%s exec_dir=%s is_installed=%s", NAME, VERSION, INSTALL_PREFIX,
             exec_dir.get_path(), is_installed().to_string());
 
-        config = new Configuration(APP_ID);
-
         // Application accels
         add_app_accelerators(ACTION_COMPOSE, { "<Ctrl>N" });
         add_app_accelerators(ACTION_HELP, { "F1" });
@@ -358,7 +348,7 @@ public class GearyApplication : Gtk.Application {
         ComposerWidget.add_window_accelerators(this);
         Components.Inspector.add_window_accelerators(this);
 
-        yield controller.open_async(null);
+        yield this.controller.open_async(null);
 
         release();
     }
@@ -367,11 +357,11 @@ public class GearyApplication : Gtk.Application {
         // see create_async() for reasoning hold/release is used
         hold();
 
-        yield controller.close_async();
+        if (this.controller != null) // If we didn't get activated, controller might be null
+            yield this.controller.close_async();
 
         release();
-
-        is_destroyed = true;
+        this.is_destroyed = true;
     }
 
     public void add_window_accelerators(string action,
@@ -479,7 +469,7 @@ public class GearyApplication : Gtk.Application {
 
     // This call will fire "exiting" only if it's not already been fired.
     public void exit(int exitcode = 0) {
-        if (exiting_fired)
+        if (this.exiting_fired)
             return;
 
         this.exitcode = exitcode;
@@ -580,7 +570,7 @@ public class GearyApplication : Gtk.Application {
 
     private void on_activate_mailto(SimpleAction action, Variant? param) {
         if (this.controller != null && param != null) {
-            this.controller.compose_mailto(param.get_string());
+            this.controller.compose(param.get_string());
         }
     }
 
diff --git a/src/client/application/geary-args.vala b/src/client/application/geary-args.vala
index 1cb4858b..c1c86dbd 100644
--- a/src/client/application/geary-args.vala
+++ b/src/client/application/geary-args.vala
@@ -6,117 +6,120 @@
 
 namespace Args {
 
-private const OptionEntry[] options = {
-    { "hidden", 0, 0, OptionArg.NONE, ref hidden_startup, N_("Start Geary with hidden main window"), null },
-    { "debug", 'd', 0, OptionArg.NONE, ref log_debug, N_("Output debugging information"), null },
-    { "log-conversations", 0, 0, OptionArg.NONE, ref log_conversations, N_("Log conversation monitoring"), 
null },
-    { "log-deserializer", 0, 0, OptionArg.NONE, ref log_deserializer, N_("Log network deserialization"), 
null },
-    { "log-network", 0, 0, OptionArg.NONE, ref log_network, N_("Log network activity"), null },
+// LOCAL OPTIONS
+public const string OPTION_VERSION = "version";
+// GENERAL OPTIONS
+public const string OPTION_LOG_DEBUG = "debug";
+public const string OPTION_LOG_NETWORK = "log-conversations";
+public const string OPTION_LOG_SERIALIZER = "log-deserializer";
+public const string OPTION_LOG_DESERIALIZER = "log-network";
+public const string OPTION_LOG_REPLAY_QUEUE = "log-replay-queue";
+public const string OPTION_LOG_CONVERSATIONS = "log-serializer";
+public const string OPTION_LOG_PERIODIC = "log-periodic";
+public const string OPTION_LOG_SQL = "log-sql";
+public const string OPTION_LOG_FOLDER_NORMALIZATION = "log-folder-normalization";
+public const string OPTION_INSPECTOR = "inspector";
+public const string OPTION_REVOKE_CERTS = "revoke-certs";
+public const string OPTION_QUIT = "quit";
+
+// This is also the order in which they are presented to the user, so it's probably best to keep
+// them alphabetical
+public const OptionEntry[] OPTION_ENTRIES = {
+    { OPTION_LOG_DEBUG, 'd', 0, OptionArg.NONE, null, N_("Output debugging information"), null },
+    { OPTION_INSPECTOR, 'i', 0, OptionArg.NONE, null, N_("Allow inspection of WebView"), null },
+    { OPTION_LOG_CONVERSATIONS, 0, 0, OptionArg.NONE, null, N_("Log conversation monitoring"), null },
+    { OPTION_LOG_DESERIALIZER, 0, 0, OptionArg.NONE, null, N_("Log network deserialization"), null },
+    { OPTION_LOG_NETWORK, 0, 0, OptionArg.NONE, null, N_("Log network activity"), null },
     /// The IMAP replay queue is how changes on the server are replicated on the client.
     /// It could also be called the IMAP events queue.
-    { "log-replay-queue", 0, 0, OptionArg.NONE, ref log_replay_queue, N_("Log IMAP replay queue"), null },
+    { OPTION_LOG_REPLAY_QUEUE, 0, 0, OptionArg.NONE, null, N_("Log IMAP replay queue"), null },
     /// Serialization is how commands and responses are converted into a stream of bytes for
     /// network transmission
-    { "log-serializer", 0, 0, OptionArg.NONE, ref log_serializer, N_("Log network serialization"), null },
-    { "log-periodic", 0, 0, OptionArg.NONE, ref log_periodic, N_("Log periodic activity"), null },
-    { "log-sql", 0, 0, OptionArg.NONE, ref log_sql, N_("Log database queries (generates lots of messages)"), 
null },
+    { OPTION_LOG_SERIALIZER, 0, 0, OptionArg.NONE, null, N_("Log network serialization"), null },
+    { OPTION_LOG_PERIODIC, 0, 0, OptionArg.NONE, null, N_("Log periodic activity"), null },
+    { OPTION_LOG_SQL, 0, 0, OptionArg.NONE, null, N_("Log database queries (generates lots of messages)"), 
null },
     /// "Normalization" can also be called "synchronization"
-    { "log-folder-normalization", 0, 0, OptionArg.NONE, ref log_folder_normalization, N_("Log folder 
normalization"), null },
-    { "inspector", 'i', 0, OptionArg.NONE, ref inspector, N_("Allow inspection of WebView"), null },
-    { "revoke-certs", 0, 0, OptionArg.NONE, ref revoke_certs, N_("Revoke all server certificates with TLS 
warnings"), null },
-    { "quit", 'q', 0, OptionArg.NONE, ref quit, N_("Perform a graceful quit"), null },
-    { "version", 'V', 0, OptionArg.NONE, ref version, N_("Display program version"), null },
+    { OPTION_LOG_FOLDER_NORMALIZATION, 0, 0, OptionArg.NONE, null, N_("Log folder normalization"), null },
+    { OPTION_REVOKE_CERTS, 0, 0, OptionArg.NONE, null, N_("Revoke all server certificates with TLS 
warnings"), null },
+    { OPTION_VERSION, 'V', 0, OptionArg.NONE, null, N_("Display program version"), null },
+    { OPTION_QUIT, 'q', 0, OptionArg.NONE, null, N_("Perform a graceful quit"), null },
+    /// Use this to specify arguments in the help section
+    { "", 0, 0, OptionArg.NONE, null, null, "[mailto:...]"; },
     { null }
 };
 
-public bool hidden_startup = false;
-public bool log_debug = false;
-public bool log_network = false;
-public bool log_serializer = false;
-public bool log_deserializer = false;
-public bool log_replay_queue = false;
-public bool log_conversations = false;
-public bool log_periodic = false;
-public bool log_sql = false;
-public bool log_folder_normalization = false;
-public bool inspector = false;
-public bool quit = false;
-public bool revoke_certs = false;
-public bool version = false;
-
-public bool parse(string[] args) {
-    var context = new OptionContext("[%s...]".printf(Geary.ComposedEmail.MAILTO_SCHEME));
-    context.set_help_enabled(true);
-    context.add_main_entries(options, null);
-    context.set_description("%s\n\n%s\n%s\n\n%s\n\t%s\n".printf(
-        // This gives a command-line hint on how to open new composer windows with mailto:
-        _("Use %s to open a new composer window").printf(Geary.ComposedEmail.MAILTO_SCHEME),
-        GearyApplication.COPYRIGHT_1,
-        GearyApplication.COPYRIGHT_2,
-        _("Please report comments, suggestions and bugs to:"),
-        GearyApplication.BUGREPORT));
-
-    try {
-        context.parse(ref args);
-    } catch (OptionError error) {
-        // i18n: Command line arguments are invalid
-        stdout.printf (_("Failed to parse command line options: %s\n"), error.message);
-        stdout.printf("\n%s", context.get_help(true, null));
-        return false;
+/**
+  * Handles options for a locally running instance, i.e. options for which you don't need to make
+  * a connection to a service instance that is already running.
+  */
+public int handle_local_options(VariantDict local_options) {
+    if (local_options.contains(OPTION_VERSION)) {
+        stdout.printf("%s %s\n", Environment.get_prgname(), GearyApplication.VERSION);
+        return 0;
     }
 
-    // other than the OptionEntry command-line arguments, the only acceptable arguments are
-    // mailto:'s
-    for (int ctr = 1; ctr < args.length; ctr++) {
-        string arg = args[ctr];
-
-        if (!arg.has_prefix(Geary.ComposedEmail.MAILTO_SCHEME)) {
-            stdout.printf(_("Unrecognized command line option ā€œ%sā€\n").printf(arg));
-            stdout.printf("\n%s", context.get_help(true, null));
+    return -1;
+}
 
-            return false;
-        }
-    }
+public int handle_general_options(Configuration config, VariantDict options) {
+    if (options.contains(OPTION_QUIT))
+        return 0;
 
-    if (version) {
-        stdout.printf("%s %s\n", GearyApplication.PRGNAME, GearyApplication.VERSION);
-        Process.exit(0);
+    bool enable_debug = options.contains(OPTION_LOG_DEBUG);
+    // Will be logging to stderr until this point
+    if (enable_debug) {
+        Geary.Logging.log_to(stdout);
+    } else {
+        Geary.Logging.log_to(null);
     }
 
-    if (log_network)
+    // Logging flags
+    if (options.contains(OPTION_LOG_NETWORK))
         Geary.Logging.enable_flags(Geary.Logging.Flag.NETWORK);
-
-    if (log_serializer)
+    if (options.contains(OPTION_LOG_SERIALIZER))
         Geary.Logging.enable_flags(Geary.Logging.Flag.SERIALIZER);
-
-    if (log_replay_queue)
+    if (options.contains(OPTION_LOG_REPLAY_QUEUE))
         Geary.Logging.enable_flags(Geary.Logging.Flag.REPLAY);
-
-    if (log_conversations)
+    if (options.contains(OPTION_LOG_CONVERSATIONS))
         Geary.Logging.enable_flags(Geary.Logging.Flag.CONVERSATIONS);
-
-    if (log_periodic)
+    if (options.contains(OPTION_LOG_PERIODIC))
         Geary.Logging.enable_flags(Geary.Logging.Flag.PERIODIC);
-
-    if (log_sql)
+    if (options.contains(OPTION_LOG_SQL))
         Geary.Logging.enable_flags(Geary.Logging.Flag.SQL);
-
-    if (log_folder_normalization)
+    if (options.contains(OPTION_LOG_FOLDER_NORMALIZATION))
         Geary.Logging.enable_flags(Geary.Logging.Flag.FOLDER_NORMALIZATION);
-
-    if (log_deserializer)
+    if (options.contains(OPTION_LOG_DESERIALIZER))
         Geary.Logging.enable_flags(Geary.Logging.Flag.DESERIALIZER);
 
-    if (log_debug) {
-        Geary.Logging.log_to(stdout);
-    } else {
-        // We'll be logging to stderror until this point, so stop
-        // that.
-        Geary.Logging.log_to(null);
-    }
+    config.enable_debug = enable_debug;
+    config.enable_inspector = options.contains(OPTION_INSPECTOR);
+    config.revoke_certs = options.contains(OPTION_REVOKE_CERTS);
 
-    return true;
+    return -1;
 }
 
+/**
+  * Handles the actual arguments of the application.
+  */
+public int handle_arguments(GearyApplication app, string[] args) {
+    for (int ctr = 1; ctr < args.length; ctr++) {
+        string arg = args[ctr];
+
+        // the only acceptable arguments are mailto:'s
+        if (arg.has_prefix(Geary.ComposedEmail.MAILTO_SCHEME)) {
+            if (arg == Geary.ComposedEmail.MAILTO_SCHEME)
+                app.activate_action(GearyApplication.ACTION_COMPOSE, null);
+            else
+                app.activate_action(GearyApplication.ACTION_MAILTO, new Variant.string(arg));
+        } else {
+            stdout.printf(_("Unrecognized argument: ā€œ%sā€\n").printf(arg));
+            stdout.printf(_("Geary only accepts mailto-links as arguments.\n"));
+
+            return 1;
+        }
+    }
+
+    return -1;
 }
 
+}
diff --git a/src/client/application/geary-config.vala b/src/client/application/geary-config.vala
index 2aef0d2d..17fe8550 100644
--- a/src/client/application/geary-config.vala
+++ b/src/client/application/geary-config.vala
@@ -52,6 +52,15 @@ public class Configuration {
     public Settings settings { get; private set; }
     public Settings gnome_interface { get; private set; }
 
+    // Can be set as an arguments
+    public bool enable_debug { get; set; default = false; }
+
+    // Can be set as an arguments
+    public bool enable_inspector { get; set; default = false; }
+
+    // Can be set as an arguments
+    public bool revoke_certs { get; set; default = false; }
+
     public DesktopEnvironment desktop_environment {
         get {
             string? xdg_current_desktop = Environment.get_variable("XDG_CURRENT_DESKTOP");
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 8e456c23..93eb48f8 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -158,7 +158,7 @@ public class GearyController : Geary.BaseObject {
     private Geary.Nonblocking.Mutex select_folder_mutex = new Geary.Nonblocking.Mutex();
     private Geary.Folder? previous_non_search_folder = null;
     private UpgradeDialog upgrade_dialog;
-    private Gee.List<string> pending_mailtos = new Gee.ArrayList<string>();
+    private Gee.List<string?> pending_mailtos = new Gee.ArrayList<string>();
 
     private uint operation_count = 0;
     private Geary.Revokable? revokable = null;
@@ -254,8 +254,7 @@ public class GearyController : Geary.BaseObject {
         ClientWebView.init_web_context(
             this.application.config,
             this.application.get_web_extensions_dir(),
-            this.application.get_user_cache_directory().get_child("web-resources"),
-            Args.log_debug
+            this.application.get_user_cache_directory().get_child("web-resources")
         );
         try {
             ClientWebView.load_resources(
@@ -420,23 +419,26 @@ public class GearyController : Geary.BaseObject {
         on_conversations_selected(new Gee.HashSet<Geary.App.Conversation>());
         on_folder_selected(null);
 
-        // Disconnect from various UI signals.
-        main_window.conversation_list_view.conversations_selected.disconnect(on_conversations_selected);
-        main_window.conversation_list_view.conversation_activated.disconnect(on_conversation_activated);
-        main_window.conversation_list_view.load_more.disconnect(on_load_more);
-        main_window.conversation_list_view.mark_conversations.disconnect(on_mark_conversations);
-        
main_window.conversation_list_view.visible_conversations_changed.disconnect(on_visible_conversations_changed);
-        main_window.folder_list.folder_selected.disconnect(on_folder_selected);
-        main_window.folder_list.copy_conversation.disconnect(on_copy_conversation);
-        main_window.folder_list.move_conversation.disconnect(on_move_conversation);
-        main_window.main_toolbar.copy_folder_menu.folder_selected.disconnect(on_copy_conversation);
-        main_window.main_toolbar.move_folder_menu.folder_selected.disconnect(on_move_conversation);
-        main_window.conversation_viewer.conversation_added.disconnect(
-            on_conversation_view_added
-        );
+        if (this.main_window != null) {
+            // Disconnect from various UI signals.
+            
this.main_window.conversation_list_view.conversations_selected.disconnect(on_conversations_selected);
+            
this.main_window.conversation_list_view.conversation_activated.disconnect(on_conversation_activated);
+            this.main_window.conversation_list_view.load_more.disconnect(on_load_more);
+            this.main_window.conversation_list_view.mark_conversations.disconnect(on_mark_conversations);
+            
this.main_window.conversation_list_view.visible_conversations_changed.disconnect(on_visible_conversations_changed);
+            this.main_window.folder_list.folder_selected.disconnect(on_folder_selected);
+            this.main_window.folder_list.copy_conversation.disconnect(on_copy_conversation);
+            this.main_window.folder_list.move_conversation.disconnect(on_move_conversation);
+            this.main_window.main_toolbar.copy_folder_menu.folder_selected.disconnect(on_copy_conversation);
+            this.main_window.main_toolbar.move_folder_menu.folder_selected.disconnect(on_move_conversation);
+            this.main_window.conversation_viewer.conversation_added.disconnect(
+                on_conversation_view_added
+            );
 
-        // hide window while shutting down, as this can take a few seconds under certain conditions
-        main_window.hide();
+            // hide window while shutting down, as this can take a few
+            // seconds under certain conditions
+            this.main_window.hide();
+        }
 
         // Release monitoring early so held resources can be freed up
         this.libnotify = null;
@@ -533,9 +535,11 @@ public class GearyController : Geary.BaseObject {
         );
         this.account_manager = null;
 
-        this.application.remove_window(this.main_window);
-        this.main_window.destroy();
-        this.main_window = null;
+        if (this.main_window != null) {
+            this.application.remove_window(this.main_window);
+            this.main_window.destroy();
+            this.main_window = null;
+        }
 
         this.upgrade_dialog = null;
 
@@ -560,17 +564,10 @@ public class GearyController : Geary.BaseObject {
         debug("Closed GearyController");
     }
 
-    /**
-     * Opens a new, blank composer.
-     */
-    public void compose() {
-        create_compose_widget(ComposerWidget.ComposeType.NEW_MESSAGE);
-    }
-
     /**
      * Opens or queues a new composer addressed to a specific email address.
      */
-    public void compose_mailto(string mailto) {
+    public void compose(string? mailto = null) {
         if (current_account == null) {
             // Schedule the send for after we have an account open.
             pending_mailtos.add(mailto);
@@ -861,7 +858,7 @@ public class GearyController : Geary.BaseObject {
                                              Geary.ServiceInformation service,
                                              Geary.Endpoint endpoint,
                                              GLib.TlsConnection cx) {
-        if (Args.revoke_certs) {
+        if (this.application.config.revoke_certs) {
             // XXX
         }
 
@@ -1062,8 +1059,7 @@ public class GearyController : Geary.BaseObject {
     private void display_main_window_if_ready() {
         if (did_attempt_open_all_accounts() &&
             !upgrade_dialog.visible &&
-            !cancellable_open_account.is_cancelled() &&
-            !Args.hidden_startup)
+            !cancellable_open_account.is_cancelled())
             main_window.show();
     }
 
@@ -1184,8 +1180,8 @@ public class GearyController : Geary.BaseObject {
 
             // If we were waiting for an account to be selected before issuing mailtos, do that now.
             if (pending_mailtos.size > 0) {
-                foreach(string mailto in pending_mailtos)
-                    compose_mailto(mailto);
+                foreach(string? mailto in pending_mailtos)
+                    compose(mailto);
 
                 pending_mailtos.clear();
             }
@@ -2530,12 +2526,14 @@ public class GearyController : Geary.BaseObject {
             revokable.committed.connect(on_revokable_committed);
         }
 
-        if (revokable != null && description != null)
-            this.main_window.main_toolbar.undo_tooltip = description;
-        else
-            this.main_window.main_toolbar.undo_tooltip = _("Undo (Ctrl+Z)");
+        if (this.main_window != null) {
+            if (revokable != null && description != null)
+                this.main_window.main_toolbar.undo_tooltip = description;
+            else
+                this.main_window.main_toolbar.undo_tooltip = _("Undo (Ctrl+Z)");
 
-        update_revokable_action();
+            update_revokable_action();
+        }
     }
 
     private void update_revokable_action() {
@@ -3074,7 +3072,7 @@ public class GearyController : Geary.BaseObject {
 
     private void on_link_activated(string uri) {
         if (uri.down().has_prefix(Geary.ComposedEmail.MAILTO_SCHEME)) {
-            compose_mailto(uri);
+            compose(uri);
         } else {
             open_uri(uri);
         }
diff --git a/src/client/components/client-web-view.vala b/src/client/components/client-web-view.vala
index 11864084..e9be8ef6 100644
--- a/src/client/components/client-web-view.vala
+++ b/src/client/components/client-web-view.vala
@@ -73,8 +73,7 @@ public abstract class ClientWebView : WebKit.WebView, Geary.BaseInterface {
      */
     public static void init_web_context(Configuration config,
                                         File web_extension_dir,
-                                        File cache_dir,
-                                        bool enable_logging) {
+                                        File cache_dir) {
         WebsiteDataManager data_manager = new WebsiteDataManager(cache_dir.get_path());
         WebKit.WebContext context = new WebKit.WebContext.with_website_data_manager(data_manager);
         // Use a shared process so we don't spawn N WebProcess instances
@@ -101,7 +100,7 @@ public abstract class ClientWebView : WebKit.WebView, Geary.BaseInterface {
                     web_extension_dir.get_path()
                 );
                 context.set_web_extensions_initialization_user_data(
-                    new Variant.boolean(enable_logging)
+                    new Variant.boolean(config.enable_debug)
                 );
             });
 
@@ -300,7 +299,7 @@ public abstract class ClientWebView : WebKit.WebView, Geary.BaseInterface {
         WebKit.Settings setts = new WebKit.Settings();
         setts.allow_modal_dialogs = false;
         setts.default_charset = "UTF-8";
-        setts.enable_developer_extras = Args.inspector;
+        setts.enable_developer_extras = config.enable_inspector;
         setts.enable_fullscreen = false;
         setts.enable_html5_database = false;
         setts.enable_html5_local_storage = false;
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index e4e6d406..fb7b25fd 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -998,7 +998,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
         }
 
         // User-Agent
-        email.mailer = GearyApplication.PRGNAME + "/" + GearyApplication.VERSION;
+        email.mailer = Environment.get_prgname() + "/" + GearyApplication.VERSION;
 
         return email;
     }
@@ -1968,7 +1968,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
                     if (!this.editor.is_rich_text)
                         append_menu_section(context_menu, section);
                 } else if (section == this.context_menu_inspector) {
-                    if (Args.inspector)
+                    if (this.config.enable_inspector)
                         append_menu_section(context_menu, section);
                 } else {
                     append_menu_section(context_menu, section);
diff --git a/src/client/conversation-viewer/conversation-message.vala 
b/src/client/conversation-viewer/conversation-message.vala
index d1a80711..392cbcf9 100644
--- a/src/client/conversation-viewer/conversation-message.vala
+++ b/src/client/conversation-viewer/conversation-message.vala
@@ -408,7 +408,7 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
         add_action(ACTION_COPY_SELECTION, false).activate.connect(() => {
                 web_view.copy_clipboard();
             });
-        add_action(ACTION_OPEN_INSPECTOR, Args.inspector).activate.connect(() => {
+        add_action(ACTION_OPEN_INSPECTOR, config.enable_inspector).activate.connect(() => {
                 this.web_view.get_inspector().show();
             });
         add_action(ACTION_OPEN_LINK, true, VariantType.STRING)
@@ -429,7 +429,7 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
         context_menu_email = (MenuModel) builder.get_object("context_menu_email");
         context_menu_image = (MenuModel) builder.get_object("context_menu_image");
         context_menu_main = (MenuModel) builder.get_object("context_menu_main");
-        if (Args.inspector) {
+        if (config.enable_inspector) {
             context_menu_inspector =
                 (MenuModel) builder.get_object("context_menu_inspector");
         }
diff --git a/src/client/notification/libnotify.vala b/src/client/notification/libnotify.vala
index dbce30bf..93a2ad99 100644
--- a/src/client/notification/libnotify.vala
+++ b/src/client/notification/libnotify.vala
@@ -26,7 +26,7 @@ public class Libnotify : Geary.BaseObject {
         monitor.add_required_fields(REQUIRED_FIELDS);
 
         if (!Notify.is_initted()) {
-            if (!Notify.init(GearyApplication.PRGNAME))
+            if (!Notify.init(Environment.get_prgname()))
                 message("Failed to initialize libnotify.");
         }
 
diff --git a/test/client/components/client-web-view-test-case.vala 
b/test/client/components/client-web-view-test-case.vala
index 9de2995e..50203047 100644
--- a/test/client/components/client-web-view-test-case.vala
+++ b/test/client/components/client-web-view-test-case.vala
@@ -16,11 +16,11 @@ public abstract class ClientWebViewTestCase<V> : TestCase {
     protected ClientWebViewTestCase(string name) {
         base(name);
         this.config = new Configuration(GearyApplication.APP_ID);
+        this.config.enable_debug = true;
         ClientWebView.init_web_context(
             this.config,
             File.new_for_path(_BUILD_ROOT_DIR).get_child("src"),
-            File.new_for_path("/tmp"), // XXX use something better here
-            true
+            File.new_for_path("/tmp") // XXX use something better here
         );
         try {
             ClientWebView.load_resources(GLib.File.new_for_path("/tmp"));
diff --git a/test/client/components/client-web-view-test.vala 
b/test/client/components/client-web-view-test.vala
index 661cac9c..8dad252e 100644
--- a/test/client/components/client-web-view-test.vala
+++ b/test/client/components/client-web-view-test.vala
@@ -15,11 +15,11 @@ public class ClientWebViewTest : TestCase {
 
     public void init_web_context() throws Error {
         Configuration config = new Configuration(GearyApplication.APP_ID);
+        config.enable_debug = true;
         ClientWebView.init_web_context(
             config,
             File.new_for_path(_BUILD_ROOT_DIR).get_child("src"),
-            File.new_for_path("/tmp"), // XXX use something better here
-            true
+            File.new_for_path("/tmp") // XXX use something better here
         );
     }
 


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