[gnome-builder/wip/chergert/sysprof: 13/18] run-manager: add concept of IdeRunHandler



commit 286a3db2da6a94fe2f3848efb21a3e7bc849f02b
Author: Christian Hergert <chergert redhat com>
Date:   Mon Aug 22 18:39:38 2016 -0700

    run-manager: add concept of IdeRunHandler
    
    This allows various plugins to hook into the run pipeline. This is useful
    since we will have various ways to "run" an application. Such as through
    a profiler or debugger.
    
    We'd like the ability to simply "run again" and unify the selection of how
    to run the project, which requires just a bit of an abstraction. That is
    what the IdeRunHandlerInfo provides.

 libide/Makefile.am                      |    1 +
 libide/runner/OVERVIEW.md               |   11 +
 libide/runner/ide-run-manager-private.h |   42 +++
 libide/runner/ide-run-manager.c         |  592 +++++++++++++++++++++++++++++--
 libide/runner/ide-run-manager.h         |   48 ++-
 5 files changed, 649 insertions(+), 45 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 402372a..ae65160 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -377,6 +377,7 @@ libide_1_0_la_SOURCES =                                   \
        preferences/ide-preferences-page-private.h        \
        preferences/ide-preferences-perspective.c         \
        preferences/ide-preferences-perspective.h         \
+       runner/ide-run-manager-private.h                  \
        search/ide-search-reducer.c                       \
        snippets/ide-source-snippet-completion-item.c     \
        snippets/ide-source-snippet-completion-item.h     \
diff --git a/libide/runner/OVERVIEW.md b/libide/runner/OVERVIEW.md
index cf5801a..94c4e33 100644
--- a/libide/runner/OVERVIEW.md
+++ b/libide/runner/OVERVIEW.md
@@ -87,3 +87,14 @@ So it might need to do something like:
 
   runner.prepend_argv('sysprof-spawner')
 
+
+## RunHandlers
+
+Because we want one runner at a time, and different plugins might implement
+different runners (debugger, profiler, basic run support), we register
+a run handler.
+
+When ide_run_manager_run_async() is called, the run handler will be called
+and it can adjust things as necessary.
+
+
diff --git a/libide/runner/ide-run-manager-private.h b/libide/runner/ide-run-manager-private.h
new file mode 100644
index 0000000..9acad53
--- /dev/null
+++ b/libide/runner/ide-run-manager-private.h
@@ -0,0 +1,42 @@
+/* ide-run-manager-private.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * 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_RUN_MANAGER_PRIVATE_H
+#define IDE_RUN_MANAGER_PRIVATE_H
+
+#include "ide-run-manager.h"
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+  gchar          *id;
+  gchar          *title;
+  gchar          *icon_name;
+  gchar          *accel;
+  gint            priority;
+  IdeRunHandler   handler;
+  gpointer        handler_data;
+  GDestroyNotify  handler_data_destroy;
+} IdeRunHandlerInfo;
+
+const GList *_ide_run_manager_get_handlers (IdeRunManager *self);
+
+G_END_DECLS
+
+#endif /* IDE_RUN_MANAGER_PRIVATE_H */
diff --git a/libide/runner/ide-run-manager.c b/libide/runner/ide-run-manager.c
index b86d93a..3840db9 100644
--- a/libide/runner/ide-run-manager.c
+++ b/libide/runner/ide-run-manager.c
@@ -24,32 +24,64 @@
 #include "ide-debug.h"
 
 #include "buildsystem/ide-build-manager.h"
+#include "buildsystem/ide-build-system.h"
 #include "buildsystem/ide-build-target.h"
 #include "buildsystem/ide-configuration.h"
 #include "buildsystem/ide-configuration-manager.h"
 #include "runner/ide-run-manager.h"
+#include "runner/ide-run-manager-private.h"
 #include "runner/ide-runner.h"
 #include "runtimes/ide-runtime.h"
 
 struct _IdeRunManager
 {
-  IdeObject           parent_instance;
+  IdeObject                parent_instance;
 
-  GCancellable       *cancellable;
-  GSimpleActionGroup *actions;
+  GCancellable            *cancellable;
+  GSimpleActionGroup      *actions;
+  IdeBuildTarget          *build_target;
 
-  guint               busy : 1;
+  const IdeRunHandlerInfo *handler;
+  GList                   *handlers;
+
+  guint                    busy : 1;
 };
 
-G_DEFINE_TYPE (IdeRunManager, ide_run_manager, IDE_TYPE_OBJECT)
+static void action_group_iface_init (GActionGroupInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeRunManager, ide_run_manager, IDE_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, action_group_iface_init))
 
 enum {
   PROP_0,
   PROP_BUSY,
+  PROP_HANDLER,
   N_PROPS
 };
 
+enum {
+  STOPPED,
+  N_SIGNALS
+};
+
 static GParamSpec *properties [N_PROPS];
+static guint signals [N_SIGNALS];
+
+static void
+ide_run_handler_info_free (gpointer data)
+{
+  IdeRunHandlerInfo *info = data;
+
+  g_free (info->id);
+  g_free (info->title);
+  g_free (info->icon_name);
+  g_free (info->accel);
+
+  if (info->handler_data_destroy)
+    info->handler_data_destroy (info->handler_data);
+
+  g_slice_free (IdeRunHandlerInfo, info);
+}
 
 static void
 ide_run_manager_finalize (GObject *object)
@@ -58,6 +90,10 @@ ide_run_manager_finalize (GObject *object)
 
   g_clear_object (&self->cancellable);
   g_clear_object (&self->actions);
+  g_clear_object (&self->build_target);
+
+  g_list_free_full (self->handlers, ide_run_handler_info_free);
+  self->handlers = NULL;
 
   G_OBJECT_CLASS (ide_run_manager_parent_class)->finalize (object);
 }
@@ -76,6 +112,10 @@ ide_run_manager_get_property (GObject    *object,
       g_value_set_boolean (value, ide_run_manager_get_busy (self));
       break;
 
+    case PROP_HANDLER:
+      g_value_set_string (value, ide_run_manager_get_handler (self));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -96,12 +136,44 @@ ide_run_manager_class_init (IdeRunManagerClass *klass)
                           FALSE,
                           (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
+  properties [PROP_HANDLER] =
+    g_param_spec_string ("handler",
+                         "Handler",
+                         "Handler",
+                         "run",
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
   g_object_class_install_properties (object_class, N_PROPS, properties);
+
+  /**
+   * IdeRunManager::stopped:
+   *
+   * This signal is emitted when the run manager has stopped the currently
+   * executing inferior.
+   */
+  signals [STOPPED] =
+    g_signal_new ("stopped",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0,
+                  NULL,
+                  NULL,
+                  NULL,
+                  G_TYPE_NONE,
+                  0);
 }
 
 static void
 ide_run_manager_init (IdeRunManager *self)
 {
+  ide_run_manager_add_handler (self,
+                               "run",
+                               _("Run"),
+                               "media-playback-start-symbolic",
+                               "<Control>F5",
+                               NULL,
+                               NULL,
+                               NULL);
 }
 
 gboolean
@@ -139,6 +211,7 @@ ide_run_manager_run_cb (GObject      *object,
 {
   IdeRunner *runner = (IdeRunner *)object;
   g_autoptr(GTask) task = user_data;
+  IdeRunManager *self;
   GError *error = NULL;
 
   IDE_ENTRY;
@@ -146,6 +219,8 @@ ide_run_manager_run_cb (GObject      *object,
   g_assert (IDE_IS_RUNNER (runner));
   g_assert (G_IS_TASK (task));
 
+  self = g_task_get_source_object (task);
+
   if (!ide_runner_run_finish (runner, result, &error))
     {
       g_task_return_error (task, error);
@@ -155,6 +230,9 @@ ide_run_manager_run_cb (GObject      *object,
   g_task_return_boolean (task, TRUE);
 
 failure:
+
+  g_signal_emit (self, signals [STOPPED], 0);
+
   IDE_EXIT;
 }
 
@@ -217,6 +295,13 @@ ide_run_manager_install_cb (GObject      *object,
   g_assert (IDE_IS_RUNNER (runner));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  /*
+   * If the current handler has a callback specified (our default "run" handler
+   * does not), then we need to allow that handler to prepare the runner.
+   */
+  if (self->handler != NULL && self->handler->handler != NULL)
+    self->handler->handler (self, runner, self->handler->handler_data);
+
   ide_runner_run_async (runner,
                         cancellable,
                         ide_run_manager_run_cb,
@@ -227,6 +312,17 @@ failure:
 }
 
 static void
+ide_run_manager_notify_busy (IdeRunManager *self)
+{
+  g_assert (IDE_IS_RUN_MANAGER (self));
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+  g_action_group_action_enabled_changed (G_ACTION_GROUP (self), "run", self->busy == FALSE);
+  g_action_group_action_enabled_changed (G_ACTION_GROUP (self), "run-with-handler", self->busy == FALSE);
+  g_action_group_action_enabled_changed (G_ACTION_GROUP (self), "stop", self->busy == TRUE);
+}
+
+static void
 ide_run_manager_task_completed (IdeRunManager *self,
                                 GParamSpec    *pspec,
                                 GTask         *task)
@@ -239,11 +335,71 @@ ide_run_manager_task_completed (IdeRunManager *self,
 
   self->busy = FALSE;
 
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+  ide_run_manager_notify_busy (self);
 
   IDE_EXIT;
 }
 
+static void
+ide_run_manager_do_install_before_run (IdeRunManager *self,
+                                       GTask         *task)
+{
+  IdeBuildManager *build_manager;
+  IdeContext *context;
+
+  g_assert (IDE_IS_RUN_MANAGER (self));
+  g_assert (G_IS_TASK (task));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  build_manager = ide_context_get_build_manager (context);
+
+  /*
+   * First we need to make sure the target is up to date and installed
+   * so that all the dependent resources are available.
+   */
+
+  self->busy = TRUE;
+
+  g_signal_connect_object (task,
+                           "notify::completed",
+                           G_CALLBACK (ide_run_manager_task_completed),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  ide_build_manager_install_async (build_manager,
+                                   g_task_get_cancellable (task),
+                                   ide_run_manager_install_cb,
+                                   g_object_ref (task));
+
+  ide_run_manager_notify_busy (self);
+}
+
+static void
+ide_run_manager_run_discover_cb (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  IdeRunManager *self = (IdeRunManager *)object;
+  g_autoptr(IdeBuildTarget) build_target = NULL;
+  g_autoptr(GTask) task = user_data;
+  GError *error = NULL;
+
+  g_assert (IDE_IS_RUN_MANAGER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  build_target = ide_run_manager_discover_default_target_finish (self, result, &error);
+
+  if (build_target == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
+
+  g_task_set_task_data (task, g_steal_pointer (&build_target), g_object_unref);
+
+  ide_run_manager_do_install_before_run (self, task);
+}
+
 void
 ide_run_manager_run_async (IdeRunManager       *self,
                            IdeBuildTarget      *build_target,
@@ -253,14 +409,12 @@ ide_run_manager_run_async (IdeRunManager       *self,
 {
   g_autoptr(GTask) task = NULL;
   g_autoptr(GCancellable) local_cancellable = NULL;
-  IdeBuildManager *build_manager;
-  IdeContext *context;
   GError *error = NULL;
 
   IDE_ENTRY;
 
   g_return_if_fail (IDE_IS_RUN_MANAGER (self));
-  g_return_if_fail (IDE_IS_BUILD_TARGET (build_target));
+  g_return_if_fail (!build_target || IDE_IS_BUILD_TARGET (build_target));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   if (cancellable == NULL)
@@ -268,40 +422,33 @@ ide_run_manager_run_async (IdeRunManager       *self,
 
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, ide_run_manager_run_async);
-  g_task_set_task_data (task, g_object_ref (build_target), g_object_unref);
+
+  g_set_object (&self->cancellable, cancellable);
 
   if (ide_run_manager_check_busy (self, &error))
     {
       g_task_return_error (task, error);
-      IDE_GOTO (failure);
+      IDE_EXIT;
     }
 
-  /*
-   * First we need to make sure the target is up to date and installed
-   * so that all the dependent resources are available.
-   */
-
-  context = ide_object_get_context (IDE_OBJECT (self));
-  build_manager = ide_context_get_build_manager (context);
-
-  self->busy = TRUE;
-
-  g_set_object (&self->cancellable, cancellable);
-
-  g_signal_connect_object (task,
-                           "notify::completed",
-                           G_CALLBACK (ide_run_manager_task_completed),
-                           self,
-                           G_CONNECT_SWAPPED);
+  if (build_target == NULL)
+    {
+      build_target = ide_run_manager_get_build_target (self);
+
+      if (build_target == NULL)
+        {
+          ide_run_manager_discover_default_target_async (self,
+                                                         cancellable,
+                                                         ide_run_manager_run_discover_cb,
+                                                         g_object_ref (task));
+          IDE_EXIT;
+        }
+    }
 
-  ide_build_manager_install_async (build_manager,
-                                   cancellable,
-                                   ide_run_manager_install_cb,
-                                   g_steal_pointer (&task));
+  g_task_set_task_data (task, g_object_ref (build_target), g_object_unref);
 
-  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+  ide_run_manager_do_install_before_run (self, task);
 
-failure:
   IDE_EXIT;
 }
 
@@ -325,7 +472,7 @@ ide_run_manager_run_finish (IdeRunManager  *self,
 static gboolean
 do_cancel_in_timeout (gpointer user_data)
 {
-  GCancellable *cancellable = user_data;
+  g_autoptr(GCancellable) cancellable = user_data;
 
   IDE_ENTRY;
 
@@ -349,3 +496,378 @@ ide_run_manager_cancel (IdeRunManager *self)
 
   IDE_EXIT;
 }
+
+void
+ide_run_manager_set_handler (IdeRunManager *self,
+                             const gchar   *id)
+{
+  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+
+  self->handler = NULL;
+
+  for (GList *iter = self->handlers; iter; iter = iter->next)
+    {
+      const IdeRunHandlerInfo *info = iter->data;
+
+      if (g_strcmp0 (info->id, id) == 0)
+        {
+          self->handler = info;
+          IDE_TRACE_MSG ("run handler set to %s", info->title);
+          g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HANDLER]);
+          break;
+        }
+    }
+}
+
+void
+ide_run_manager_add_handler (IdeRunManager  *self,
+                             const gchar    *id,
+                             const gchar    *title,
+                             const gchar    *icon_name,
+                             const gchar    *accel,
+                             IdeRunHandler   run_handler,
+                             gpointer        user_data,
+                             GDestroyNotify  user_data_destroy)
+{
+  IdeRunHandlerInfo *info;
+  g_autofree gchar *action_name = NULL;
+  const gchar *accels[] = { accel, NULL };
+
+  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+  g_return_if_fail (id != NULL);
+  g_return_if_fail (title != NULL);
+
+  info = g_slice_new (IdeRunHandlerInfo);
+  info->id = g_strdup (id);
+  info->title = g_strdup (title);
+  info->icon_name = g_strdup (icon_name);
+  info->accel = g_strdup (accel);
+  info->handler = run_handler;
+  info->handler_data = user_data;
+  info->handler_data_destroy = user_data_destroy;
+
+  action_name = g_strdup_printf ("run-manager.run-with-handler('%s')", id);
+
+  gtk_application_set_accels_for_action (GTK_APPLICATION (g_application_get_default()),
+                                         action_name,
+                                         accels);
+
+  self->handlers = g_list_append (self->handlers, info);
+
+  if (self->handler == NULL)
+    self->handler = info;
+}
+
+void
+ide_run_manager_remove_handler (IdeRunManager *self,
+                                const gchar   *id)
+{
+  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+  g_return_if_fail (id != NULL);
+
+  for (GList *iter = self->handlers; iter; iter = iter->next)
+    {
+      IdeRunHandlerInfo *info = iter->data;
+
+      if (g_strcmp0 (info->id, id) == 0)
+        {
+          self->handlers = g_list_remove_link (self->handlers, iter);
+
+          if (self->handler == info && self->handlers != NULL)
+            self->handler = self->handlers->data;
+          else
+            self->handler = NULL;
+
+          ide_run_handler_info_free (info);
+
+          break;
+        }
+    }
+}
+
+/**
+ * ide_run_manager_get_build_target:
+ *
+ * Gets the build target that will be executed by the run manager if a
+ * specific build target has not been specified to ide_run_manager_run_async().
+ *
+ * Returns: (transfer none): An #IdeBuildTarget or %NULL if no build target
+ *   has been set.
+ */
+IdeBuildTarget *
+ide_run_manager_get_build_target (IdeRunManager *self)
+{
+  g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+
+  return self->build_target;
+}
+
+static IdeBuildTarget *
+find_best_target (GPtrArray *targets)
+{
+  IdeBuildTarget *ret = NULL;
+  guint i;
+
+  g_assert (targets != NULL);
+
+  /* TODO:
+   *
+   * This is just a barebones way to try to discover a target that matters. We
+   * could probably defer this off to the build system. Either way, it's shit
+   * and should be thought through by someone.
+   */
+
+  for (i = 0; i < targets->len; i++)
+    {
+      IdeBuildTarget *target = g_ptr_array_index (targets, i);
+      g_autoptr(GFile) installdir = NULL;
+
+      installdir = ide_build_target_get_install_directory (target);
+
+      if (installdir == NULL)
+        continue;
+
+      if (ret == NULL)
+        ret = target;
+    }
+
+  return ret;
+}
+
+static void
+ide_run_manager_discover_default_target_cb (GObject      *object,
+                                            GAsyncResult *result,
+                                            gpointer      user_data)
+{
+  IdeBuildSystem *build_system = (IdeBuildSystem *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GPtrArray) targets = NULL;
+  IdeBuildTarget *best_match;
+  GError *error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  targets = ide_build_system_get_build_targets_finish (build_system, result, &error);
+
+  if (targets == NULL)
+    {
+      g_task_return_error (task, error);
+      IDE_EXIT;
+    }
+
+  best_match = find_best_target (targets);
+
+  if (best_match == NULL)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Failed to locate build target");
+      IDE_EXIT;
+    }
+
+  g_task_return_pointer (task, g_object_ref (best_match), g_object_unref);
+
+  IDE_EXIT;
+}
+
+void
+ide_run_manager_discover_default_target_async (IdeRunManager       *self,
+                                               GCancellable        *cancellable,
+                                               GAsyncReadyCallback  callback,
+                                               gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  IdeBuildSystem *build_system;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_run_manager_discover_default_target_async);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  build_system = ide_context_get_build_system (context);
+
+  ide_build_system_get_build_targets_async (build_system,
+                                            cancellable,
+                                            ide_run_manager_discover_default_target_cb,
+                                            g_object_ref (task));
+
+  IDE_EXIT;
+}
+
+/**
+ * ide_run_manager_discover_default_target_finish:
+ *
+ * Returns: (transfer none): An #IdeBuildTarget if successful; otherwise %NULL
+ *   and @error is set.
+ */
+IdeBuildTarget *
+ide_run_manager_discover_default_target_finish (IdeRunManager  *self,
+                                                GAsyncResult   *result,
+                                                GError        **error)
+{
+  IdeBuildTarget *ret;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  ret = g_task_propagate_pointer (G_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+const GList *
+_ide_run_manager_get_handlers (IdeRunManager *self)
+{
+  g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+
+  return self->handlers;
+}
+
+const gchar *
+ide_run_manager_get_handler (IdeRunManager *self)
+{
+  g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
+
+  if (self->handler != NULL)
+    return self->handler->id;
+
+  return NULL;
+}
+
+static const gchar *action_names[] = {
+  "run",
+  "run-with-handler",
+  "stop",
+  NULL
+};
+
+static gboolean
+ide_run_manager_has_action (GActionGroup *group,
+                            const gchar  *action_name)
+{
+  g_assert (G_IS_ACTION_GROUP (group));
+  g_assert (action_name != NULL);
+
+  for (guint i = 0; action_names[i]; i++)
+    {
+      if (g_strcmp0 (action_names[i], action_name) == 0)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gchar **
+ide_run_manager_list_actions (GActionGroup *group)
+{
+  return g_strdupv ((gchar **)action_names);
+}
+
+static gboolean
+ide_run_manager_query_action (GActionGroup        *group,
+                              const gchar         *action_name,
+                              gboolean            *enabled,
+                              const GVariantType **parameter_type,
+                              const GVariantType **state_type,
+                              GVariant           **state_hint,
+                              GVariant           **state)
+{
+  IdeRunManager *self = (IdeRunManager *)group;
+  const GVariantType *real_parameter_type = NULL;
+  gboolean real_enabled = FALSE;
+
+  g_assert (IDE_IS_RUN_MANAGER (self));
+  g_assert (action_name != NULL);
+
+  if (g_strcmp0 (action_name, "run-with-handler") == 0)
+    {
+      real_enabled = self->busy == FALSE;
+      real_parameter_type = G_VARIANT_TYPE_STRING;
+      goto finish;
+    }
+
+  if (g_strcmp0 (action_name, "run") == 0)
+    {
+      real_enabled = self->busy == FALSE;
+      goto finish;
+    }
+
+  if (g_strcmp0 (action_name, "stop") == 0)
+    {
+      real_enabled = self->busy == TRUE;
+      goto finish;
+    }
+
+finish:
+  if (state_type)
+    *state_type = NULL;
+
+  if (state_hint)
+    *state_hint = NULL;
+
+  if (state)
+    *state = NULL;
+
+  if (enabled)
+    *enabled = real_enabled;
+
+  if (parameter_type)
+    *parameter_type = real_parameter_type;
+
+  return TRUE;
+}
+
+static void
+ide_run_manager_activate_action (GActionGroup *group,
+                                 const gchar  *action_name,
+                                 GVariant     *parameter)
+{
+  IdeRunManager *self = (IdeRunManager *)group;
+  g_autoptr(GVariant) sunk = NULL;
+
+  g_assert (IDE_IS_RUN_MANAGER (self));
+  g_assert (action_name != NULL);
+
+  if (g_variant_is_floating (parameter))
+    sunk = g_variant_ref_sink (parameter);
+
+  if (FALSE) {}
+  else if (g_strcmp0 (action_name, "run-with-handler") == 0)
+    {
+      const gchar *handler = g_variant_get_string (parameter, NULL);
+
+      /* "" translates to current handler */
+      if (handler && *handler)
+        ide_run_manager_set_handler (self, handler);
+
+      ide_run_manager_run_async (self, NULL, NULL, NULL, NULL);
+    }
+  else if (g_strcmp0 (action_name, "run") == 0)
+    {
+      ide_run_manager_run_async (self, NULL, NULL, NULL, NULL);
+    }
+  else if (g_strcmp0 (action_name, "stop") == 0)
+    {
+      ide_run_manager_cancel (self);
+    }
+}
+
+static void
+action_group_iface_init (GActionGroupInterface *iface)
+{
+  iface->has_action = ide_run_manager_has_action;
+  iface->list_actions = ide_run_manager_list_actions;
+  iface->query_action = ide_run_manager_query_action;
+  iface->activate_action = ide_run_manager_activate_action;
+}
diff --git a/libide/runner/ide-run-manager.h b/libide/runner/ide-run-manager.h
index 49a3e3f..81356f8 100644
--- a/libide/runner/ide-run-manager.h
+++ b/libide/runner/ide-run-manager.h
@@ -21,22 +21,50 @@
 
 #include "ide-object.h"
 
+#include "buildsystem/ide-build-target.h"
+#include "runner/ide-runner.h"
+
 G_BEGIN_DECLS
 
 #define IDE_TYPE_RUN_MANAGER (ide_run_manager_get_type())
 
 G_DECLARE_FINAL_TYPE (IdeRunManager, ide_run_manager, IDE, RUN_MANAGER, IdeObject)
 
-void     ide_run_manager_cancel     (IdeRunManager        *self);
-gboolean ide_run_manager_get_busy   (IdeRunManager        *self);
-void     ide_run_manager_run_async  (IdeRunManager        *self,
-                                     IdeBuildTarget       *build_target,
-                                     GCancellable         *cancellable,
-                                     GAsyncReadyCallback   callback,
-                                     gpointer              user_data);
-gboolean ide_run_manager_run_finish (IdeRunManager        *self,
-                                     GAsyncResult         *result,
-                                     GError              **error);
+typedef void (*IdeRunHandler) (IdeRunManager *self,
+                               IdeRunner     *runner,
+                               gpointer       user_data);
+
+IdeBuildTarget *ide_run_manager_get_build_target               (IdeRunManager        *self);
+void            ide_run_manager_cancel                         (IdeRunManager        *self);
+gboolean        ide_run_manager_get_busy                       (IdeRunManager        *self);
+const gchar    *ide_run_manager_get_handler                    (IdeRunManager        *self);
+void            ide_run_manager_set_handler                    (IdeRunManager        *self,
+                                                                const gchar          *id);
+void            ide_run_manager_add_handler                    (IdeRunManager        *self,
+                                                                const gchar          *id,
+                                                                const gchar          *title,
+                                                                const gchar          *icon_name,
+                                                                const gchar          *accel,
+                                                                IdeRunHandler         run_handler,
+                                                                gpointer              user_data,
+                                                                GDestroyNotify        user_data_destroy);
+void            ide_run_manager_remove_handler                 (IdeRunManager        *self,
+                                                                const gchar          *id);
+void            ide_run_manager_run_async                      (IdeRunManager        *self,
+                                                                IdeBuildTarget       *build_target,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+gboolean        ide_run_manager_run_finish                     (IdeRunManager        *self,
+                                                                GAsyncResult         *result,
+                                                                GError              **error);
+void            ide_run_manager_discover_default_target_async  (IdeRunManager        *self,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+IdeBuildTarget *ide_run_manager_discover_default_target_finish (IdeRunManager        *self,
+                                                                GAsyncResult        *result,
+                                                                GError              **error);
 
 G_END_DECLS
 


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