[gnome-builder/wip/chergert/perspective] libide: start on tool abstraction



commit eac271cfcf1e2e2d792c28e40b6091eb873c6d3f
Author: Christian Hergert <chergert redhat com>
Date:   Wed Nov 18 04:55:06 2015 -0800

    libide: start on tool abstraction
    
    One of the last major parts to get solved is how we will handle tools/
    now that libgnome-builder is gone.
    
    To simplify the creation of tools, they can now be implemented as plugins.
    Simply define X-Tool-Name: in your plugin definition and implement
    IdeApplicationTool. Then the `ide' program can be used to execute your
    tool such as "ide build <args>".
    
    We still need to port the tools over, but this lays the framework for that
    to happen and continue using IdeApplication.

 data/icons/hicolor/Makefile.am        |   10 +-
 data/keybindings/shared.css           |    9 +
 libide/Makefile.am                    |    6 +
 libide/ide-application-command-line.c |  369 ++++++++++++++
 libide/ide-application-plugins.c      |  189 +++++++
 libide/ide-application-private.h      |   42 +-
 libide/ide-application-tool.c         |   51 ++
 libide/ide-application-tool.h         |   55 ++
 libide/ide-application.c              |  906 +++++++++------------------------
 libide/ide-application.h              |   45 +-
 libide/ide-css-provider.c             |    2 +-
 libide/ide-keybindings.c              |    2 +-
 libide/ide-workbench.c                |   20 +
 src/main.c                            |   34 +--
 14 files changed, 990 insertions(+), 750 deletions(-)
---
diff --git a/data/icons/hicolor/Makefile.am b/data/icons/hicolor/Makefile.am
index 57f3591..2e9b36f 100644
--- a/data/icons/hicolor/Makefile.am
+++ b/data/icons/hicolor/Makefile.am
@@ -6,16 +6,16 @@ EXTRA_DIST =
 noinst_LTLIBRARIES = libicons.la
 
 nodist_libicons_la_SOURCES = \
-       gb-icons-resources.c \
-       gb-icons-resources.h
+       ide-icons-resources.c \
+       ide-icons-resources.h
 
 libicons_la_CFLAGS = $(ICONS_CFLAGS)
 libicons_la_LIBADD = $(ICONS_LIBS)
 
-glib_resources_c = gb-icons-resources.c
-glib_resources_h = gb-icons-resources.h
+glib_resources_c = ide-icons-resources.c
+glib_resources_h = ide-icons-resources.h
 glib_resources_xml = icons.gresource.xml
-glib_resources_namespace = gb_icons
+glib_resources_namespace = ide_icons
 include $(top_srcdir)/build/autotools/Makefile.am.gresources
 
 -include $(top_srcdir)/git.mk
diff --git a/data/keybindings/shared.css b/data/keybindings/shared.css
index ace9992..db2d02a 100644
--- a/data/keybindings/shared.css
+++ b/data/keybindings/shared.css
@@ -29,6 +29,15 @@ VteTerminal {
 
 }
 
+ binding-set builder-workbench-bindings
+{
+  bind "<ctrl>comma" { "set-perspective" ("preferences") };
+}
+
 entry.gb-command-bar-entry {
   gtk-key-bindings: builder-command-bar-entry;
 }
+
+workbench {
+  gtk-key-bindings: builder-workbench-bindings;
+}
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 1d5d3a7..e488eb4 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -27,6 +27,8 @@ libide_1_0_la_public_sources = \
        ide-application.h \
        ide-application-addin.c \
        ide-application-addin.h \
+       ide-application-tool.c \
+       ide-application-tool.h \
        ide-back-forward-item.c \
        ide-back-forward-item.h \
        ide-back-forward-list.c \
@@ -260,8 +262,10 @@ libide_1_0_la_SOURCES = \
        gsettings/ide-gsettings-file-settings.h \
        gsettings/ide-language-defaults.c \
        gsettings/ide-language-defaults.h \
+       ide-application-command-line.c \
        ide-application-actions.c \
        ide-application-actions.h \
+       ide-application-plugins.c \
        ide-application-private.h \
        ide-async-helper.c \
        ide-async-helper.h \
@@ -370,6 +374,7 @@ libide_1_0_la_includes = \
        -I$(top_srcdir)/contrib/libeditorconfig \
        -I$(top_srcdir)/contrib/search \
        -I$(top_srcdir)/contrib/xml \
+       -I$(top_srcdir)/data/icons/hicolor \
        -I$(srcdir) \
        -I$(srcdir)/directory \
        -I$(srcdir)/doap \
@@ -417,6 +422,7 @@ libide_1_0_la_LIBADD = \
        $(LIBIDE_LIBS) \
        $(SHM_LIB) \
        -lm \
+       $(top_builddir)/data/icons/hicolor/libicons.la \
        $(top_builddir)/contrib/egg/libegg-private.la \
        $(top_builddir)/contrib/gd/libgd.la \
        $(top_builddir)/contrib/libeditorconfig/libeditorconfig.la \
diff --git a/libide/ide-application-command-line.c b/libide/ide-application-command-line.c
new file mode 100644
index 0000000..d48f1c2
--- /dev/null
+++ b/libide/ide-application-command-line.c
@@ -0,0 +1,369 @@
+/* ide-application-command-line.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib/gi18n.h>
+#include <girepository.h>
+#include <libpeas/peas.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "ide-application.h"
+#include "ide-application-private.h"
+#include "ide-log.h"
+
+static PeasPluginInfo *
+ide_application_locate_tool (IdeApplication *self,
+                             const gchar    *tool_name)
+{
+  PeasEngine *engine;
+  const GList *list;
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (tool_name != NULL);
+
+  engine = peas_engine_get_default ();
+  list = peas_engine_get_plugin_list (engine);
+
+  for (; list != NULL; list = list->next)
+    {
+      PeasPluginInfo *plugin_info = list->data;
+      const gchar *name;
+
+      name = peas_plugin_info_get_external_data (plugin_info, "Tool-Name");
+      if (g_strcmp0 (name, tool_name) == 0)
+        return plugin_info;
+    }
+
+  return NULL;
+}
+
+static PeasPluginInfo *
+ide_application_locate_worker (IdeApplication *self,
+                               const gchar    *worker_name)
+{
+  PeasEngine *engine;
+  const GList *list;
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (worker_name != NULL);
+
+  engine = peas_engine_get_default ();
+  list = peas_engine_get_plugin_list (engine);
+
+  for (; list != NULL; list = list->next)
+    {
+      PeasPluginInfo *plugin_info = list->data;
+      const gchar *name;
+
+      name = peas_plugin_info_get_module_name (plugin_info);
+      if (g_strcmp0 (name, worker_name) == 0)
+        return plugin_info;
+    }
+
+  return NULL;
+}
+
+static gchar *
+ide_application_get_command_help (IdeApplication *self,
+                                  gboolean        long_form)
+{
+  PeasEngine *engine;
+  const GList *list;
+  GString *str;
+  gint count = 0;
+
+  g_assert (IDE_IS_APPLICATION (self));
+
+  engine = peas_engine_get_default ();
+  list = peas_engine_get_plugin_list (engine);
+
+  str = g_string_new (NULL);
+
+  if (long_form)
+    g_string_append_printf (str, "%s\n", _("Commands:"));
+
+  for (; list != NULL; list = list->next)
+    {
+      PeasPluginInfo *plugin_info = list->data;
+      const gchar *name;
+      const gchar *desc;
+
+      name = peas_plugin_info_get_external_data (plugin_info, "Tool-Name");
+      desc = peas_plugin_info_get_external_data (plugin_info, "Tool-Description");
+
+      if (name != NULL)
+        {
+          if (long_form)
+            g_string_append_printf (str, "  %-25s %s\n", name, desc);
+          else
+            g_string_append_printf (str, "%s\n", name);
+
+          count++;
+        }
+    }
+
+  if (count == 0)
+    {
+      g_string_free (str, TRUE);
+      return NULL;
+    }
+
+  return g_strstrip (g_string_free (str, FALSE));
+}
+
+static gboolean
+ide_application_increase_verbosity (void)
+{
+  ide_log_increase_verbosity ();
+  return TRUE;
+}
+
+gboolean
+ide_application_local_command_line (GApplication   *application,
+                                    gchar        ***arguments,
+                                    gint           *exit_status)
+{
+  IdeApplication *self = (IdeApplication *)application;
+  GOptionContext *context = NULL;
+  GOptionGroup *group;
+  const gchar *shortdesc = NULL;
+  GError *error = NULL;
+  gchar *type = NULL;
+  gchar *dbus_address = NULL;
+  gboolean standalone = FALSE;
+  gboolean version = FALSE;
+  gboolean list_commands = FALSE;
+
+  GOptionEntry entries[] = {
+    { "list-commands",
+      0,
+      G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_NONE,
+      &list_commands},
+
+    { "standalone",
+      's',
+      G_OPTION_FLAG_NONE,
+      G_OPTION_ARG_NONE,
+      &standalone,
+      N_("Run Builder in standalone mode") },
+
+    { "version",
+      'V',
+      G_OPTION_FLAG_NONE,
+      G_OPTION_ARG_NONE,
+      &version,
+      N_("Show the application's version") },
+
+    { "type",
+      0,
+      G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_STRING,
+      &type },
+
+    { "dbus-address",
+      0,
+      G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_STRING,
+      &dbus_address},
+
+    { "verbose",
+      'v',
+      G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_IN_MAIN,
+      G_OPTION_ARG_CALLBACK,
+      ide_application_increase_verbosity,
+      N_("Increase verbosity. May be specified multiple times.") },
+
+    { NULL }
+  };
+
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (arguments != NULL);
+  g_assert (exit_status != NULL);
+
+  *exit_status = EXIT_SUCCESS;
+
+  if (g_str_equal (g_get_prgname (), "ide"))
+    shortdesc = _("COMMAND");
+
+  context = g_option_context_new (shortdesc);
+
+  g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+
+  group = gtk_get_option_group (TRUE);
+  g_option_context_add_group (context, group);
+
+  group = g_irepository_get_option_group ();
+  g_option_context_add_group (context, group);
+
+  ide_application_discover_plugins (self);
+
+  /*
+   * If we are the "ide" program, then we want to setup ourselves for
+   * verb style commands and add a commands group for help.
+   */
+  if (g_str_equal (g_get_prgname (), "ide"))
+    {
+      gchar *command_help;
+
+      self->mode = IDE_APPLICATION_MODE_TOOL;
+
+      g_option_context_set_strict_posix (context, TRUE);
+
+      command_help = ide_application_get_command_help (self, TRUE);
+      g_option_context_set_summary (context, command_help);
+      g_free (command_help);
+    }
+  else if (g_str_equal (g_get_prgname (), "gnome-builder-worker"))
+    {
+      self->mode = IDE_APPLICATION_MODE_WORKER;
+    }
+
+  if (!g_option_context_parse_strv (context, arguments, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      *exit_status = EXIT_FAILURE;
+      goto cleanup;
+    }
+
+  if (list_commands)
+    {
+      gchar *command_help;
+
+      command_help = ide_application_get_command_help (self, FALSE);
+      g_print ("%s\n", command_help ?: _("No commands available"));
+      g_free (command_help);
+
+      *exit_status = 0;
+      goto cleanup;
+    }
+
+  if (standalone || (self->mode != IDE_APPLICATION_MODE_PRIMARY))
+    {
+      GApplicationFlags flags;
+
+      flags = g_application_get_flags (application);
+      g_application_set_flags (application, flags | G_APPLICATION_NON_UNIQUE);
+    }
+
+  if (version)
+    {
+      g_print (PACKAGE_STRING"\n");
+      *exit_status = EXIT_SUCCESS;
+      goto cleanup;
+    }
+
+  if (self->mode == IDE_APPLICATION_MODE_TOOL)
+    {
+      PeasPluginInfo *tool_plugin;
+      const gchar *tool_name;
+
+      if (g_strv_length (*arguments) < 2)
+        {
+          g_printerr ("%s\n", _("Please provide a command"));
+          *exit_status = EXIT_FAILURE;
+          goto cleanup;
+        }
+
+      tool_name = (*arguments) [1];
+      tool_plugin = ide_application_locate_tool (self, tool_name);
+
+      if (tool_plugin == NULL)
+        {
+          g_printerr ("%s: \"%s\"\n", _("No such tool"), tool_name);
+          *exit_status = EXIT_FAILURE;
+          goto cleanup;
+        }
+
+      self->tool = tool_plugin;
+      self->tool_arguments = g_strdupv (*arguments);
+    }
+  else if (self->mode == IDE_APPLICATION_MODE_WORKER)
+    {
+      PeasPluginInfo *worker_plugin;
+
+      if (type == NULL)
+        {
+          g_printerr ("%s\n", _("Please provide a worker type"));
+          *exit_status = EXIT_FAILURE;
+          goto cleanup;
+        }
+
+      if (dbus_address== NULL)
+        {
+          g_printerr ("%s\n", _("Please provide a dbus address"));
+          *exit_status = EXIT_FAILURE;
+          goto cleanup;
+        }
+
+      worker_plugin = ide_application_locate_worker (self, type);
+
+      if (worker_plugin == NULL)
+        {
+          g_printerr ("%s: \"%s\"\n", _("No such worker"), type);
+          *exit_status = EXIT_FAILURE;
+          goto cleanup;
+        }
+
+      self->worker = worker_plugin;
+      self->dbus_address = g_strdup (dbus_address);
+    }
+
+  ide_application_load_plugins (self);
+
+  if (!g_application_register (application, NULL, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      *exit_status = EXIT_FAILURE;
+      goto cleanup;
+    }
+
+  if (self->mode == IDE_APPLICATION_MODE_PRIMARY)
+    {
+      g_autoptr(GPtrArray) files = NULL;
+      gint i;
+
+      files = g_ptr_array_new_with_free_func (g_object_unref);
+
+      for (i = 1; (*arguments) [i]; i++)
+        {
+          GFile *file;
+
+          file = g_file_new_for_commandline_arg ((*arguments) [i]);
+          if (file != NULL)
+            g_ptr_array_add (files, file);
+        }
+
+      if (files->len > 0)
+        g_application_open (G_APPLICATION (self), (GFile **)files->pdata, files->len, "");
+    }
+
+  g_application_activate (application);
+
+cleanup:
+  g_clear_pointer (&type, g_free);
+  g_clear_pointer (&dbus_address, g_free);
+  g_clear_error (&error);
+  g_option_context_free (context);
+
+  return TRUE;
+}
diff --git a/libide/ide-application-plugins.c b/libide/ide-application-plugins.c
new file mode 100644
index 0000000..1ae1f98
--- /dev/null
+++ b/libide/ide-application-plugins.c
@@ -0,0 +1,189 @@
+/* ide-application-plugins.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libpeas/peas.h>
+#include <girepository.h>
+
+#include "ide-application.h"
+#include "ide-application-addin.h"
+#include "ide-application-private.h"
+#include "ide-macros.h"
+
+static gboolean
+ide_application_can_load_plugin (IdeApplication *self,
+                                 PeasPluginInfo *plugin_info)
+{
+  g_assert (IDE_IS_APPLICATION (self));
+  g_assert (plugin_info != NULL);
+
+  if (self->mode == IDE_APPLICATION_MODE_WORKER)
+    {
+      if (self->worker != plugin_info)
+        return FALSE;
+    }
+
+  if (self->mode == IDE_APPLICATION_MODE_TOOL)
+    {
+      if (self->tool != plugin_info)
+        return FALSE;
+    }
+
+  /*
+   * TODO: Do ABI check on external data.
+   */
+
+  return TRUE;
+}
+
+void
+ide_application_discover_plugins (IdeApplication *self)
+{
+  PeasEngine *engine = peas_engine_get_default ();
+  const GList *list;
+
+  g_return_if_fail (IDE_IS_APPLICATION (self));
+
+  peas_engine_enable_loader (engine, "python3");
+
+  if (g_getenv ("GB_IN_TREE_PLUGINS") != NULL)
+    {
+      GDir *dir;
+
+      g_irepository_require_private (g_irepository_get_default (),
+                                     BUILDDIR"/libide",
+                                     "Ide", "1.0", 0, NULL);
+
+      if ((dir = g_dir_open (BUILDDIR"/plugins", 0, NULL)))
+        {
+          const gchar *name;
+
+          while ((name = g_dir_read_name (dir)))
+            {
+              gchar *path;
+
+              path = g_build_filename (BUILDDIR, "plugins", name, NULL);
+              peas_engine_prepend_search_path (engine, path, path);
+              g_free (path);
+            }
+
+          g_dir_close (dir);
+        }
+    }
+  else
+    {
+      peas_engine_prepend_search_path (engine,
+                                       PACKAGE_LIBDIR"/gnome-builder/plugins",
+                                       PACKAGE_DATADIR"/gnome-builder/plugins");
+    }
+
+  peas_engine_prepend_search_path (engine,
+                                   "resource:///org/gnome/builder/plugins/editor",
+                                   "resource:///org/gnome/builder/plugins/editor");
+
+  peas_engine_rescan_plugins (engine);
+
+  list = peas_engine_get_plugin_list (engine);
+
+  for (; list; list = list->next)
+    {
+      PeasPluginInfo *plugin_info = list->data;
+
+      g_debug ("Discovered plugin \"%s\"",
+               peas_plugin_info_get_module_name (plugin_info));
+    }
+}
+
+void
+ide_application_load_plugins (IdeApplication *self)
+{
+  PeasEngine *engine;
+  const GList *list;
+
+  g_return_if_fail (IDE_IS_APPLICATION (self));
+
+  engine = peas_engine_get_default ();
+  list = peas_engine_get_plugin_list (engine);
+
+  for (; list; list = list->next)
+    {
+      PeasPluginInfo *plugin_info = list->data;
+
+      if (ide_application_can_load_plugin (self, plugin_info))
+        {
+          g_debug ("Loading plugin \"%s\"",
+                   peas_plugin_info_get_module_name (plugin_info));
+          peas_engine_load_plugin (engine, plugin_info);
+        }
+    }
+}
+
+static void
+ide_application_addin_added (PeasExtensionSet *set,
+                             PeasPluginInfo   *plugin_info,
+                             PeasExtension    *extension,
+                             gpointer          user_data)
+{
+  IdeApplication *self = user_data;
+
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_APPLICATION_ADDIN (extension));
+
+  ide_application_addin_load (IDE_APPLICATION_ADDIN (extension), self);
+}
+
+static void
+ide_application_addin_removed (PeasExtensionSet *set,
+                               PeasPluginInfo   *plugin_info,
+                               PeasExtension    *extension,
+                               gpointer          user_data)
+{
+  IdeApplication *self = user_data;
+
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_APPLICATION_ADDIN (extension));
+
+  ide_application_addin_unload (IDE_APPLICATION_ADDIN (extension), self);
+}
+
+void
+ide_application_load_addins (IdeApplication *self)
+{
+  g_return_if_fail (IDE_IS_APPLICATION (self));
+
+  self->addins = peas_extension_set_new (peas_engine_get_default (),
+                                         IDE_TYPE_APPLICATION_ADDIN,
+                                         NULL);
+
+  g_signal_connect_object (self->addins,
+                           "extension-added",
+                           G_CALLBACK (ide_application_addin_added),
+                           self,
+                           0);
+
+  g_signal_connect_object (self->addins,
+                           "extension-removed",
+                           G_CALLBACK (ide_application_addin_removed),
+                           self,
+                           0);
+
+  peas_extension_set_foreach (self->addins,
+                              ide_application_addin_added,
+                              self);
+}
diff --git a/libide/ide-application-private.h b/libide/ide-application-private.h
index cf3856f..911d350 100644
--- a/libide/ide-application-private.h
+++ b/libide/ide-application-private.h
@@ -1,4 +1,4 @@
-/* gb-application-private.h
+/* ide-application-private.h
  *
  * Copyright (C) 2015 Christian Hergert <christian hergert me>
  *
@@ -19,10 +19,10 @@
 #ifndef IDE_APPLICATION_PRIVATE_H
 #define IDE_APPLICATION_PRIVATE_H
 
-#include <gtk/gtk.h>
 #include <gio/gio.h>
 #include <libpeas/peas.h>
 
+#include "ide-application.h"
 #include "ide-keybindings.h"
 #include "ide-recent-projects.h"
 #include "ide-worker-manager.h"
@@ -31,20 +31,32 @@ G_BEGIN_DECLS
 
 struct _IdeApplication
 {
-  GtkApplication        parent_instance;
-
-  gchar                *argv0;
-  gchar                *dbus_address;
-  PeasExtensionSet     *extensions;
-  GtkWindowGroup       *greeter_group;
-  IdeKeybindings       *keybindings;
-  GtkWindow            *preferences_window;
-  IdeRecentProjects    *recent_projects;
-  GDateTime            *startup_time;
-  gchar                *type;
-  IdeWorkerManager     *worker_manager;
+  GtkApplication       parent_instance;
+
+  IdeApplicationMode   mode;
+
+  PeasExtensionSet    *addins;
+  gchar               *dbus_address;
+
+  PeasPluginInfo      *tool;
+  gchar              **tool_arguments;
+
+  PeasPluginInfo      *worker;
+
+  IdeWorkerManager    *worker_manager;
+
+  IdeKeybindings      *keybindings;
+
+  IdeRecentProjects   *recent_projects;
 };
 
+void     ide_application_discover_plugins   (IdeApplication   *self) G_GNUC_INTERNAL;
+void     ide_application_load_plugins       (IdeApplication   *self) G_GNUC_INTERNAL;
+void     ide_application_load_addins        (IdeApplication   *self) G_GNUC_INTERNAL;
+gboolean ide_application_local_command_line (GApplication     *application,
+                                             gchar          ***arguments,
+                                             gint             *exit_status) G_GNUC_INTERNAL;
+
 G_END_DECLS
 
-#endif /* GB_APPLICATION_PRIVATE_H */
+#endif /* IDE_APPLICATION_PRIVATE_H */
diff --git a/libide/ide-application-tool.c b/libide/ide-application-tool.c
new file mode 100644
index 0000000..fe0d43f
--- /dev/null
+++ b/libide/ide-application-tool.c
@@ -0,0 +1,51 @@
+/* ide-application-tool.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-application-tool.h"
+
+G_DEFINE_INTERFACE (IdeApplicationTool, ide_application_tool, G_TYPE_OBJECT)
+
+static void
+ide_application_tool_default_init (IdeApplicationToolInterface *iface)
+{
+}
+
+void
+ide_application_tool_run_async (IdeApplicationTool  *self,
+                                const gchar * const *arguments,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_APPLICATION_TOOL (self));
+  g_return_if_fail (arguments != NULL);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  IDE_APPLICATION_TOOL_GET_IFACE (self)->run_async (self, arguments, cancellable, callback, user_data);
+}
+
+gint
+ide_application_tool_run_finish (IdeApplicationTool  *self,
+                                 GAsyncResult        *result,
+                                 GError             **error)
+{
+  g_return_val_if_fail (IDE_IS_APPLICATION_TOOL (self), 0);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), 0);
+
+  return IDE_APPLICATION_TOOL_GET_IFACE (self)->run_finish (self, result, error);
+}
diff --git a/libide/ide-application-tool.h b/libide/ide-application-tool.h
new file mode 100644
index 0000000..6ccabee
--- /dev/null
+++ b/libide/ide-application-tool.h
@@ -0,0 +1,55 @@
+/* ide-application-tool.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_APPLICATION_TOOL_H
+#define IDE_APPLICATION_TOOL_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_APPLICATION_TOOL (ide_application_tool_get_type())
+
+G_DECLARE_INTERFACE (IdeApplicationTool, ide_application_tool, IDE, APPLICATION_TOOL, GObject)
+
+struct _IdeApplicationToolInterface
+{
+  GTypeInterface parent_interface;
+
+  void (*run_async)  (IdeApplicationTool   *self,
+                      const gchar * const  *arguments,
+                      GCancellable         *cancellable,
+                      GAsyncReadyCallback   callback,
+                      gpointer              user_data);
+  gint (*run_finish) (IdeApplicationTool   *self,
+                      GAsyncResult         *result,
+                      GError              **error);
+};
+
+void ide_application_tool_run_async  (IdeApplicationTool   *self,
+                                      const gchar * const  *arguments,
+                                      GCancellable         *cancellable,
+                                      GAsyncReadyCallback   callback,
+                                      gpointer              user_data);
+gint ide_application_tool_run_finish (IdeApplicationTool   *self,
+                                      GAsyncResult         *result,
+                                      GError              **error);
+
+G_END_DECLS
+
+#endif /* IDE_APPLICATION_TOOL_H */
diff --git a/libide/ide-application.c b/libide/ide-application.c
index 6881346..ab03738 100644
--- a/libide/ide-application.c
+++ b/libide/ide-application.c
@@ -1,6 +1,6 @@
 /* ide-application.c
  *
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -16,32 +16,28 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#define G_LOG_DOMAIN "ide-application"
-
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 
-#ifdef __linux
-# include <sys/prctl.h>
-#endif
-
 #include <glib/gi18n.h>
+#include <girepository.h>
 #include <gtksourceview/gtksource.h>
 #include <libgit2-glib/ggit.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
 
 #include "ide-application.h"
-#include "ide-application-actions.h"
-#include "ide-application-addin.h"
 #include "ide-application-private.h"
+#include "ide-application-tool.h"
 #include "ide-css-provider.h"
 #include "ide-debug.h"
+#include "ide-global.h"
+#include "ide-icons-resources.h"
 #include "ide-internal.h"
-#include "ide-file.h"
-#include "ide-log.h"
 #include "ide-macros.h"
 #include "ide-resources.h"
-#include "ide-vcs.h"
 #include "ide-workbench.h"
 #include "ide-worker.h"
 
@@ -49,211 +45,32 @@
 
 G_DEFINE_TYPE (IdeApplication, ide_application, GTK_TYPE_APPLICATION)
 
-static gboolean
-ide_application_can_load_plugin (IdeApplication *self,
-                                 PeasPluginInfo *plugin_info)
-{
-  const gchar *plugin_name;
-
-  g_assert (IDE_IS_APPLICATION (self));
-  g_assert (plugin_info != NULL);
-
-  /* Currently we only allow in-tree plugins */
-  if (!peas_plugin_info_is_builtin (plugin_info))
-    return FALSE;
-
-  plugin_name = peas_plugin_info_get_module_name (plugin_info);
-
-  /* If --type was specified, only that plugin may be loaded */
-  if ((self->type != NULL) && !ide_str_equal0 (plugin_name, self->type))
-    return FALSE;
-
-  return TRUE;
-}
-
-static void
-ide_application_load_plugins (IdeApplication *self)
-{
-  PeasEngine *engine = peas_engine_get_default ();
-  const GList *list;
-  const GList *iter;
-
-  peas_engine_enable_loader (engine, "python3");
-
-  if (g_getenv ("GB_IN_TREE_PLUGINS") != NULL)
-    {
-      GDir *dir;
-
-      g_irepository_require_private (g_irepository_get_default (),
-                                     BUILDDIR"/libide",
-                                     "Ide", "1.0", 0, NULL);
-
-      if ((dir = g_dir_open (BUILDDIR"/plugins", 0, NULL)))
-        {
-          const gchar *name;
-
-          while ((name = g_dir_read_name (dir)))
-            {
-              gchar *path;
-
-              path = g_build_filename (BUILDDIR, "plugins", name, NULL);
-              peas_engine_prepend_search_path (engine, path, path);
-              g_free (path);
-            }
-
-          g_dir_close (dir);
-        }
-    }
-  else
-    {
-      peas_engine_prepend_search_path (engine,
-                                       PACKAGE_LIBDIR"/gnome-builder/plugins",
-                                       PACKAGE_DATADIR"/gnome-builder/plugins");
-    }
-
-  peas_engine_prepend_search_path (engine,
-                                   "resource:///org/gnome/builder/plugins/editor",
-                                   "resource:///org/gnome/builder/plugins/editor");
-
-  peas_engine_rescan_plugins (engine);
-
-  list = peas_engine_get_plugin_list (engine);
-
-  for (iter = list; iter; iter = iter->next)
-    g_debug ("Discovered plugin \"%s\"", peas_plugin_info_get_module_name (iter->data));
-
-  for (iter = list; iter; iter = iter->next)
-    {
-      PeasPluginInfo *plugin_info = iter->data;
-
-      if (ide_application_can_load_plugin (self, plugin_info))
-        {
-          g_debug ("Loading plugin \"%s\"", peas_plugin_info_get_module_name (plugin_info));
-          peas_engine_load_plugin (engine, plugin_info);
-        }
-    }
-}
-
-static gboolean
-ide_application_is_worker (IdeApplication *self)
-{
-  g_assert (IDE_IS_APPLICATION (self));
-
-  return (self->type != NULL) && (self->dbus_address != NULL);
-}
-
-static void
-ide_application_load_worker (IdeApplication *self)
-{
-  g_autoptr(GDBusConnection) connection = NULL;
-  PeasEngine *engine;
-  PeasPluginInfo *plugin_info;
-  GError *error = NULL;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_APPLICATION (self));
-  g_assert (ide_application_is_worker (self));
-
-#ifdef __linux
-  /* Ensure we are killed with our parent */
-  prctl (PR_SET_PDEATHSIG, 15);
-#endif
-
-  IDE_TRACE_MSG ("Connecting to %s", self->dbus_address);
-
-  connection = g_dbus_connection_new_for_address_sync (self->dbus_address,
-                                                       (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
-                                                        G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING),
-                                                       NULL, NULL, &error);
-
-  if (error != NULL)
-    {
-      g_error ("DBus failure: %s", error->message);
-      g_clear_error (&error);
-      IDE_EXIT;
-    }
-
-  g_assert (G_IS_DBUS_CONNECTION (connection));
-
-  engine = peas_engine_get_default ();
-  plugin_info = peas_engine_get_plugin_info (engine, self->type);
-
-  if ((plugin_info != NULL) && peas_plugin_info_is_loaded (plugin_info))
-    {
-      PeasExtension *exten;
-
-      exten = peas_engine_create_extension (engine, plugin_info, IDE_TYPE_WORKER, NULL);
-
-      if (exten != NULL)
-        {
-          ide_worker_register_service (IDE_WORKER (exten), connection);
-          IDE_GOTO (success);
-        }
-    }
-
-  g_error ("Failed to create \"%s\" worker.", self->type);
-
-  IDE_EXIT;
-
-success:
-  g_application_hold (G_APPLICATION (self));
-  g_dbus_connection_start_message_processing (connection);
-
-  IDE_EXIT;
-}
-
-static void
-ide_application_setup_search_paths (void)
-{
-  GtkSourceStyleSchemeManager *style_scheme_manager;
-  static gboolean initialized;
-
-  if (initialized)
-    return;
-
-  style_scheme_manager = gtk_source_style_scheme_manager_get_default ();
-  gtk_source_style_scheme_manager_append_search_path (style_scheme_manager,
-                                                      PACKAGE_DATADIR"/gtksourceview-3.0/styles/");
-  initialized = TRUE;
-}
-
-/**
- * ide_application_make_skeleton_dirs:
- * @self: A #IdeApplication.
- *
- * Creates all the directories we might need later. Simpler to just ensure they
- * are created during startup.
- */
 static void
 ide_application_make_skeleton_dirs (IdeApplication *self)
 {
   gchar *path;
 
+  IDE_ENTRY;
+
   g_return_if_fail (IDE_IS_APPLICATION (self));
 
-  path = g_build_filename (g_get_user_data_dir (),
-                           "gnome-builder",
-                           NULL);
+  path = g_build_filename (g_get_user_data_dir (), "gnome-builder", NULL);
   g_mkdir_with_parents (path, 0750);
   g_free (path);
 
-  path = g_build_filename (g_get_user_config_dir (),
-                           "gnome-builder",
-                           NULL);
+  path = g_build_filename (g_get_user_config_dir (), "gnome-builder", NULL);
   g_mkdir_with_parents (path, 0750);
   g_free (path);
 
-  path = g_build_filename (g_get_user_config_dir (),
-                           "gnome-builder",
-                           "snippets",
-                           NULL);
+  path = g_build_filename (g_get_user_config_dir (), "gnome-builder", "snippets", NULL);
   g_mkdir_with_parents (path, 0750);
   g_free (path);
+
+  IDE_EXIT;
 }
 
 static void
-ide_application_register_theme_overrides (IdeApplication *application)
+ide_application_register_theme_overrides (IdeApplication *self)
 {
   g_autoptr(GSettings) settings = NULL;
   g_autoptr(GtkCssProvider) provider = NULL;
@@ -262,7 +79,10 @@ ide_application_register_theme_overrides (IdeApplication *application)
 
   IDE_ENTRY;
 
-  gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (), "/org/gnome/builder/icons/");
+  g_assert (IDE_IS_APPLICATION (self));
+
+  gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (),
+                                    "/org/gnome/builder/icons/");
 
   provider = ide_css_provider_new ();
   screen = gdk_screen_get_default ();
@@ -271,40 +91,20 @@ ide_application_register_theme_overrides (IdeApplication *application)
 
   gtk_settings = gtk_settings_get_for_screen (screen);
   settings = g_settings_new ("org.gnome.builder");
-  g_settings_bind (settings, "night-mode", gtk_settings, "gtk-application-prefer-dark-theme",
+  g_settings_bind (settings, "night-mode",
+                   gtk_settings, "gtk-application-prefer-dark-theme",
                    G_SETTINGS_BIND_DEFAULT);
 
   IDE_EXIT;
 }
 
 static void
-ide_application_load_keybindings (IdeApplication *self)
+ide_application_register_keybindings (IdeApplication *self)
 {
   g_autoptr(GSettings) settings = NULL;
   g_autofree gchar *name = NULL;
 
-  /* TODO: Move this to keybindings */
-  static const struct { gchar *name; gchar *binding; } shared_bindings[] = {
-    { "workbench.show-left-pane", "F9" },
-    { "workbench.show-right-pane", "<shift>F9" },
-    { "workbench.show-bottom-pane", "<ctrl>F9" },
-    { "workbench.toggle-panels", "<ctrl><shift>F9" },
-    { "workbench.focus-left", "<ctrl>grave" },
-    { "workbench.focus-right", "<ctrl>9" },
-    { "workbench.focus-stack(1)", "<ctrl>1" },
-    { "workbench.focus-stack(2)", "<ctrl>2" },
-    { "workbench.focus-stack(3)", "<ctrl>3" },
-    { "workbench.focus-stack(4)", "<ctrl>4" },
-    { "workbench.focus-stack(5)", "<ctrl>5" },
-    { "workbench.show-gear-menu", "F10" },
-    { "perspective.global-search", "<ctrl>period" },
-    { "app.preferences", "<Primary>comma" },
-    { "app.shortcuts", "<ctrl>question" },
-    { "workbench.new-document", "<ctrl>n" },
-    { "workbench.open-document", "<ctrl>o" },
-    { NULL }
-  };
-  gsize i;
+  IDE_ENTRY;
 
   g_assert (IDE_IS_APPLICATION (self));
 
@@ -313,267 +113,45 @@ ide_application_load_keybindings (IdeApplication *self)
   self->keybindings = ide_keybindings_new (GTK_APPLICATION (self), name);
   g_settings_bind (settings, "keybindings", self->keybindings, "mode", G_SETTINGS_BIND_GET);
 
-  for (i = 0; shared_bindings [i].name; i++)
-    {
-      const gchar *accels[2] = { shared_bindings [i].binding, NULL };
-      gtk_application_set_accels_for_action (GTK_APPLICATION (self),
-                                             shared_bindings [i].name,
-                                             accels);
-    }
+  IDE_EXIT;
 }
 
-static IdeWorkbench *
-ide_application_find_workbench_for_file (IdeApplication *self,
-                                        GFile         *file)
+static void
+ide_application_register_search_paths (IdeApplication *self)
 {
-  GList *iter;
-  GList *workbenches;
-
   g_assert (IDE_IS_APPLICATION (self));
-  g_assert (G_IS_FILE (file));
-
-  workbenches = gtk_application_get_windows (GTK_APPLICATION (self));
-
-  /*
-   * Find the a project that contains this file in its working directory.
-   */
-  for (iter = workbenches; iter; iter = iter->next)
-    {
-      if (IDE_IS_WORKBENCH (iter->data))
-        {
-          IdeWorkbench *workbench = iter->data;
-          g_autofree gchar *relpath = NULL;
-          IdeContext *context;
-          IdeVcs *vcs;
-          GFile *workdir;
 
-          context = ide_workbench_get_context (workbench);
-          vcs = ide_context_get_vcs (context);
-          workdir = ide_vcs_get_working_directory (vcs);
-
-          relpath = g_file_get_relative_path (workdir, file);
-
-          if (relpath != NULL)
-            return workbench;
-        }
-    }
-
-  /*
-   * No matches found, take the first workbench we find.
-   */
-  for (iter = workbenches; iter; iter = iter->next)
-    if (IDE_IS_WORKBENCH (iter->data))
-      return iter->data;
-
-  return NULL;
+  gtk_source_style_scheme_manager_append_search_path (gtk_source_style_scheme_manager_get_default (),
+                                                      PACKAGE_DATADIR"/gtksourceview-3.0/styles/");
+  g_irepository_prepend_search_path (PACKAGE_LIBDIR"/gnome-builder/girepository-1.0");
 }
 
 static void
-ide_application__context_new_cb (GObject      *object,
-                                GAsyncResult *result,
-                                gpointer      user_data)
+ide_application_register_ggit (IdeApplication *self)
 {
-  g_autoptr(GTask) task = user_data;
-  g_autoptr(IdeContext) context = NULL;
-  IdeApplication *self;
-  IdeWorkbench *workbench;
-  GPtrArray *ar;
-  GError *error = NULL;
-  gsize i;
-
-  g_assert (G_IS_TASK (task));
-
-  self = g_task_get_source_object (task);
-  ar = g_task_get_task_data (task);
+  GgitFeatureFlags ggit_flags;
 
   g_assert (IDE_IS_APPLICATION (self));
-  g_assert (ar);
-
-  context = ide_context_new_finish (result, &error);
-
-  if (!context)
-    {
-      g_task_return_error (task, error);
-      goto cleanup;
-    }
-
-  {
-    IdeVcs *vcs;
-    GFile *workdir;
-    g_autofree gchar *path = NULL;
 
-    vcs = ide_context_get_vcs (context);
-    workdir = ide_vcs_get_working_directory (vcs);
-    path = g_file_get_path (workdir);
-
-    g_debug ("Project working directory: %s", path);
-  }
+  ggit_init ();
 
-  workbench = g_object_new (IDE_TYPE_WORKBENCH,
-                            "application", self,
-                            "context", context,
-                            NULL);
+  ggit_flags = ggit_get_features ();
 
-  for (i = 0; i < ar->len; i++)
+  if ((ggit_flags & GGIT_FEATURE_THREADS) == 0)
     {
-      GFile *file;
-
-      file = g_ptr_array_index (ar, i);
-      g_assert (G_IS_FILE (file));
-
-      //ide_workbench_open (workbench, file);
+      g_error (_("Builder requires libgit2-glib with threading support."));
+      exit (EXIT_FAILURE);
     }
 
-  gtk_window_present (GTK_WINDOW (workbench));
-
-  g_task_return_boolean (task, TRUE);
-
-cleanup:
-  g_application_unmark_busy (G_APPLICATION (self));
-  g_application_release (G_APPLICATION (self));
-}
-
-/**
- * ide_application_open_project_async:
- * @self: A #IdeApplication.
- * @file: A #GFile.
- * @additional_files: (element-type GFile) (nullable): A #GPtrArray of #GFile or %NULL.
- *
- */
-void
-ide_application_open_project_async (IdeApplication      *self,
-                                    GFile               *file,
-                                    GPtrArray           *additional_files,
-                                    GCancellable        *cancellable,
-                                    GAsyncReadyCallback  callback,
-                                    gpointer             user_data)
-{
-  g_autoptr(GFile) directory = NULL;
-  g_autoptr(GTask) task = NULL;
-  g_autoptr(GPtrArray) ar = NULL;
-  GList *windows;
-  GList *iter;
-
-  g_return_if_fail (IDE_IS_APPLICATION (self));
-  g_return_if_fail (G_IS_FILE (file));
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = g_task_new (self, cancellable, callback, user_data);
-
-  windows = gtk_application_get_windows (GTK_APPLICATION (self));
-
-  for (iter = windows; iter; iter = iter->next)
+  if ((ggit_flags & GGIT_FEATURE_SSH) == 0)
     {
-      if (IDE_IS_WORKBENCH (iter->data))
-        {
-          IdeContext *context;
-
-          context = ide_workbench_get_context (iter->data);
-
-          if (context != NULL)
-            {
-              GFile *project_file;
-
-              project_file = ide_context_get_project_file (context);
-
-              if (g_file_equal (file, project_file))
-                {
-                  gtk_window_present (iter->data);
-                  g_task_return_boolean (task, TRUE);
-                  return;
-                }
-            }
-        }
+      g_error (_("Builder requires libgit2-glib with SSH support."));
+      exit (EXIT_FAILURE);
     }
-
-  if (additional_files)
-    ar = g_ptr_array_ref (additional_files);
-  else
-    ar = g_ptr_array_new ();
-
-  g_task_set_task_data (task, g_ptr_array_ref (ar), (GDestroyNotify)g_ptr_array_unref);
-
-  if (g_file_query_file_type (file, 0, NULL) == G_FILE_TYPE_DIRECTORY)
-    directory = g_object_ref (file);
-  else
-    directory = g_file_get_parent (file);
-
-  g_application_mark_busy (G_APPLICATION (self));
-  g_application_hold (G_APPLICATION (self));
-
-  ide_context_new_async (directory,
-                         NULL,
-                         ide_application__context_new_cb,
-                         g_object_ref (task));
-}
-
-gboolean
-ide_application_open_project_finish (IdeApplication  *self,
-                                     GAsyncResult    *result,
-                                     GError         **error)
-{
-  GTask *task = (GTask *)result;
-
-  g_return_val_if_fail (IDE_IS_APPLICATION (self), FALSE);
-  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
-  g_return_val_if_fail (G_IS_TASK (task), FALSE);
-
-  return g_task_propagate_boolean (task, error);
 }
 
 static void
-ide_application_open (GApplication  *application,
-                      GFile        **files,
-                      gint           n_files,
-                      const gchar   *hint)
-{
-  IdeApplication *self = (IdeApplication *)application;
-  IdeWorkbench *workbench;
-  g_autoptr(GPtrArray) ar = NULL;
-  guint i;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_APPLICATION (self));
-
-  /*
-   * Try to open the files using an existing workbench.
-   */
-  for (i = 0; i < n_files; i++)
-    {
-      GFile *file = files [i];
-
-      g_assert (G_IS_FILE (file));
-
-      workbench = ide_application_find_workbench_for_file (self, file);
-
-      if (workbench != NULL)
-        {
-          //ide_workbench_open (workbench, file);
-          gtk_window_present (GTK_WINDOW (workbench));
-          continue;
-        }
-
-      if (!ar)
-        ar = g_ptr_array_new_with_free_func (g_object_unref);
-      g_ptr_array_add (ar, g_object_ref (file));
-    }
-
-  /*
-   * No workbench found for these files, let's create one!
-   */
-  if (ar && ar->len)
-    {
-      GFile *file = g_ptr_array_index (ar, 0);
-
-      ide_application_open_project_async (self, file, ar, NULL, NULL, NULL);
-    }
-
-  IDE_EXIT;
-}
-
-void
-ide_application_show_projects_window (IdeApplication *self)
+ide_application_activate_primary (IdeApplication *self)
 {
   GtkWindow *window;
   GList *windows;
@@ -588,15 +166,8 @@ ide_application_show_projects_window (IdeApplication *self)
 
       if (IDE_IS_WORKBENCH (window))
         {
-          const gchar *name;
-
-          name = ide_workbench_get_visible_perspective_name (IDE_WORKBENCH (window));
-
-          if (ide_str_equal0 ("greeter", name))
-            {
-              gtk_window_present (windows->data);
-              return;
-            }
+          gtk_window_present (window);
+          return;
         }
     }
 
@@ -607,195 +178,169 @@ ide_application_show_projects_window (IdeApplication *self)
 }
 
 static void
-ide_application_activate (GApplication *application)
+ide_application_activate_worker (IdeApplication *self)
 {
-  IdeApplication *self = (IdeApplication *)application;
-  IdeWorkbench *workbench;
-  GList *list;
+  g_autoptr(GDBusConnection) connection = NULL;
+  PeasExtension *extension;
+  PeasEngine *engine;
+  GError *error = NULL;
+
+  IDE_ENTRY;
 
   g_assert (IDE_IS_APPLICATION (self));
+  g_assert (self->worker != NULL);
+  g_assert (self->dbus_address != NULL);
+
+#ifdef __linux
+  /* Ensure we are killed with our parent */
+  prctl (PR_SET_PDEATHSIG, 15);
+#endif
+
+  IDE_TRACE_MSG ("Connecting to %s", self->dbus_address);
 
-  if (ide_application_is_worker (self))
+  connection = g_dbus_connection_new_for_address_sync (self->dbus_address,
+                                                       (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+                                                        G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING),
+                                                       NULL, NULL, &error);
+
+  if (error != NULL)
     {
-      ide_application_load_worker (self);
-      return;
+      g_error ("DBus failure: %s", error->message);
+      g_clear_error (&error);
+      IDE_EXIT;
     }
 
-  list = gtk_application_get_windows (GTK_APPLICATION (application));
+  engine = peas_engine_get_default ();
+  extension = peas_engine_create_extension (engine, self->worker, IDE_TYPE_WORKER, NULL);
 
-  for (; list; list = list->next)
+  if (extension == NULL)
     {
-      if (IDE_IS_WORKBENCH (list->data))
-        {
-          gtk_window_present (GTK_WINDOW (list->data));
-          return;
-        }
+      g_error ("Failed to create \"%s\" worker",
+               peas_plugin_info_get_module_name (self->worker));
+      IDE_EXIT;
     }
 
-  workbench = g_object_new (IDE_TYPE_WORKBENCH,
-                            "application", self,
-                            NULL);
-  gtk_window_present (GTK_WINDOW (workbench));
+  ide_worker_register_service (IDE_WORKER (extension), connection);
+  g_application_hold (G_APPLICATION (self));
+  g_dbus_connection_start_message_processing (connection);
+
+  IDE_EXIT;
 }
 
 static void
-ide_application__extension_added (PeasExtensionSet    *extensions,
-                                  PeasPluginInfo      *plugin_info,
-                                  IdeApplicationAddin *addin,
-                                  IdeApplication      *self)
+ide_application_activate_tool_cb (GObject      *object,
+                                  GAsyncResult *result,
+                                  gpointer      user_data)
 {
+  IdeApplicationTool *tool = (IdeApplicationTool *)object;
+  g_autoptr(IdeApplication) self = user_data;
+  GError *error = NULL;
+  gint exit_code;
+
   g_assert (IDE_IS_APPLICATION (self));
-  g_assert (plugin_info != NULL);
-  g_assert (IDE_IS_APPLICATION_ADDIN (addin));
-  g_assert (PEAS_IS_EXTENSION_SET (extensions));
+  g_assert (IDE_IS_APPLICATION_TOOL (tool));
 
-  ide_application_addin_load (addin, self);
-}
+  exit_code = ide_application_tool_run_finish (tool, result, &error);
 
-static void
-ide_application__extension_removed (PeasExtensionSet    *extensions,
-                                    PeasPluginInfo      *plugin_info,
-                                    IdeApplicationAddin *addin,
-                                    IdeApplication      *self)
-{
-  g_assert (IDE_IS_APPLICATION (self));
-  g_assert (plugin_info != NULL);
-  g_assert (IDE_IS_APPLICATION_ADDIN (addin));
-  g_assert (PEAS_IS_EXTENSION_SET (extensions));
+  if (error != NULL)
+    {
+      g_printerr ("%s\n", error->message);
+      g_clear_error (&error);
+    }
 
-  ide_application_addin_unload (addin, self);
+  /* GApplication does not provide a way to pass exit code. */
+  if (exit_code != 0)
+    exit (exit_code);
+
+  g_application_release (G_APPLICATION (self));
 }
 
 static void
-ide_application_load_addins (IdeApplication *self)
+ide_application_activate_tool (IdeApplication *self)
 {
   PeasEngine *engine;
+  PeasExtension *tool;
 
   g_assert (IDE_IS_APPLICATION (self));
+  g_assert (self->tool != NULL);
+  g_assert (self->tool_arguments != NULL);
 
   engine = peas_engine_get_default ();
+  tool = peas_engine_create_extension (engine,
+                                       self->tool,
+                                       IDE_TYPE_APPLICATION_TOOL,
+                                       NULL);
+  if (tool == NULL)
+    return;
 
-  self->extensions = peas_extension_set_new (engine, IDE_TYPE_APPLICATION_ADDIN, NULL);
-
-  peas_extension_set_foreach (self->extensions,
-                              (PeasExtensionSetForeachFunc)ide_application__extension_added,
-                              self);
+  g_application_hold (G_APPLICATION (self));
 
-  g_signal_connect_object (self->extensions,
-                           "extension-added",
-                           G_CALLBACK (ide_application__extension_added),
-                           self,
-                           0);
+  ide_application_tool_run_async (IDE_APPLICATION_TOOL (tool),
+                                  (const gchar * const *)self->tool_arguments,
+                                  NULL,
+                                  ide_application_activate_tool_cb,
+                                  g_object_ref (self));
 
-  g_signal_connect_object (self->extensions,
-                           "extension-removed",
-                           G_CALLBACK (ide_application__extension_removed),
-                           self,
-                           0);
+  g_object_unref (tool);
 }
 
 static void
-ide_application_startup (GApplication *app)
+ide_application_activate (GApplication *application)
 {
-  IdeApplication *self = (IdeApplication *)app;
-  GgitFeatureFlags ggit_flags;
-
-  IDE_ENTRY;
+  IdeApplication *self = (IdeApplication *)application;
 
   g_assert (IDE_IS_APPLICATION (self));
 
-  self->startup_time = g_date_time_new_now_utc ();
-
-  g_resources_register (ide_get_resource ());
-
-  g_application_set_resource_base_path (app, "/org/gnome/builder");
-
-  g_irepository_prepend_search_path (PACKAGE_LIBDIR"/gnome-builder/girepository-1.0");
-
-  if (!ide_application_is_worker (self))
-    self->greeter_group = gtk_window_group_new ();
-
-  _ide_battery_monitor_init ();
-  _ide_thread_pool_init (ide_application_is_worker (self));
-
-  modeline_parser_init ();
+  if (self->mode == IDE_APPLICATION_MODE_PRIMARY)
+    ide_application_activate_primary (self);
+  else if (self->mode == IDE_APPLICATION_MODE_WORKER)
+    ide_application_activate_worker (self);
+  else if (self->mode == IDE_APPLICATION_MODE_TOOL)
+    ide_application_activate_tool (self);
+}
 
-  ggit_init ();
+static void
+ide_application_startup (GApplication *application)
+{
+  IdeApplication *self = (IdeApplication *)application;
+  gboolean small_thread_pool;
 
-  ggit_flags = ggit_get_features ();
+  g_assert (IDE_IS_APPLICATION (self));
 
-  if ((ggit_flags & GGIT_FEATURE_THREADS) == 0)
-    {
-      g_error (_("Builder requires libgit2-glib with threading support."));
-      exit (EXIT_FAILURE);
-    }
+  g_resources_register (ide_get_resource ());
+  g_resources_register (ide_icons_get_resource ());
 
-  if ((ggit_flags & GGIT_FEATURE_SSH) == 0)
-    {
-      g_error (_("Builder requires libgit2-glib with SSH support."));
-      exit (EXIT_FAILURE);
-    }
+  g_application_set_resource_base_path (application, "/org/gnome/builder");
+  ide_application_register_search_paths (self);
 
-  G_APPLICATION_CLASS (ide_application_parent_class)->startup (app);
+  small_thread_pool = (self->mode != IDE_APPLICATION_MODE_PRIMARY);
+  _ide_thread_pool_init (small_thread_pool);
 
-  if (!ide_application_is_worker (self))
+  if (self->mode == IDE_APPLICATION_MODE_PRIMARY)
     {
       ide_application_make_skeleton_dirs (self);
-      ide_application_actions_init (self);
       ide_application_register_theme_overrides (self);
-      ide_application_setup_search_paths ();
-      ide_application_load_keybindings (self);
-      ide_application_load_plugins (self);
-      ide_application_load_addins (self);
-    }
-
-  IDE_EXIT;
-}
-
-static gboolean
-ide_application_increase_verbosity (void)
-{
-  ide_log_increase_verbosity ();
-  return TRUE;
-}
+      ide_application_register_keybindings (self);
+      ide_application_register_ggit (self);
 
-static gint
-ide_application_handle_local_options (GApplication *app,
-                                      GVariantDict *options)
-{
-  if (g_variant_dict_contains (options, "version"))
-    {
-      g_print ("%s - Version %s\n", g_get_application_name (), VERSION);
-      return 0;
+      modeline_parser_init ();
     }
 
-   if (g_variant_dict_contains (options, "standalone") || g_variant_dict_contains (options, "type"))
-    {
-      GApplicationFlags flags;
+  _ide_battery_monitor_init ();
 
-      flags = g_application_get_flags (app);
-      g_application_set_flags (app, flags | G_APPLICATION_NON_UNIQUE);
-    }
+  G_APPLICATION_CLASS (ide_application_parent_class)->startup (application);
 
-  return -1;
+  ide_application_load_addins (self);
 }
 
-static gboolean
-ide_application_local_command_line (GApplication   *application,
-                                    gchar        ***arguments,
-                                    int            *exit_status)
+static void
+ide_application_open (GApplication  *application,
+                      GFile        **files,
+                      gint           n_files,
+                      const gchar   *hint)
 {
-  IdeApplication *self = (IdeApplication *)application;
-
-  g_assert (IDE_IS_APPLICATION (self));
-  g_assert (arguments != NULL);
-  g_assert (*arguments != NULL);
-  g_assert (exit_status != NULL);
-
-  self->argv0 = g_strdup ((*arguments) [0]);
+  g_assert (IDE_IS_APPLICATION (application));
 
-  return G_APPLICATION_CLASS (ide_application_parent_class)->
-    local_command_line (application, arguments, exit_status);
 }
 
 static void
@@ -803,102 +348,61 @@ ide_application_finalize (GObject *object)
 {
   IdeApplication *self = (IdeApplication *)object;
 
-  IDE_ENTRY;
-
-  g_clear_object (&self->extensions);
-  g_clear_pointer (&self->startup_time, g_date_time_unref);
-  g_clear_pointer (&self->argv0, g_free);
+  g_clear_pointer (&self->dbus_address, g_free);
+  g_clear_pointer (&self->tool_arguments, g_strfreev);
+  g_clear_object (&self->worker_manager);
   g_clear_object (&self->keybindings);
   g_clear_object (&self->recent_projects);
-  g_clear_object (&self->greeter_group);
 
   G_OBJECT_CLASS (ide_application_parent_class)->finalize (object);
-
-  IDE_EXIT;
 }
 
 static void
 ide_application_class_init (IdeApplicationClass *klass)
 {
-  GApplicationClass *app_class = G_APPLICATION_CLASS (klass);
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  IDE_ENTRY;
+  GApplicationClass *g_app_class = G_APPLICATION_CLASS (klass);
 
   object_class->finalize = ide_application_finalize;
 
-  app_class->activate = ide_application_activate;
-  app_class->startup = ide_application_startup;
-  app_class->open = ide_application_open;
-  app_class->local_command_line = ide_application_local_command_line;
-  app_class->handle_local_options = ide_application_handle_local_options;
-
-  IDE_EXIT;
+  g_app_class->activate = ide_application_activate;
+  g_app_class->local_command_line = ide_application_local_command_line;
+  g_app_class->open = ide_application_open;
+  g_app_class->startup = ide_application_startup;
 }
 
 static void
-ide_application_init (IdeApplication *app)
+ide_application_init (IdeApplication *self)
 {
-  GOptionEntry options[] = {
-    { "standalone",
-      's',
-      G_OPTION_FLAG_IN_MAIN,
-      G_OPTION_ARG_NONE,
-      NULL,
-      N_("Run Builder in standalone mode") },
-
-    { "version",
-      0,
-      G_OPTION_FLAG_IN_MAIN,
-      G_OPTION_ARG_NONE,
-      NULL,
-      N_("Show the application's version") },
-
-    { "verbose",
-      'v',
-      G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_IN_MAIN | G_OPTION_FLAG_HIDDEN,
-      G_OPTION_ARG_CALLBACK,
-      ide_application_increase_verbosity,
-      N_("Increase verbosity. May be specified multiple times.") },
-
-    { "dbus-address",
-      0,
-      G_OPTION_FLAG_HIDDEN,
-      G_OPTION_ARG_STRING,
-      &app->dbus_address,
-      N_("The DBus server address for which to connect.") },
-
-    { "type",
-      0,
-      G_OPTION_FLAG_HIDDEN,
-      G_OPTION_ARG_STRING,
-      &app->type,
-      N_("The type of plugin worker process to run.") },
-
-    { NULL }
-  };
+  ide_set_program_name (PACKAGE_NAME);
 
-  IDE_ENTRY;
+  self->mode = IDE_APPLICATION_MODE_PRIMARY;
 
-  g_application_add_main_option_entries (G_APPLICATION (app), options);
+  setlocale (LC_ALL, "");
 
-  IDE_EXIT;
+  bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain (GETTEXT_PACKAGE);
+
+  g_set_application_name (_("Builder"));
 }
 
-GDateTime *
-ide_application_get_startup_time (IdeApplication *self)
+IdeApplication *
+ide_application_new (void)
 {
-  g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+  return g_object_new (IDE_TYPE_APPLICATION,
+                       "application-id", "org.gnome.Builder",
+                       "flags", G_APPLICATION_HANDLES_OPEN,
+                       NULL);
 
-  return self->startup_time;
 }
 
-const gchar *
-ide_application_get_keybindings_mode (IdeApplication *self)
+IdeApplicationMode
+ide_application_get_mode (IdeApplication *self)
 {
-  g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+  g_return_val_if_fail (IDE_IS_APPLICATION (self), 0);
 
-  return ide_keybindings_get_mode (self->keybindings);
+  return self->mode;
 }
 
 static void
@@ -952,8 +456,11 @@ ide_application_get_worker_async (IdeApplication      *self,
   g_return_if_fail (plugin_name != NULL);
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  if (self->mode != IDE_APPLICATION_MODE_PRIMARY)
+    return NULL;
+
   if (self->worker_manager == NULL)
-    self->worker_manager = ide_worker_manager_new (self->argv0);
+    self->worker_manager = ide_worker_manager_new ("gnome-builder-worker");
 
   task = g_task_new (self, cancellable, callback, user_data);
 
@@ -1007,6 +514,9 @@ ide_application_get_recent_projects (IdeApplication *self)
 {
   g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
 
+  if (self->mode != IDE_APPLICATION_MODE_PRIMARY)
+    return NULL;
+
   if (self->recent_projects == NULL)
     {
       self->recent_projects = ide_recent_projects_new ();
@@ -1015,3 +525,51 @@ ide_application_get_recent_projects (IdeApplication *self)
 
   return self->recent_projects;
 }
+
+void
+ide_application_show_projects_window (IdeApplication *self)
+{
+  GtkWindow *window;
+  GList *windows;
+
+  g_assert (IDE_IS_APPLICATION (self));
+
+  if (self->mode != IDE_APPLICATION_MODE_PRIMARY)
+    return;
+
+  windows = gtk_application_get_windows (GTK_APPLICATION (self));
+
+  for (; windows; windows = windows->next)
+    {
+      window = windows->data;
+
+      if (IDE_IS_WORKBENCH (window))
+        {
+          const gchar *name;
+
+          name = ide_workbench_get_visible_perspective_name (IDE_WORKBENCH (window));
+
+          if (ide_str_equal0 ("greeter", name))
+            {
+              gtk_window_present (windows->data);
+              return;
+            }
+        }
+    }
+
+  window = g_object_new (IDE_TYPE_WORKBENCH,
+                         "application", self,
+                         NULL);
+  gtk_window_present (window);
+}
+
+const gchar *
+ide_application_get_keybindings_mode (IdeApplication *self)
+{
+  g_return_val_if_fail (IDE_IS_APPLICATION (self), NULL);
+
+  if (self->mode == IDE_APPLICATION_MODE_PRIMARY)
+    return ide_keybindings_get_mode (self->keybindings);
+
+  return NULL;
+}
diff --git a/libide/ide-application.h b/libide/ide-application.h
index ba2282c..35140fd 100644
--- a/libide/ide-application.h
+++ b/libide/ide-application.h
@@ -1,6 +1,6 @@
 /* ide-application.h
  *
- * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,32 +25,31 @@
 
 G_BEGIN_DECLS
 
-#define IDE_APPLICATION_DEFAULT (IDE_APPLICATION(g_application_get_default()))
 #define IDE_TYPE_APPLICATION    (ide_application_get_type())
+#define IDE_APPLICATION_DEFAULT (IDE_APPLICATION (g_application_get_default()))
 
 G_DECLARE_FINAL_TYPE (IdeApplication, ide_application, IDE, APPLICATION, GtkApplication)
 
-const gchar       *ide_application_get_keybindings_mode (IdeApplication        *self);
-GDateTime         *ide_application_get_startup_time     (IdeApplication        *self);
-IdeRecentProjects *ide_application_get_recent_projects (IdeApplication *self);
-void               ide_application_show_projects_window (IdeApplication        *self);
-void               ide_application_open_project_async   (IdeApplication        *self,
-                                                        GFile                *file,
-                                                        GPtrArray            *additional_files,
-                                                        GCancellable         *cancellable,
-                                                        GAsyncReadyCallback   callback,
-                                                        gpointer              user_data);
-gboolean           ide_application_open_project_finish  (IdeApplication        *self,
-                                                        GAsyncResult         *result,
-                                                        GError              **error);
-void               ide_application_get_worker_async     (IdeApplication        *self,
-                                                        const gchar          *plugin_name,
-                                                        GCancellable         *cancellable,
-                                                        GAsyncReadyCallback   callback,
-                                                        gpointer              user_data);
-GDBusProxy        *ide_application_get_worker_finish    (IdeApplication        *self,
-                                                        GAsyncResult         *result,
-                                                        GError              **error);
+typedef enum
+{
+  IDE_APPLICATION_MODE_PRIMARY,
+  IDE_APPLICATION_MODE_WORKER,
+  IDE_APPLICATION_MODE_TOOL,
+} IdeApplicationMode;
+
+IdeApplicationMode  ide_application_get_mode             (IdeApplication       *self);
+IdeApplication     *ide_application_new                  (void);
+IdeRecentProjects  *ide_application_get_recent_projects  (IdeApplication       *self);
+void                ide_application_show_projects_window (IdeApplication       *self);
+const gchar        *ide_application_get_keybindings_mode (IdeApplication       *self);
+void                ide_application_get_worker_async     (IdeApplication       *self,
+                                                          const gchar          *plugin_name,
+                                                          GCancellable         *cancellable,
+                                                          GAsyncReadyCallback   callback,
+                                                          gpointer              user_data);
+GDBusProxy         *ide_application_get_worker_finish    (IdeApplication       *self,
+                                                          GAsyncResult         *result,
+                                                          GError              **error);
 
 G_END_DECLS
 
diff --git a/libide/ide-css-provider.c b/libide/ide-css-provider.c
index 5c84204..7495399 100644
--- a/libide/ide-css-provider.c
+++ b/libide/ide-css-provider.c
@@ -19,9 +19,9 @@
 #define G_LOG_DOMAIN "ide-css-provider"
 
 #include <glib/gi18n.h>
-#include <ide.h>
 
 #include "ide-css-provider.h"
+#include "ide-debug.h"
 
 struct _IdeCssProvider
 {
diff --git a/libide/ide-keybindings.c b/libide/ide-keybindings.c
index 37b2b32..0f2eaef 100644
--- a/libide/ide-keybindings.c
+++ b/libide/ide-keybindings.c
@@ -19,8 +19,8 @@
 #define G_LOG_DOMAIN "ide-keybindings"
 
 #include <glib/gi18n.h>
-#include <ide.h>
 
+#include "ide-debug.h"
 #include "ide-keybindings.h"
 
 struct _IdeKeybindings
diff --git a/libide/ide-workbench.c b/libide/ide-workbench.c
index dc47565..bc38036 100644
--- a/libide/ide-workbench.c
+++ b/libide/ide-workbench.c
@@ -36,7 +36,13 @@ enum {
   LAST_PROP
 };
 
+enum {
+  SET_PERSPECTIVE,
+  LAST_SIGNAL
+};
+
 static GParamSpec *properties [LAST_PROP];
+static guint signals [LAST_SIGNAL];
 
 static void
 ide_workbench_notify_visible_child (IdeWorkbench *self,
@@ -214,6 +220,17 @@ ide_workbench_class_init (IdeWorkbenchClass *klass)
 
   g_object_class_install_properties (object_class, LAST_PROP, properties);
 
+  /**
+   * IdeWorkbench::set-perspective:
+   */
+  signals [SET_PERSPECTIVE] =
+    g_signal_new_class_handler ("set-perspective",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                                G_CALLBACK (ide_workbench_set_visible_perspective_name),
+                                NULL, NULL, NULL,
+                                G_TYPE_NONE, 1, G_TYPE_STRING);
+
   gtk_widget_class_set_css_name (widget_class, "workbench");
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/builder/ui/ide-workbench.ui");
   gtk_widget_class_bind_template_child (widget_class, IdeWorkbench, perspectives_stack);
@@ -332,6 +349,9 @@ ide_workbench_addin_added (PeasExtensionSet *set,
   IDE_TRACE_MSG ("Loading workbench addin for %s",
                  peas_plugin_info_get_module_name (plugin_info));
 
+  g_print ("================ ADDED: %s\n",
+           peas_plugin_info_get_module_name (plugin_info));
+
   ide_workbench_addin_load (IDE_WORKBENCH_ADDIN (extension), self);
 }
 
diff --git a/src/main.c b/src/main.c
index 175851b..06df1a8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -16,38 +16,15 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#define G_LOG_DOMAIN "Builder"
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <gtk/gtk.h>
 #include <ide.h>
-#include <locale.h>
-
-#include "gb-icons-resources.h"
 
 int
 main (int   argc,
       char *argv[])
 {
-  GApplication *app;
+  IdeApplication *app;
   int ret;
 
-  setlocale (LC_ALL, "");
-
-  bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
-  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-  textdomain (GETTEXT_PACKAGE);
-
-  g_set_prgname (PACKAGE_TARNAME);
-  g_set_application_name (_("Builder"));
-
-  ide_set_program_name ("gnome-builder");
-
   ide_log_init (TRUE, NULL);
 
   g_message ("Initializing with Gtk+ version %d.%d.%d.",
@@ -55,13 +32,8 @@ main (int   argc,
              gtk_get_minor_version (),
              gtk_get_micro_version ());
 
-  g_resources_register (gb_icons_get_resource ());
-
-  app = g_object_new (IDE_TYPE_APPLICATION,
-                      "application-id", "org.gnome.Builder",
-                      "flags", G_APPLICATION_HANDLES_OPEN,
-                      NULL);
-  ret = g_application_run (app, argc, argv);
+  app = ide_application_new ();
+  ret = g_application_run (G_APPLICATION (app), argc, argv);
   g_clear_object (&app);
 
   ide_log_shutdown ();


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