[gnome-builder/fix-command-bar] commands: use proposed Gtk API to simplify command resolution



commit f173a644b78e684d5a9b896eecf53b2e13cd0f72
Author: Christian Hergert <christian hergert me>
Date:   Thu Dec 11 02:57:40 2014 -0800

    commands: use proposed Gtk API to simplify command resolution

 src/commands/gb-command-gaction-provider.c |  303 ++++++++++++----------------
 src/commands/gb-command-gaction.c          |  217 ++++++++++++++++++++
 src/commands/gb-command-gaction.h          |   55 +++++
 src/gnome-builder.mk                       |    2 +
 4 files changed, 405 insertions(+), 172 deletions(-)
---
diff --git a/src/commands/gb-command-gaction-provider.c b/src/commands/gb-command-gaction-provider.c
index 2a63558..9a9b514 100644
--- a/src/commands/gb-command-gaction-provider.c
+++ b/src/commands/gb-command-gaction-provider.c
@@ -21,6 +21,7 @@
 #include <string.h>
 
 #include "gb-command-gaction-provider.h"
+#include "gb-command-gaction.h"
 #include "gb-log.h"
 
 G_DEFINE_TYPE (GbCommandGactionProvider, gb_command_gaction_provider,
@@ -34,246 +35,204 @@ gb_command_gaction_provider_new (GbWorkbench *workbench)
                        NULL);
 }
 
-static GbCommandResult *
-execute_action (GbCommand *command,
-                gpointer   user_data)
+static GList *
+discover_groups (GbCommandGactionProvider *provider)
 {
-  GAction *action;
-  GVariant *params;
+  GbDocumentView *view;
+  GApplication *application;
+  GbWorkbench *workbench;
+  GtkWidget *widget;
+  GList *list = NULL;
+
+  g_return_val_if_fail (GB_IS_COMMAND_GACTION_PROVIDER (provider), NULL);
+
+  view = gb_command_provider_get_active_view (GB_COMMAND_PROVIDER (provider));
+
+  g_print ("Active View: %p\n", view);
+  if (view)
+    g_print ("== %s\n", g_type_name (G_TYPE_FROM_INSTANCE (view)));
 
-  g_return_val_if_fail (GB_IS_COMMAND (command), NULL);
+  for (widget = GTK_WIDGET (view);
+       widget;
+       widget = gtk_widget_get_parent (widget))
+    {
+      gchar **prefixes;
+      guint i;
 
-  action = g_object_get_data (G_OBJECT (command), "action");
-  g_return_val_if_fail (G_IS_ACTION (action), NULL);
+      prefixes = gtk_widget_list_action_prefixes (widget);
 
-  params = g_object_get_data (G_OBJECT (command), "parameters");
+      if (prefixes)
+        {
+          GActionGroup *group;
 
-  g_action_activate (action, params);
+          for (i = 0; prefixes [i]; i++)
+            {
+              g_print (" Group = %s\n", prefixes [i]);
 
-  return NULL;
+              group = gtk_widget_get_action_group (widget, prefixes [i]);
+
+              if (G_IS_ACTION_GROUP (group))
+                list = g_list_append (list, group);
+            }
+
+          g_strfreev (prefixes);
+        }
+    }
+
+  workbench = gb_command_provider_get_workbench (GB_COMMAND_PROVIDER (provider));
+  list = g_list_append (list, G_ACTION_GROUP (workbench));
+
+  application = g_application_get_default ();
+  list = g_list_append (list, G_ACTION_GROUP (application));
+
+  return list;
 }
 
-static GbCommand *
-gb_command_gaction_provider_lookup (GbCommandProvider *provider,
-                                    const gchar       *command_text)
+static gboolean
+parse_command_text (const gchar  *command_text,
+                    gchar       **name,
+                    GVariant    **params)
 {
-  GbCommandGactionProvider *self = (GbCommandGactionProvider *)provider;
-  GtkWidget *widget;
-  GbCommand *command = NULL;
-  GVariant *parameters = NULL;
-  GAction *action = NULL;
-  gchar **parts;
+  GVariant *ret_params = NULL;
+  const gchar *str;
   gchar *tmp;
-  gchar *name = NULL;
+  gchar **parts;
+  gchar *ret_name = NULL;
 
-  ENTRY;
+  g_return_val_if_fail (command_text, FALSE);
+  g_return_val_if_fail (name, FALSE);
+  g_return_val_if_fail (params, FALSE);
 
-  g_return_val_if_fail (GB_IS_COMMAND_GACTION_PROVIDER (self), NULL);
+  *name = NULL;
+  *params = NULL;
 
   /* Determine the command name */
   tmp = g_strdelimit (g_strdup (command_text), "(", ' ');
   parts = g_strsplit (tmp, " ", 2);
-  name = g_strdup (parts [0]);
+  ret_name = g_strdup (parts [0]);
   g_free (tmp);
   g_strfreev (parts);
 
-  /* Parse the parameters if provided */
-  command_text += strlen (name);
-  for (; *command_text; command_text = g_utf8_next_char (command_text))
+  /* Advance to the (optional) parameters */
+  for (str = command_text + strlen (ret_name);
+       *str;
+       str = g_utf8_next_char (str))
     {
       gunichar ch;
 
-      ch = g_utf8_get_char (command_text);
+      ch = g_utf8_get_char (str);
       if (g_unichar_isspace (ch))
         continue;
+
       break;
     }
 
-  if (*command_text)
+  /* Parse any (optional) parameters */
+  if (*str != '\0')
     {
-      parameters = g_variant_parse (NULL, command_text, NULL, NULL, NULL);
-      if (!parameters)
-        GOTO (cleanup);
+      ret_params = g_variant_parse (NULL, str, NULL, NULL, NULL);
+      if (!ret_params)
+        goto failure;
     }
 
-  /*
-   * TODO: We are missing some API in Gtk+ to be able to resolve an action
-   *       for a particular name. It exists, but is currently only private
-   *       API. So we'll hold off a bit on this until we can use that.
-   *       Alternatively, we can just walk up the chain to the
-   *       ApplicationWindow which is a GActionMap.
-   */
+  *name = ret_name;
+  *params = ret_params;
 
-  widget = GTK_WIDGET (gb_command_provider_get_active_view (provider));
-  while (widget)
-    {
-      if (G_IS_ACTION_MAP (widget))
-        {
-          action = g_action_map_lookup_action (G_ACTION_MAP (widget), name);
-          if (action)
-            break;
-        }
+  return TRUE;
 
-      widget = gtk_widget_get_parent (widget);
-    }
+failure:
+  g_clear_pointer (&ret_name, g_free);
+  g_clear_pointer (&ret_params, g_variant_unref);
 
-  /*
-   * Now try to lookup the action from the workspace up, which is the case if
-   * we don't have an active view.
-   */
-  if (!action)
-    {
-      GbWorkbench *workbench;
-      GbWorkspace *workspace;
+  return FALSE;
+}
 
-      workbench = gb_command_provider_get_workbench (provider);
-      workspace = gb_workbench_get_active_workspace (workbench);
-      widget = GTK_WIDGET (workspace);
+static GbCommand *
+gb_command_gaction_provider_lookup (GbCommandProvider *provider,
+                                    const gchar       *command_text)
+{
+  GbCommandGactionProvider *self = (GbCommandGactionProvider *)provider;
+  GbCommand *command = NULL;
+  GVariant *params = NULL;
+  GList *groups;
+  GList *iter;
+  gchar *action_name = NULL;
 
-      while (widget)
-        {
-          if (G_IS_ACTION_MAP (widget))
-            {
-              action = g_action_map_lookup_action (G_ACTION_MAP (widget), name);
-              if (action)
-                break;
-            }
-          else if (GB_IS_WORKSPACE (widget))
-            {
-              GActionGroup *group;
-
-              group = gb_workspace_get_actions (GB_WORKSPACE (widget));
-              if (G_IS_ACTION_MAP (group))
-                {
-                  action = g_action_map_lookup_action (G_ACTION_MAP (group), name);
-                  if (action)
-                    break;
-                }
-            }
+  ENTRY;
 
-          widget = gtk_widget_get_parent (widget);
-        }
-    }
+  g_return_val_if_fail (GB_IS_COMMAND_GACTION_PROVIDER (self), NULL);
+  g_return_val_if_fail (command_text, NULL);
 
-  /*
-   * Now try to lookup the action inside of the GApplication.
-   * This is useful for stuff like "quit", and "preferences".
-   */
-  if (!action)
-    {
-      GApplication *app;
+  if (!parse_command_text (command_text, &action_name, &params))
+    RETURN (NULL);
 
-      app = g_application_get_default ();
-      action = g_action_map_lookup_action (G_ACTION_MAP (app), name);
-    }
+  groups = discover_groups (self);
 
-  if (!action && parameters)
+  for (iter = groups; iter; iter = iter->next)
     {
-      g_variant_unref (parameters);
-      parameters = NULL;
-    }
+      GActionGroup *group = G_ACTION_GROUP (iter->data);
 
-  if (action)
-    {
-      command = gb_command_new ();
-      if (parameters)
-        g_object_set_data_full (G_OBJECT (command), "parameters",
-                                g_variant_ref (parameters),
-                                (GDestroyNotify)g_variant_unref);
-      g_object_set_data_full (G_OBJECT (command), "action",
-                              g_object_ref (action), g_object_unref);
-      g_signal_connect (command, "execute", G_CALLBACK (execute_action), NULL);
+      if (g_action_group_has_action (group, action_name))
+        {
+          command = g_object_new (GB_TYPE_COMMAND_GACTION,
+                                  "action-group", group,
+                                  "action-name", action_name,
+                                  "parameters", params,
+                                  NULL);
+          break;
+        }
     }
 
-
-cleanup:
-  g_free (name);
+  g_clear_pointer (&params, g_variant_unref);
+  g_list_free (groups);
+  g_free (action_name);
 
   RETURN (command);
 }
 
 static void
-add_completions_from_group (GPtrArray         *completions,
-                            const gchar       *prefix,
-                            GActionGroup      *group)
-{
-  gchar **actions = g_action_group_list_actions (group);
-  int i;
-
-  for (i = 0; actions[i] != NULL; i++)
-    {
-      if (g_str_has_prefix (actions[i], prefix))
-        g_ptr_array_add (completions, g_strdup (actions[i]));
-    }
-
-  g_strfreev (actions);
-}
-
-static void
 gb_command_gaction_provider_complete (GbCommandProvider *provider,
                                       GPtrArray         *completions,
                                       const gchar       *initial_command_text)
 {
   GbCommandGactionProvider *self = (GbCommandGactionProvider *)provider;
-  GtkWidget *widget;
-  const gchar *tmp;
-  gchar *prefix;
-  GApplication *app;
-  GbWorkbench *workbench;
-  GbWorkspace *workspace;
+  GList *groups;
+  GList *iter;
 
   ENTRY;
 
   g_return_if_fail (GB_IS_COMMAND_GACTION_PROVIDER (self));
+  g_return_if_fail (initial_command_text);
 
-  tmp = initial_command_text;
-
-  while (*tmp != 0 && *tmp != ' ' && *tmp != '(')
-    tmp++;
-
-  if (*tmp != 0)
-    return;
+  g_print ("initial=\"%s\"\n", initial_command_text);
 
-  prefix = g_strndup (initial_command_text, tmp - initial_command_text);
+  groups = discover_groups (self);
 
-  widget = GTK_WIDGET (gb_command_provider_get_active_view (provider));
-  while (widget)
+  for (iter = groups; iter; iter = iter->next)
     {
-      if (G_IS_ACTION_GROUP (widget))
-        add_completions_from_group (completions, prefix, G_ACTION_GROUP (widget));
+      GActionGroup *group = iter->data;
+      gchar **names;
+      guint i;
 
-      widget = gtk_widget_get_parent (widget);
-    }
+      g_assert (G_IS_ACTION_GROUP (group));
 
-  /*
-   * Now try to lookup the action from the workspace up, which is the case if
-   * we don't have an active view.
-   */
-  workbench = gb_command_provider_get_workbench (provider);
-  workspace = gb_workbench_get_active_workspace (workbench);
-  widget = GTK_WIDGET (workspace);
+      g_print ("Group %p\n", group);
 
-  while (widget)
-    {
-      if (G_IS_ACTION_GROUP (widget))
-        add_completions_from_group (completions, prefix, G_ACTION_GROUP (widget));
-      else if (GB_IS_WORKSPACE (widget))
-        add_completions_from_group (completions, prefix,
-                                   gb_workspace_get_actions (GB_WORKSPACE (widget)));
+      names = g_action_group_list_actions (group);
 
-      widget = gtk_widget_get_parent (widget);
-    }
+      for (i = 0; names [i]; i++)
+        {
+          g_print ("> %s\n", names[i]);
+          if (g_str_has_prefix (names [i], initial_command_text))
+            g_ptr_array_add (completions, g_strdup (names [i]));
+        }
 
-  /*
-   * Now try to lookup the action inside of the GApplication.
-   * This is useful for stuff like "quit", and "preferences".
-   */
-  app = g_application_get_default ();
-  add_completions_from_group (completions, prefix, G_ACTION_GROUP (app));
+      //g_free (names);
+    }
 
-  g_free (prefix);
+  g_list_free (groups);
 
-  RETURN();
+  EXIT;
 }
 
 static void
diff --git a/src/commands/gb-command-gaction.c b/src/commands/gb-command-gaction.c
new file mode 100644
index 0000000..bd34b96
--- /dev/null
+++ b/src/commands/gb-command-gaction.c
@@ -0,0 +1,217 @@
+/* gb-command-gaction.c
+ *
+ * Copyright (C) 2014 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/>.
+ */
+
+#define G_LOG_DOMAIN "command-gaction"
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "gb-command-gaction.h"
+
+struct _GbCommandGactionPrivate
+{
+  GActionGroup *action_group;
+  gchar        *action_name;
+  GVariant     *parameters;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbCommandGaction, gb_command_gaction,
+                            GB_TYPE_COMMAND)
+
+enum {
+  PROP_0,
+  PROP_ACTION_GROUP,
+  PROP_ACTION_NAME,
+  PROP_PARAMETERS,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+static void
+gb_command_gaction_set_action_group (GbCommandGaction *gaction,
+                                     GActionGroup     *action_group)
+{
+  g_return_if_fail (GB_IS_COMMAND_GACTION (gaction));
+  g_return_if_fail (G_IS_ACTION_GROUP (action_group));
+
+  if (gaction->priv->action_group != action_group)
+    {
+      g_clear_object (&gaction->priv->action_group);
+      gaction->priv->action_group = g_object_ref (action_group);
+    }
+}
+
+static void
+gb_command_gaction_set_action_name (GbCommandGaction *gaction,
+                                    const gchar      *action_name)
+{
+  g_return_if_fail (GB_IS_COMMAND_GACTION (gaction));
+
+  if (gaction->priv->action_name != action_name)
+    {
+      g_clear_pointer (&gaction->priv->action_name, g_free);
+      gaction->priv->action_name = g_strdup (action_name);
+    }
+}
+
+static void
+gb_command_gaction_set_parameters (GbCommandGaction *gaction,
+                                   GVariant         *variant)
+{
+  g_return_if_fail (GB_IS_COMMAND_GACTION (gaction));
+
+  if (gaction->priv->parameters != variant)
+    {
+      g_clear_pointer (&gaction->priv->parameters, g_variant_unref);
+      gaction->priv->parameters = g_variant_ref (variant);
+    }
+}
+
+static GbCommandResult *
+gb_command_gaction_execute (GbCommand *command)
+{
+  GbCommandGaction *self = (GbCommandGaction *)command;
+
+  g_return_val_if_fail (GB_IS_COMMAND_GACTION (self), NULL);
+
+  if (self->priv->action_group &&
+      self->priv->action_name &&
+      g_action_group_has_action (self->priv->action_group,
+                                 self->priv->action_name))
+    {
+      g_action_group_activate_action (self->priv->action_group,
+                                      self->priv->action_name,
+                                      self->priv->parameters);
+    }
+
+  return NULL;
+}
+
+static void
+gb_command_gaction_finalize (GObject *object)
+{
+  GbCommandGactionPrivate *priv = GB_COMMAND_GACTION (object)->priv;
+
+  g_clear_object (&priv->action_group);
+  g_clear_pointer (&priv->action_name, g_free);
+  g_clear_pointer (&priv->parameters, g_variant_unref);
+
+  G_OBJECT_CLASS (gb_command_gaction_parent_class)->finalize (object);
+}
+
+static void
+gb_command_gaction_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  GbCommandGaction *self = GB_COMMAND_GACTION (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTION_GROUP:
+      g_value_set_object (value, self->priv->action_group);
+      break;
+
+    case PROP_ACTION_NAME:
+      g_value_set_string (value, self->priv->action_name);
+      break;
+
+    case PROP_PARAMETERS:
+      g_value_set_variant (value, self->priv->parameters);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gb_command_gaction_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  GbCommandGaction *self = GB_COMMAND_GACTION (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTION_GROUP:
+      gb_command_gaction_set_action_group (self, g_value_get_object (value));
+      break;
+
+    case PROP_ACTION_NAME:
+      gb_command_gaction_set_action_name (self, g_value_get_string (value));
+      break;
+
+    case PROP_PARAMETERS:
+      gb_command_gaction_set_parameters (self, g_value_get_variant (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gb_command_gaction_class_init (GbCommandGactionClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GbCommandClass *command_class = GB_COMMAND_CLASS (klass);
+
+  object_class->finalize = gb_command_gaction_finalize;
+  object_class->get_property = gb_command_gaction_get_property;
+  object_class->set_property = gb_command_gaction_set_property;
+
+  command_class->execute = gb_command_gaction_execute;
+
+  gParamSpecs [PROP_ACTION_GROUP] =
+    g_param_spec_object ("action-group",
+                         _("Action Group"),
+                         _("The GActionGroup containing the action."),
+                         G_TYPE_ACTION_GROUP,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_ACTION_GROUP,
+                                   gParamSpecs [PROP_ACTION_GROUP]);
+
+  gParamSpecs [PROP_ACTION_NAME] =
+    g_param_spec_string ("action-name",
+                         _("Action Name"),
+                         _("The name of the action to execute."),
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_ACTION_NAME,
+                                   gParamSpecs [PROP_ACTION_NAME]);
+
+  gParamSpecs [PROP_PARAMETERS] =
+    g_param_spec_variant ("parameters",
+                          _("Parameters"),
+                          _("The parameters for the action."),
+                          G_VARIANT_TYPE_ANY,
+                          NULL,
+                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_PARAMETERS,
+                                   gParamSpecs [PROP_PARAMETERS]);
+}
+
+static void
+gb_command_gaction_init (GbCommandGaction *self)
+{
+  self->priv = gb_command_gaction_get_instance_private (self);
+}
diff --git a/src/commands/gb-command-gaction.h b/src/commands/gb-command-gaction.h
new file mode 100644
index 0000000..942ad0e
--- /dev/null
+++ b/src/commands/gb-command-gaction.h
@@ -0,0 +1,55 @@
+/* gb-command-gaction.h
+ *
+ * Copyright (C) 2014 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 GB_COMMAND_GACTION_H
+#define GB_COMMAND_GACTION_H
+
+#include "gb-command.h"
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_COMMAND_GACTION            (gb_command_gaction_get_type())
+#define GB_COMMAND_GACTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_COMMAND_GACTION, 
GbCommandGaction))
+#define GB_COMMAND_GACTION_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_COMMAND_GACTION, 
GbCommandGaction const))
+#define GB_COMMAND_GACTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GB_TYPE_COMMAND_GACTION, 
GbCommandGactionClass))
+#define GB_IS_COMMAND_GACTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GB_TYPE_COMMAND_GACTION))
+#define GB_IS_COMMAND_GACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GB_TYPE_COMMAND_GACTION))
+#define GB_COMMAND_GACTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GB_TYPE_COMMAND_GACTION, 
GbCommandGactionClass))
+
+typedef struct _GbCommandGaction        GbCommandGaction;
+typedef struct _GbCommandGactionClass   GbCommandGactionClass;
+typedef struct _GbCommandGactionPrivate GbCommandGactionPrivate;
+
+struct _GbCommandGaction
+{
+       GbCommand parent;
+
+       /*< private >*/
+       GbCommandGactionPrivate *priv;
+};
+
+struct _GbCommandGactionClass
+{
+       GbCommandClass parent;
+};
+
+GType gb_command_gaction_get_type (void);
+
+G_END_DECLS
+
+#endif /* GB_COMMAND_GACTION_H */
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index 55fb7db..73fb02f 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -27,6 +27,8 @@ libgnome_builder_la_SOURCES = \
        src/commands/gb-command-bar.h \
        src/commands/gb-command-bar-item.c \
        src/commands/gb-command-bar-item.h \
+       src/commands/gb-command-gaction.c \
+       src/commands/gb-command-gaction.h \
        src/commands/gb-command-gaction-provider.c \
        src/commands/gb-command-gaction-provider.h \
        src/commands/gb-command-manager.c \


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