[gtk+] Add new GtkActionable interface



commit 88ec007b9858132720cc602ac1a63396f66116bc
Author: Ryan Lortie <desrt desrt ca>
Date:   Thu Jan 5 22:22:06 2012 -0500

    Add new GtkActionable interface
    
    This is the interface for GtkWidgets that can be associated with an
    action on a GtkAppicationWindow or associated GtkApplication.
    
    It essentially features 'action-name' and 'action-target' properties
    with some associated convenience API.
    
    This interface is implemented by GtkButton and GtkToolButton.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=667394

 gtk/Makefile.am             |    4 +
 gtk/gsimpleactionobserver.c |  288 +++++++++++++++++++++++++++++++++++++++++++
 gtk/gsimpleactionobserver.h |   53 ++++++++
 gtk/gtk.h                   |    1 +
 gtk/gtkactionable.c         |  117 +++++++++++++++++
 gtk/gtkactionable.h         |   71 +++++++++++
 gtk/gtkapplicationprivate.h |    6 +
 gtk/gtkapplicationwindow.c  |   24 +++-
 gtk/gtkbutton.c             |  161 ++++++++++++++++++++++++-
 gtk/gtkbuttonprivate.h      |    5 +
 gtk/gtktoolbutton.c         |   72 +++++++++++-
 11 files changed, 791 insertions(+), 11 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index ab46644..97719ac 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -172,6 +172,7 @@ gtk_public_h_sources = 		\
 	gtkaccellabel.h		\
 	gtkaccelmap.h		\
 	gtkaccessible.h		\
+	gtkactionable.h		\
 	gtkaction.h		\
 	gtkactiongroup.h	\
 	gtkactivatable.h	\
@@ -392,6 +393,7 @@ gtk_private_type_h_sources = \
 # GTK+ header files that don't get installed
 gtk_private_h_sources =		\
 	gactionmuxer.h		\
+	gsimpleactionobserver.h	\
 	gactionobserver.h	\
 	gactionobservable.h	\
 	gtkapplicationprivate.h	\
@@ -512,8 +514,10 @@ deprecated_c_sources = 			\
 gtk_base_c_sources = 		\
 	$(deprecated_c_sources)	\
 	gactionmuxer.c		\
+	gsimpleactionobserver.c	\
 	gactionobserver.c	\
 	gactionobservable.c	\
+	gtkactionable.c		\
 	gtkquery.c		\
 	gtksearchengine.c	\
 	gtksearchenginesimple.c	\
diff --git a/gtk/gsimpleactionobserver.c b/gtk/gsimpleactionobserver.c
new file mode 100644
index 0000000..fdbe4bf
--- /dev/null
+++ b/gtk/gsimpleactionobserver.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright  2012 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Authors: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gsimpleactionobserver.h"
+#include "gactionobservable.h"
+
+typedef GObjectClass GSimpleActionObserverClass;
+struct _GSimpleActionObserver
+{
+  GObject parent_instance;
+
+  GActionGroup *action_group;
+  gchar *action_name;
+  GVariant *target;
+
+  gboolean can_activate;
+  gboolean active;
+  gboolean enabled;
+
+  gint reporting;
+};
+
+static void g_simple_action_observer_init_iface (GActionObserverInterface *iface);
+G_DEFINE_TYPE_WITH_CODE (GSimpleActionObserver, g_simple_action_observer, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, g_simple_action_observer_init_iface));
+
+enum
+{
+  PROP_0,
+  PROP_ACTIVE,
+  PROP_ENABLED,
+  N_PROPS
+};
+
+static GParamSpec *g_simple_action_observer_pspecs[N_PROPS];
+
+static void
+g_simple_action_observer_action_added (GActionObserver    *g_observer,
+                                       GActionObservable  *observable,
+                                       const gchar        *action_name,
+                                       const GVariantType *parameter_type,
+                                       gboolean            enabled,
+                                       GVariant           *state)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
+  gboolean active;
+
+  /* we can only activate if we have the correct type of parameter */
+  observer->can_activate = (observer->target == NULL && parameter_type == NULL) ||
+                            (observer->target != NULL && parameter_type != NULL &&
+                             g_variant_is_of_type (observer->target, parameter_type));
+
+  if (observer->can_activate)
+    {
+      if (observer->target != NULL && state != NULL)
+        active = g_variant_equal (state, observer->target);
+
+      else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+        active = g_variant_get_boolean (state);
+
+      else
+        active = FALSE;
+
+      if (active != observer->active)
+        {
+          observer->active = active;
+          observer->reporting++;
+          g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
+          observer->reporting--;
+        }
+
+      if (enabled != observer->enabled)
+        {
+          observer->enabled = enabled;
+          g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
+        }
+    }
+}
+
+static void
+g_simple_action_observer_action_enabled_changed (GActionObserver   *g_observer,
+                                                 GActionObservable *observable,
+                                                 const gchar       *action_name,
+                                                 gboolean           enabled)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
+
+  if (!observer->can_activate)
+    return;
+
+  if (enabled != observer->enabled)
+    {
+      observer->enabled = enabled;
+      g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
+    }
+}
+
+static void
+g_simple_action_observer_action_state_changed (GActionObserver   *g_observer,
+                                               GActionObservable *observable,
+                                               const gchar       *action_name,
+                                               GVariant          *state)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
+  gboolean active = FALSE;
+
+  if (!observer->can_activate)
+    return;
+
+  if (observer->target)
+    active = g_variant_equal (state, observer->target);
+
+  else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+    active = g_variant_get_boolean (state);
+
+  if (active != observer->active)
+    {
+      observer->active = active;
+      observer->reporting++;
+      g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
+      observer->reporting--;
+    }
+}
+
+static void
+g_simple_action_observer_action_removed (GActionObserver   *g_observer,
+                                         GActionObservable *observable,
+                                         const gchar       *action_name)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (g_observer);
+
+  if (!observer->can_activate)
+    return;
+
+  observer->can_activate = FALSE;
+
+  if (observer->active)
+    {
+      observer->active = FALSE;
+      observer->reporting++;
+      g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ACTIVE]);
+      observer->reporting--;
+    }
+
+  if (observer->enabled)
+    {
+      observer->enabled = FALSE;
+      g_object_notify_by_pspec (G_OBJECT (observer), g_simple_action_observer_pspecs[PROP_ENABLED]);
+    }
+}
+
+static void
+g_simple_action_observer_get_property (GObject *object, guint prop_id,
+                                       GValue *value, GParamSpec *pspec)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, observer->active);
+      break;
+
+    case PROP_ENABLED:
+      g_value_set_boolean (value, observer->enabled);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+g_simple_action_observer_finalize (GObject *object)
+{
+  GSimpleActionObserver *observer = G_SIMPLE_ACTION_OBSERVER (object);
+
+  g_object_unref (observer->action_group);
+  g_free (observer->action_name);
+
+  if (observer->target)
+    g_variant_unref (observer->target);
+
+  G_OBJECT_CLASS (g_simple_action_observer_parent_class)
+    ->finalize (object);
+}
+
+static void
+g_simple_action_observer_init (GSimpleActionObserver *observer)
+{
+}
+
+static void
+g_simple_action_observer_init_iface (GActionObserverInterface *iface)
+{
+  iface->action_added = g_simple_action_observer_action_added;
+  iface->action_enabled_changed = g_simple_action_observer_action_enabled_changed;
+  iface->action_state_changed = g_simple_action_observer_action_state_changed;
+  iface->action_removed = g_simple_action_observer_action_removed;
+}
+
+static void
+g_simple_action_observer_class_init (GObjectClass *class)
+{
+  class->get_property = g_simple_action_observer_get_property;
+  class->finalize = g_simple_action_observer_finalize;
+
+  g_simple_action_observer_pspecs[PROP_ACTIVE] = g_param_spec_boolean ("active", "active", "active", FALSE,
+                                                                       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_simple_action_observer_pspecs[PROP_ENABLED] = g_param_spec_boolean ("enabled", "enabled", "enabled", FALSE,
+                                                                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_properties (class, N_PROPS, g_simple_action_observer_pspecs);
+}
+
+GSimpleActionObserver *
+g_simple_action_observer_new (GActionObservable *observable,
+                              const gchar       *action_name,
+                              GVariant          *target)
+{
+  GSimpleActionObserver *observer;
+  const GVariantType *type;
+  gboolean enabled;
+  GVariant *state;
+
+  observer = g_object_new (G_TYPE_SIMPLE_ACTION_OBSERVER, NULL);
+  observer->action_group = g_object_ref (observable);
+  observer->action_name = g_strdup (action_name);
+  if (target)
+    observer->target = g_variant_ref_sink (target);
+
+  g_action_observable_register_observer (observable, action_name, G_ACTION_OBSERVER (observer));
+
+  if (g_action_group_query_action (observer->action_group, action_name, &enabled, &type, NULL, NULL, &state))
+    {
+      g_simple_action_observer_action_added (G_ACTION_OBSERVER (observer), observable,
+                                             action_name, type, enabled, state);
+      if (state)
+        g_variant_unref (state);
+    }
+
+  return observer;
+}
+
+void
+g_simple_action_observer_activate (GSimpleActionObserver *observer)
+{
+  g_return_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer));
+
+  if (observer->can_activate && !observer->reporting)
+    g_action_group_activate_action (G_ACTION_GROUP (observer->action_group),
+                                    observer->action_name, observer->target);
+}
+
+gboolean
+g_simple_action_observer_get_active (GSimpleActionObserver *observer)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer), FALSE);
+
+  return observer->active;
+}
+
+gboolean
+g_simple_action_observer_get_enabled (GSimpleActionObserver *observer)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ACTION_OBSERVER (observer), FALSE);
+
+  return observer->enabled;
+}
diff --git a/gtk/gsimpleactionobserver.h b/gtk/gsimpleactionobserver.h
new file mode 100644
index 0000000..c3340f7
--- /dev/null
+++ b/gtk/gsimpleactionobserver.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright  2012 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Authors: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __G_SIMPLE_ACTION_OBSERVER_H__
+#define __G_SIMPLE_ACTION_OBSERVER_H__
+
+#include "gactionobserver.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SIMPLE_ACTION_OBSERVER                       (g_simple_action_observer_get_type ())
+#define G_SIMPLE_ACTION_OBSERVER(inst)                      (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             G_TYPE_SIMPLE_ACTION_OBSERVER,                          \
+                                                             GSimpleActionObserver))
+#define G_IS_SIMPLE_ACTION_OBSERVER(inst)                   (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             G_TYPE_SIMPLE_ACTION_OBSERVER))
+
+typedef struct _GSimpleActionObserver                       GSimpleActionObserver;
+
+G_GNUC_INTERNAL
+GType                   g_simple_action_observer_get_type               (void);
+G_GNUC_INTERNAL
+GSimpleActionObserver * g_simple_action_observer_new                    (GActionObservable     *observable,
+                                                                         const gchar           *action_name,
+                                                                         GVariant              *target);
+G_GNUC_INTERNAL
+void                    g_simple_action_observer_activate               (GSimpleActionObserver *observer);
+G_GNUC_INTERNAL
+gboolean                g_simple_action_observer_get_active             (GSimpleActionObserver *observer);
+G_GNUC_INTERNAL
+gboolean                g_simple_action_observer_get_enabled            (GSimpleActionObserver *observer);
+
+G_END_DECLS
+
+#endif /* __G_SIMPLE_ACTION_OBSERVER_H__ */
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 0668636..88823e2 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -36,6 +36,7 @@
 #include <gtk/gtkaccelmap.h>
 #include <gtk/gtkaccessible.h>
 #include <gtk/gtkaction.h>
+#include <gtk/gtkactionable.h>
 #include <gtk/gtkactiongroup.h>
 #include <gtk/gtkactivatable.h>
 #include <gtk/gtkadjustment.h>
diff --git a/gtk/gtkactionable.c b/gtk/gtkactionable.c
new file mode 100644
index 0000000..f78474e
--- /dev/null
+++ b/gtk/gtkactionable.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright  2012 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Authors: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gtkactionable.h"
+
+#include "gtkwidget.h"
+#include "gtkintl.h"
+
+G_DEFINE_INTERFACE (GtkActionable, gtk_actionable, GTK_TYPE_WIDGET)
+
+static void
+gtk_actionable_default_init (GtkActionableInterface *iface)
+{
+  g_object_interface_install_property (iface,
+    g_param_spec_string ("action-name", P_("action name"),
+                         P_("The name of the associated action, like 'app.quit'"),
+                         NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_interface_install_property (iface,
+    g_param_spec_variant ("action-target", P_("action target value"),
+                          P_("The parameter for action invocations"),
+                          G_VARIANT_TYPE_ANY, NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+const gchar *
+gtk_actionable_get_action_name (GtkActionable *actionable)
+{
+  g_return_val_if_fail (GTK_IS_ACTIONABLE (actionable), NULL);
+
+  return GTK_ACTIONABLE_GET_IFACE (actionable)
+    ->get_action_name (actionable);
+}
+
+void
+gtk_actionable_set_action_name (GtkActionable *actionable,
+                                const gchar   *action_name)
+{
+  g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
+
+  GTK_ACTIONABLE_GET_IFACE (actionable)
+    ->set_action_name (actionable, action_name);
+}
+
+GVariant *
+gtk_actionable_get_action_target_value (GtkActionable *actionable)
+{
+  g_return_val_if_fail (GTK_IS_ACTIONABLE (actionable), NULL);
+
+  return GTK_ACTIONABLE_GET_IFACE (actionable)
+    ->get_action_target_value (actionable);
+}
+
+void
+gtk_actionable_set_action_target_value (GtkActionable *actionable,
+                                        GVariant      *target_value)
+{
+  g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
+
+  GTK_ACTIONABLE_GET_IFACE (actionable)
+    ->set_action_target_value (actionable, target_value);
+}
+
+void
+gtk_actionable_set_action_target (GtkActionable *actionable,
+                                  const gchar   *format_string,
+                                  ...)
+{
+  va_list ap;
+
+  va_start (ap, format_string);
+  gtk_actionable_set_action_target_value (actionable, g_variant_new_va (format_string, NULL, &ap));
+  va_end (ap);
+}
+
+void
+gtk_actionable_set_detailed_action_name (GtkActionable *actionable,
+                                         const gchar   *detailed_action_name)
+{
+  gchar **parts;
+
+  g_return_if_fail (GTK_IS_ACTIONABLE (actionable));
+
+  if (detailed_action_name == NULL)
+    {
+      gtk_actionable_set_action_name (actionable, NULL);
+      gtk_actionable_set_action_target_value (actionable, NULL);
+      return;
+    }
+
+  parts = g_strsplit (detailed_action_name, "::", 2);
+  gtk_actionable_set_action_name (actionable, parts[0]);
+  if (parts[0] && parts[1])
+    gtk_actionable_set_action_target (actionable, "s", parts[1]);
+  else
+    gtk_actionable_set_action_target_value (actionable, NULL);
+  g_strfreev (parts);
+}
diff --git a/gtk/gtkactionable.h b/gtk/gtkactionable.h
new file mode 100644
index 0000000..bef9bde
--- /dev/null
+++ b/gtk/gtkactionable.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright  2012 Canonical Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * licence or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Authors: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __GTK_ACTIONABLE_H__
+#define __GTK_ACTIONABLE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_ACTIONABLE                                 (gtk_actionable_get_type ())
+#define GTK_ACTIONABLE(inst)                                (G_TYPE_CHECK_INSTANCE_CAST ((inst),                     \
+                                                             GTK_TYPE_ACTIONABLE, GtkActionable))
+#define GTK_IS_ACTIONABLE(inst)                             (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                     \
+                                                             GTK_TYPE_ACTIONABLE))
+#define GTK_ACTIONABLE_GET_IFACE(inst)                      (G_TYPE_INSTANCE_GET_INTERFACE ((inst),                  \
+                                                             GTK_TYPE_ACTIONABLE, GtkActionableInterface))
+
+typedef struct _GtkActionableInterface                      GtkActionableInterface;
+typedef struct _GtkActionable                               GtkActionable;
+
+struct _GtkActionableInterface
+{
+  GTypeInterface g_iface;
+
+  const gchar * (* get_action_name)             (GtkActionable *actionable);
+  void          (* set_action_name)             (GtkActionable *actionable,
+                                                 const gchar   *action_name);
+  GVariant *    (* get_action_target_value)     (GtkActionable *actionable);
+  void          (* set_action_target_value)     (GtkActionable *actionable,
+                                                 GVariant      *action_target_value);
+};
+
+GType                   gtk_actionable_get_type                         (void) G_GNUC_CONST;
+
+const gchar *           gtk_actionable_get_action_name                  (GtkActionable *actionable);
+void                    gtk_actionable_set_action_name                  (GtkActionable *actionable,
+                                                                         const gchar   *action_name);
+
+GVariant *              gtk_actionable_get_action_target_value          (GtkActionable *actionable);
+void                    gtk_actionable_set_action_target_value          (GtkActionable *actionable,
+                                                                         GVariant      *target_value);
+
+void                    gtk_actionable_set_action_target                (GtkActionable *actionable,
+                                                                         const gchar   *format_string,
+                                                                         ...);
+
+void                    gtk_actionable_set_detailed_action_name         (GtkActionable *actionable,
+                                                                         const gchar   *detailed_action_name);
+
+G_END_DECLS
+
+#endif /* __GTK_ACTIONABLE_H__ */
diff --git a/gtk/gtkapplicationprivate.h b/gtk/gtkapplicationprivate.h
index 9693f78..4ff3b2b 100644
--- a/gtk/gtkapplicationprivate.h
+++ b/gtk/gtkapplicationprivate.h
@@ -23,6 +23,7 @@
 #ifndef __GTK_APPLICATION_PRIVATE_H__
 #define __GTK_APPLICATION_PRIVATE_H__
 
+#include "gsimpleactionobserver.h"
 #include "gtkapplicationwindow.h"
 
 G_GNUC_INTERNAL
@@ -33,4 +34,9 @@ gboolean                gtk_application_window_publish                  (GtkAppl
 G_GNUC_INTERNAL
 void                    gtk_application_window_unpublish                (GtkApplicationWindow *window);
 
+G_GNUC_INTERNAL
+GSimpleActionObserver * gtk_application_window_get_observer             (GtkApplicationWindow *window,
+                                                                         const gchar          *action_name,
+                                                                         GVariant             *target);
+
 #endif /* __GTK_APPLICATION_PRIVATE_H__ */
diff --git a/gtk/gtkapplicationwindow.c b/gtk/gtkapplicationwindow.c
index 5f1d57c..341e577 100644
--- a/gtk/gtkapplicationwindow.c
+++ b/gtk/gtkapplicationwindow.c
@@ -182,6 +182,7 @@ struct _GtkApplicationWindowPrivate
 {
   GSimpleActionGroup *actions;
   GActionObservable *muxer;
+  gboolean muxer_initialised;
   GtkWidget *menubar;
   GtkAccelGroup *accels;
   GSList *accel_closures;
@@ -671,14 +672,11 @@ gtk_application_window_real_realize (GtkWidget *widget)
   g_signal_connect (settings, "notify::gtk-shell-shows-menubar",
                     G_CALLBACK (gtk_application_window_shell_shows_menubar_changed), window);
 
-  if (window->priv->muxer == NULL)
+  if (!window->priv->muxer_initialised)
     {
-      GActionMuxer *muxer;
-
-      muxer = g_action_muxer_new ();
-      g_action_muxer_insert (muxer, "app", G_ACTION_GROUP (application));
-      g_action_muxer_insert (muxer, "win", G_ACTION_GROUP (window));
-      window->priv->muxer = G_ACTION_OBSERVABLE (muxer);
+      g_action_muxer_insert (G_ACTION_MUXER (window->priv->muxer), "app", G_ACTION_GROUP (application));
+      g_action_muxer_insert (G_ACTION_MUXER (window->priv->muxer), "win", G_ACTION_GROUP (window));
+      window->priv->muxer_initialised = TRUE;
     }
 
   gtk_application_window_update_shell_shows_app_menu (window, settings);
@@ -875,6 +873,8 @@ gtk_application_window_init (GtkApplicationWindow *window)
                             G_CALLBACK (g_action_group_action_state_changed), window);
   g_signal_connect_swapped (window->priv->actions, "action-removed",
                             G_CALLBACK (g_action_group_action_removed), window);
+
+  window->priv->muxer = G_ACTION_OBSERVABLE (g_action_muxer_new ());
 }
 
 static void
@@ -982,3 +982,13 @@ gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
       g_object_notify_by_pspec (G_OBJECT (window), gtk_application_window_properties[PROP_SHOW_MENUBAR]);
     }
 }
+
+GSimpleActionObserver *
+gtk_application_window_get_observer (GtkApplicationWindow *window,
+                                     const gchar          *action_name,
+                                     GVariant             *target)
+{
+  g_return_val_if_fail (GTK_IS_APPLICATION_WINDOW (window), NULL);
+
+  return g_simple_action_observer_new (window->priv->muxer, action_name, target);
+}
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 3b3b5b2..f7a9084 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -58,7 +58,8 @@
 #include "gtkprivate.h"
 #include "gtkintl.h"
 #include "a11y/gtkbuttonaccessible.h"
-
+#include "gtkapplicationprivate.h"
+#include "gtkactionable.h"
 
 static const GtkBorder default_default_border = { 1, 1, 1, 1 };
 static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
@@ -90,6 +91,8 @@ enum {
   PROP_XALIGN,
   PROP_YALIGN,
   PROP_IMAGE_POSITION,
+  PROP_ACTION_NAME,
+  PROP_ACTION_TARGET,
 
   /* activatable properties */
   PROP_ACTIVATABLE_RELATED_ACTION,
@@ -147,8 +150,11 @@ static void gtk_button_state_changed   (GtkWidget             *widget,
 					GtkStateType           previous_state);
 static void gtk_button_grab_notify     (GtkWidget             *widget,
 					gboolean               was_grabbed);
+static void gtk_button_hierarchy_changed (GtkWidget           *widget,
+                                          GtkWidget           *previous_toplevel);
 
 
+static void gtk_button_actionable_iface_init     (GtkActionableInterface *iface);
 static void gtk_button_activatable_interface_init(GtkActivatableIface  *iface);
 static void gtk_button_update                    (GtkActivatable       *activatable,
 				                  GtkAction            *action,
@@ -170,6 +176,7 @@ static void gtk_button_get_preferred_height      (GtkWidget           *widget,
 static guint button_signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE_WITH_CODE (GtkButton, gtk_button, GTK_TYPE_BIN,
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_button_actionable_iface_init)
 			 G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
 						gtk_button_activatable_interface_init))
 
@@ -208,6 +215,7 @@ gtk_button_class_init (GtkButtonClass *klass)
   widget_class->leave_notify_event = gtk_button_leave_notify;
   widget_class->state_changed = gtk_button_state_changed;
   widget_class->grab_notify = gtk_button_grab_notify;
+  widget_class->hierarchy_changed = gtk_button_hierarchy_changed;
 
   container_class->child_type = gtk_button_child_type;
   container_class->add = gtk_button_add;
@@ -330,6 +338,9 @@ gtk_button_class_init (GtkButtonClass *klass)
                                                       GTK_POS_LEFT,
                                                       GTK_PARAM_READWRITE));
 
+  g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
+  g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");
+
   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
 
@@ -584,6 +595,12 @@ gtk_button_destroy (GtkWidget *widget)
       priv->label_text = NULL;
     }
 
+  if (priv->action_name)
+    {
+      g_free (priv->action_name);
+      priv->action_name = NULL;
+    }
+
   GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget);
 }
 
@@ -666,6 +683,8 @@ gtk_button_dispose (GObject *object)
   GtkButton *button = GTK_BUTTON (object);
   GtkButtonPrivate *priv = button->priv;
 
+  g_clear_object (&priv->action_observer);
+
   if (priv->action)
     {
       gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL);
@@ -675,6 +694,83 @@ gtk_button_dispose (GObject *object)
 }
 
 static void
+gtk_button_update_action_observer (GtkButton *button)
+{
+  GtkWidget *window;
+
+  g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
+
+  /* we are the only owner so this will clear all the signals */
+  g_clear_object (&button->priv->action_observer);
+
+  window = gtk_widget_get_toplevel (GTK_WIDGET (button));
+
+  if (GTK_IS_APPLICATION_WINDOW (window) && button->priv->action_name)
+    {
+      GSimpleActionObserver *observer;
+
+      observer = gtk_application_window_get_observer (GTK_APPLICATION_WINDOW (window),
+                                                      button->priv->action_name,
+                                                      button->priv->action_target);
+
+      _gtk_button_set_depressed (button, g_simple_action_observer_get_active (observer));
+
+      if (g_object_class_find_property (G_OBJECT_GET_CLASS (button), "active"))
+        g_object_bind_property (observer, "active", button, "active", G_BINDING_SYNC_CREATE);
+      g_object_bind_property (observer, "enabled", button, "sensitive", G_BINDING_SYNC_CREATE);
+
+      button->priv->action_observer = observer;
+
+      g_signal_connect_after (button, "clicked", G_CALLBACK (gtk_real_button_clicked), NULL);
+    }
+}
+
+static void
+gtk_button_set_action_name (GtkActionable *actionable,
+                            const gchar   *action_name)
+{
+  GtkButton *button = GTK_BUTTON (actionable);
+
+  g_return_if_fail (GTK_IS_BUTTON (button));
+
+  if (g_strcmp0 (action_name, button->priv->action_name) != 0)
+    {
+      g_free (button->priv->action_name);
+      button->priv->action_name = g_strdup (action_name);
+
+      gtk_button_update_action_observer (button);
+
+      g_object_notify (G_OBJECT (button), "action-name");
+    }
+}
+
+static void
+gtk_button_set_action_target_value (GtkActionable *actionable,
+                                    GVariant      *action_target)
+{
+  GtkButton *button = GTK_BUTTON (actionable);
+
+  g_return_if_fail (GTK_IS_BUTTON (button));
+
+  if (action_target != button->priv->action_target &&
+      (!action_target || !button->priv->action_target ||
+       !g_variant_equal (action_target, button->priv->action_target)))
+    {
+      if (button->priv->action_target)
+        g_variant_unref (button->priv->action_target);
+
+      button->priv->action_target = NULL;
+
+      if (action_target)
+        button->priv->action_target = g_variant_ref_sink (action_target);
+
+      gtk_button_update_action_observer (button);
+
+      g_object_notify (G_OBJECT (button), "action-target");
+    }
+}
+
+static void
 gtk_button_set_property (GObject         *object,
                          guint            prop_id,
                          const GValue    *value,
@@ -718,6 +814,12 @@ gtk_button_set_property (GObject         *object,
     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
       gtk_button_set_use_action_appearance (button, g_value_get_boolean (value));
       break;
+    case PROP_ACTION_NAME:
+      gtk_button_set_action_name (GTK_ACTIONABLE (button), g_value_get_string (value));
+      break;
+    case PROP_ACTION_TARGET:
+      gtk_button_set_action_target_value (GTK_ACTIONABLE (button), g_value_get_variant (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -768,12 +870,43 @@ gtk_button_get_property (GObject         *object,
     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
       g_value_set_boolean (value, priv->use_action_appearance);
       break;
+    case PROP_ACTION_NAME:
+      g_value_set_string (value, priv->action_name);
+      break;
+    case PROP_ACTION_TARGET:
+      g_value_set_variant (value, priv->action_target);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
     }
 }
 
+static const gchar *
+gtk_button_get_action_name (GtkActionable *actionable)
+{
+  GtkButton *button = GTK_BUTTON (actionable);
+
+  return button->priv->action_name;
+}
+
+static GVariant *
+gtk_button_get_action_target_value (GtkActionable *actionable)
+{
+  GtkButton *button = GTK_BUTTON (actionable);
+
+  return button->priv->action_target;
+}
+
+static void
+gtk_button_actionable_iface_init (GtkActionableInterface *iface)
+{
+  iface->get_action_name = gtk_button_get_action_name;
+  iface->set_action_name = gtk_button_set_action_name;
+  iface->get_action_target_value = gtk_button_get_action_target_value;
+  iface->set_action_target_value = gtk_button_set_action_target_value;
+}
+
 static void 
 gtk_button_activatable_interface_init (GtkActivatableIface  *iface)
 {
@@ -1822,6 +1955,9 @@ gtk_real_button_clicked (GtkButton *button)
 {
   GtkButtonPrivate *priv = button->priv;
 
+  if (priv->action_observer)
+    g_simple_action_observer_activate (priv->action_observer);
+
   if (priv->action)
     gtk_action_activate (priv->action);
 }
@@ -2416,6 +2552,28 @@ gtk_button_grab_notify (GtkWidget *widget,
     }
 }
 
+static void
+gtk_button_hierarchy_changed (GtkWidget *widget,
+                              GtkWidget *previous_toplevel)
+{
+  GtkButton *button = GTK_BUTTON (widget);
+  GtkWidgetClass *parent_class;
+
+  parent_class = GTK_WIDGET_CLASS (gtk_button_parent_class);
+  if (parent_class->hierarchy_changed)
+    parent_class->hierarchy_changed (widget, previous_toplevel);
+
+  if (button->priv->action_name)
+    {
+      GtkWidget *toplevel;
+
+      toplevel = gtk_widget_get_toplevel (widget);
+
+      if (toplevel != previous_toplevel)
+        gtk_button_update_action_observer (button);
+    }
+}
+
 /**
  * gtk_button_set_image:
  * @button: a #GtkButton
@@ -2525,7 +2683,6 @@ gtk_button_get_image_position (GtkButton *button)
   return button->priv->image_position;
 }
 
-
 /**
  * gtk_button_get_event_window:
  * @button: a #GtkButton
diff --git a/gtk/gtkbuttonprivate.h b/gtk/gtkbuttonprivate.h
index 5db1bc1..232973c 100644
--- a/gtk/gtkbuttonprivate.h
+++ b/gtk/gtkbuttonprivate.h
@@ -19,6 +19,7 @@
 #ifndef __GTK_BUTTON_PRIVATE_H__
 #define __GTK_BUTTON_PRIVATE_H__
 
+#include "gsimpleactionobserver.h"
 #include "gtkaction.h"
 
 G_BEGIN_DECLS
@@ -29,6 +30,10 @@ struct _GtkButtonPrivate
   GtkAction             *action;
   GtkWidget             *image;
 
+  gchar                 *action_name;
+  GVariant              *action_target;
+  GSimpleActionObserver *action_observer;
+
   GdkDevice             *grab_keyboard;
   GdkWindow             *event_window;
 
diff --git a/gtk/gtktoolbutton.c b/gtk/gtktoolbutton.c
index 934ca61..ea67c34 100644
--- a/gtk/gtktoolbutton.c
+++ b/gtk/gtktoolbutton.c
@@ -32,6 +32,7 @@
 #include "gtkintl.h"
 #include "gtktoolbar.h"
 #include "gtkactivatable.h"
+#include "gtkactionable.h"
 #include "gtkprivate.h"
 
 #include <string.h>
@@ -81,7 +82,9 @@ enum {
   PROP_LABEL_WIDGET,
   PROP_STOCK_ID,
   PROP_ICON_NAME,
-  PROP_ICON_WIDGET
+  PROP_ICON_WIDGET,
+  PROP_ACTION_NAME,
+  PROP_ACTION_TARGET
 };
 
 static void gtk_tool_button_init          (GtkToolButton      *button,
@@ -107,6 +110,7 @@ static void gtk_tool_button_style_updated  (GtkWidget          *widget);
 
 static void gtk_tool_button_construct_contents (GtkToolItem *tool_item);
 
+static void gtk_tool_button_actionable_iface_init      (GtkActionableInterface *iface);
 static void gtk_tool_button_activatable_interface_init (GtkActivatableIface  *iface);
 static void gtk_tool_button_update                     (GtkActivatable       *activatable,
 							GtkAction            *action,
@@ -135,7 +139,6 @@ static GObjectClass        *parent_class = NULL;
 static GtkActivatableIface *parent_activatable_iface;
 static guint                toolbutton_signals[LAST_SIGNAL] = { 0 };
 
-
 GType
 gtk_tool_button_get_type (void)
 {
@@ -143,6 +146,12 @@ gtk_tool_button_get_type (void)
   
   if (!type)
     {
+      const GInterfaceInfo actionable_info =
+      {
+        (GInterfaceInitFunc) gtk_tool_button_actionable_iface_init,
+        (GInterfaceFinalizeFunc) NULL,
+        NULL
+      };
       const GInterfaceInfo activatable_info =
       {
         (GInterfaceInitFunc) gtk_tool_button_activatable_interface_init,
@@ -158,6 +167,7 @@ gtk_tool_button_get_type (void)
 					    (GInstanceInitFunc) gtk_tool_button_init,
 					    0);
 
+      g_type_add_interface_static (type, GTK_TYPE_ACTIONABLE, &actionable_info);
       g_type_add_interface_static (type, GTK_TYPE_ACTIVATABLE,
                                    &activatable_info);
     }
@@ -278,6 +288,9 @@ gtk_tool_button_class_init (GtkToolButtonClass *klass)
 							GTK_TYPE_WIDGET,
 							GTK_PARAM_READWRITE));
 
+  g_object_class_override_property (object_class, PROP_ACTION_NAME, "action-name");
+  g_object_class_override_property (object_class, PROP_ACTION_TARGET, "action-target");
+
   /**
    * GtkButton:icon-spacing:
    * 
@@ -612,6 +625,12 @@ gtk_tool_button_set_property (GObject         *object,
     case PROP_ICON_WIDGET:
       gtk_tool_button_set_icon_widget (button, g_value_get_object (value));
       break;
+    case PROP_ACTION_NAME:
+      g_object_set_property (G_OBJECT (button->priv->button), "action-name", value);
+      break;
+    case PROP_ACTION_TARGET:
+      g_object_set_property (G_OBJECT (button->priv->button), "action-target", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -660,12 +679,61 @@ gtk_tool_button_get_property (GObject         *object,
     case PROP_ICON_WIDGET:
       g_value_set_object (value, button->priv->icon_widget);
       break;
+    case PROP_ACTION_NAME:
+      g_object_get_property (G_OBJECT (button->priv->button), "action-name", value);
+      break;
+    case PROP_ACTION_TARGET:
+      g_object_get_property (G_OBJECT (button->priv->button), "action-target", value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
     }
 }
 
+static const gchar *
+gtk_tool_button_get_action_name (GtkActionable *actionable)
+{
+  GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
+
+  return gtk_actionable_get_action_name (GTK_ACTIONABLE (button->priv->button));
+}
+
+static void
+gtk_tool_button_set_action_name (GtkActionable *actionable,
+                                 const gchar   *action_name)
+{
+  GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
+
+  gtk_actionable_set_action_name (GTK_ACTIONABLE (button->priv->button), action_name);
+}
+
+static GVariant *
+gtk_tool_button_get_action_target_value (GtkActionable *actionable)
+{
+  GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
+
+  return gtk_actionable_get_action_target_value (GTK_ACTIONABLE (button->priv->button));
+}
+
+static void
+gtk_tool_button_set_action_target_value (GtkActionable *actionable,
+                                         GVariant      *action_target)
+{
+  GtkToolButton *button = GTK_TOOL_BUTTON (actionable);
+
+  gtk_actionable_set_action_target_value (GTK_ACTIONABLE (button->priv->button), action_target);
+}
+
+static void
+gtk_tool_button_actionable_iface_init (GtkActionableInterface *iface)
+{
+  iface->get_action_name = gtk_tool_button_get_action_name;
+  iface->set_action_name = gtk_tool_button_set_action_name;
+  iface->get_action_target_value = gtk_tool_button_get_action_target_value;
+  iface->set_action_target_value = gtk_tool_button_set_action_target_value;
+}
+
 static void
 gtk_tool_button_finalize (GObject *object)
 {



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