[gtk+/wip/action_helper: 1/2] First pass at action rework



commit c3a4392f9bfaede0e8615deffc57c962de2fc77e
Author: Ryan Lortie <desrt desrt ca>
Date:   Sat Jun 9 01:00:34 2012 -0400

    First pass at action rework
    
    This introduces the ability for widgets to define their own action
    context (ie: introduce new named action groups other than "app" and
    "win" that will be visible to their child widgets).
    
    We introduce GtkActionHelper to manage the common tasks associated with
    implementing GtkActionable.
    
    GAction{Muxer,Observerable,Observer} and GSimpleActionObserver are
    dropped.
    
    Accels are not yet working and Mac OS support is almost certainly broken
    at this point.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=677220

 gtk/Makefile.am             |   51 +---
 gtk/gactionmuxer.c          |  505 -----------------------------
 gtk/gactionmuxer.h          |   51 ---
 gtk/gactionobservable.c     |   78 -----
 gtk/gactionobservable.h     |   62 ----
 gtk/gactionobserver.c       |  159 ----------
 gtk/gactionobserver.h       |   88 -----
 gtk/gsimpleactionobserver.c |  286 -----------------
 gtk/gsimpleactionobserver.h |   51 ---
 gtk/gtkactionhelper.c       |  736 +++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkactionhelper.h       |   67 ++++
 gtk/gtkapplication.c        |    1 -
 gtk/gtkapplicationprivate.h |    9 -
 gtk/gtkapplicationwindow.c  |   44 +--
 gtk/gtkbutton.c             |  113 +------
 gtk/gtkbuttonprivate.h      |    7 +-
 gtk/gtkmenu.c               |   43 +++
 gtk/gtkmenuitem.c           |   78 +++++-
 gtk/gtkmenuitemprivate.h    |    7 +-
 gtk/gtkmenuprivate.h        |    1 +
 gtk/gtkmodelmenu.c          |   71 +----
 gtk/gtkmodelmenu.h          |    3 -
 gtk/gtkmodelmenuitem.c      |  235 ++++----------
 gtk/gtkmodelmenuitem.h      |    2 -
 gtk/gtkswitch.c             |  107 +------
 gtk/gtkwidget.c             |  125 ++++++++
 gtk/gtkwidget.h             |   16 +-
 gtk/gtkwindow.c             |   16 +
 28 files changed, 1202 insertions(+), 1810 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 07316ba..82cf10c 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -399,10 +399,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	\
+	gtkactionhelper.h	\
 	gtkapplicationprivate.h	\
 	gtkaccelgroupprivate.h	\
 	gtkaccelmapprivate.h	\
@@ -568,10 +565,7 @@ deprecated_c_sources = 			\
 
 gtk_base_c_sources = 		\
 	$(deprecated_c_sources)	\
-	gactionmuxer.c		\
-	gsimpleactionobserver.c	\
-	gactionobserver.c	\
-	gactionobservable.c	\
+	gtkactionhelper.c	\
 	gtkactionable.c		\
 	gtkquery.c		\
 	gtksearchentry.c	\
@@ -1226,47 +1220,6 @@ LDADDS =								\
 	$(top_builddir)/gdk/libgdk-3.la				\
 	$(GTK_DEP_LIBS)
 
-if HAVE_INTROSPECTION
-introspection_files = \
-    $(filter-out %private.h gtktextdisplay.h gtktextlayout.h, $(gtkinclude_HEADERS) $(deprecatedinclude_HEADERS)) \
-    $(gtk_base_c_sources) \
-    gtkprintoperation-unix.c \
-    gtktypebuiltins.h \
-    gtktypebuiltins.c
-
-if USE_X11
-introspection_files += \
-    gtksocket.c \
-    gtkplug.c
-endif
-
-Gtk-3.0.gir: $(INTROSPECTION_SCANNER) libgtk-3.la $(top_builddir)/gdk/Gdk-3.0.gir Makefile
-Gtk_3_0_gir_SCANNERFLAGS = 			\
-	--add-include-path=$(top_builddir)/gdk	\
-	--include-uninstalled=$(top_builddir)/gdk/Gdk-3.0.gir
-Gtk_3_0_gir_INCLUDES = Atk-1.0
-if USE_X11
-Gtk_3_0_gir_SCANNERFLAGS += --add-include-path=$(top_builddir)/gdk/x11
-Gtk_3_0_gir_INCLUDES += xlib-2.0
-endif
-Gtk_3_0_gir_SCANNERFLAGS += --c-include="gtk/gtkx.h"
-Gtk_3_0_gir_CFLAGS = \
-		$(AM_CPPFLAGS) \
-		-DGTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
-Gtk_3_0_gir_LIBS = libgtk-3.la $(top_builddir)/gdk/libgdk-3.la
-Gtk_3_0_gir_FILES = $(introspection_files)
-Gtk_3_0_gir_EXPORT_PACKAGES = gtk+-3.0
-INTROSPECTION_GIRS += Gtk-3.0.gir
-
-girdir = $(datadir)/gir-1.0
-gir_DATA = $(INTROSPECTION_GIRS)
-
-typelibsdir = $(libdir)/girepository-1.0
-typelibs_DATA = $(INTROSPECTION_GIRS:.gir=.typelib)
-
-CLEANFILES += $(gir_DATA) $(typelibs_DATA)
-endif
-
 #
 # Installed tools
 #
diff --git a/gtk/gtkactionhelper.c b/gtk/gtkactionhelper.c
new file mode 100644
index 0000000..7b47493
--- /dev/null
+++ b/gtk/gtkactionhelper.c
@@ -0,0 +1,736 @@
+/*
+ * Copyright  2012 Canonical Limited
+ *
+ * This library 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "gtkactionhelper.h"
+
+#include "gtkwidget.h"
+
+#include <string.h>
+
+typedef struct
+{
+  GActionGroup *group;
+
+  GHashTable *watchers;
+} GtkActionHelperGroup;
+
+/* These functions are now GtkActionHelperGroup reports changes to the
+ * individual GtkActionHelper instances.
+ */
+static void             gtk_action_helper_action_added                  (GtkActionHelper    *helper,
+                                                                         gboolean            enabled,
+                                                                         const GVariantType *parameter_type,
+                                                                         GVariant           *state,
+                                                                         gboolean            should_emit_signals);
+
+static void             gtk_action_helper_action_removed                (GtkActionHelper    *helper);
+
+static void             gtk_action_helper_action_enabled_changed        (GtkActionHelper    *helper,
+                                                                         gboolean            enabled);
+
+static void             gtk_action_helper_action_state_changed          (GtkActionHelper    *helper,
+                                                                         GVariant           *new_state);
+
+/*< private >
+ * GtkActionHelperGroup:
+ *
+ * This helper struct is created one per GActionGroup in order to
+ * monitor the action group for changes and notify interested
+ * GtkActionHelper instances in an efficient way.
+ *
+ * This exists because GSignal is insufficient in these two ways:
+ *
+ *  1) Signal detail involves quarking the detail string and we don't
+ *     want to quark arbitrary action names (which may be coming from
+ *     out of process).
+ *
+ *  2) Even if that was okay, detailed signal emission is linear in the
+ *     total number of connected handlers (ie: each connected handler is
+ *     manually checked to see if the detail matches).
+ *
+ * If we fix GSignal to have a non-quark-based hash table for detailed
+ * emissions then we can lose this struct.
+ *
+ * The reference counting situation here is a little bit tricky.  The
+ * #GActionGroup "owns" this struct (via the #GDestroyNotify on the
+ * qdata).  At the same time, we want to keep the #GActionGroup alive
+ * for as long as we have any watchers.  We do this by taking a
+ * reference on the #GActionGroup for each watch.
+ */
+
+static void
+gtk_action_helper_group_action_added (GActionGroup *action_group,
+                                      const gchar  *action_name,
+                                      gpointer      user_data)
+{
+  GtkActionHelperGroup *group_helper = user_data;
+  GSList *watchers;
+
+  g_assert (action_group == group_helper->group);
+
+  watchers = g_hash_table_lookup (group_helper->watchers, action_name);
+
+  if (watchers)
+    {
+      const GVariantType *parameter_type;
+      gboolean enabled;
+      GVariant *state;
+      GSList *node;
+
+      if (!g_action_group_query_action (action_group, action_name, &enabled, &parameter_type, NULL, NULL, &state))
+        g_assert_not_reached ();
+
+      for (node = watchers; node; node = node->next)
+        gtk_action_helper_action_added (node->data, enabled, parameter_type, state, TRUE);
+
+      if (state)
+        g_variant_unref (state);
+    }
+}
+
+static void
+gtk_action_helper_group_action_removed (GActionGroup *action,
+                                        const gchar  *action_name,
+                                        gpointer      user_data)
+{
+  GtkActionHelperGroup *group_helper = user_data;
+  GSList *node;
+
+  for (node = g_hash_table_lookup (group_helper->watchers, action_name); node; node = node->next)
+    gtk_action_helper_action_removed (node->data);
+}
+
+static void
+gtk_action_helper_group_action_enabled_changed (GActionGroup *group,
+                                                const gchar  *action_name,
+                                                gboolean      enabled,
+                                                gpointer      user_data)
+{
+  GtkActionHelperGroup *group_helper = user_data;
+  GSList *node;
+
+  for (node = g_hash_table_lookup (group_helper->watchers, action_name); node; node = node->next)
+    gtk_action_helper_action_enabled_changed (node->data, enabled);
+}
+
+static void
+gtk_action_helper_group_action_state_changed (GActionGroup *group,
+                                              const gchar  *action_name,
+                                              GVariant     *new_state,
+                                              gpointer      user_data)
+{
+  GtkActionHelperGroup *group_helper = user_data;
+  GSList *node;
+
+  for (node = g_hash_table_lookup (group_helper->watchers, action_name); node; node = node->next)
+    gtk_action_helper_action_state_changed (node->data, new_state);
+}
+
+static void
+gtk_action_helper_group_free (gpointer data)
+{
+  GtkActionHelperGroup *group_helper = data;
+
+  g_signal_handlers_disconnect_by_data (group_helper->group, group_helper);
+  g_assert_cmpint (g_hash_table_size (group_helper->watchers), ==, 0);
+  g_hash_table_unref (group_helper->watchers);
+
+  g_slice_free (GtkActionHelperGroup, group_helper);
+}
+
+/*< really quite private >
+ * gtk_action_helper_group_get:
+ * @group: a #GActionGroup
+ *
+ * Gets the #GtkActionHelperGroup for @group, creating it if it does not
+ * exist.
+ *
+ * The helper will be freed when @group is destroyed.
+ */
+static GtkActionHelperGroup *
+gtk_action_helper_group_get (GActionGroup *group)
+{
+  GtkActionHelperGroup *group_helper;
+  static GQuark group_helper_quark;
+
+  if (!group_helper_quark)
+    group_helper_quark = g_quark_from_static_string ("GtkActionHelper group data quark");
+
+  group_helper = g_object_get_qdata (G_OBJECT (group), group_helper_quark);
+
+  if (group_helper == NULL)
+    {
+      group_helper = g_slice_new (GtkActionHelperGroup);
+      group_helper->group = group;
+      group_helper->watchers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+      g_signal_connect (group, "action-added",
+                        G_CALLBACK (gtk_action_helper_group_action_added), group_helper);
+      g_signal_connect (group, "action-removed",
+                        G_CALLBACK (gtk_action_helper_group_action_removed), group_helper);
+      g_signal_connect (group, "action-enabled-changed",
+                        G_CALLBACK (gtk_action_helper_group_action_enabled_changed), group_helper);
+      g_signal_connect (group, "action-state-changed",
+                        G_CALLBACK (gtk_action_helper_group_action_state_changed), group_helper);
+
+      g_object_set_qdata_full (G_OBJECT (group), group_helper_quark, group_helper, gtk_action_helper_group_free);
+    }
+
+  return group_helper;
+}
+
+static void
+gtk_action_helper_group_watch (GtkActionHelperGroup *group_helper,
+                               const gchar          *action_name,
+                               GtkActionHelper      *helper)
+{
+  GSList *helpers;
+
+  helpers = g_hash_table_lookup (group_helper->watchers, action_name);
+  helpers = g_slist_prepend (helpers, helper);
+  g_hash_table_insert (group_helper->watchers, g_strdup (action_name), helpers);
+  g_object_ref (group_helper->group);
+}
+
+static void
+gtk_action_helper_group_unwatch (GtkActionHelperGroup *group_helper,
+                                 const gchar          *action_name,
+                                 GtkActionHelper      *helper)
+{
+  GSList *helpers;
+
+  helpers = g_hash_table_lookup (group_helper->watchers, action_name);
+  g_warn_if_fail (helpers != NULL);
+  helpers = g_slist_remove (helpers, helper);
+
+  if (helpers)
+    g_hash_table_insert (group_helper->watchers, g_strdup (action_name), helpers);
+  else
+    g_hash_table_remove (group_helper->watchers, action_name);
+
+  /* This may be our own death... */
+  g_object_unref (group_helper->group);
+}
+
+typedef GObjectClass GtkActionHelperClass;
+
+struct _GtkActionHelper
+{
+  GObject parent_instance;
+
+  GtkActionable *widget;
+
+  GtkActionHelperGroup *group;
+
+  gchar *full_action_name;    /* "app.quit" */
+  gchar *action_group_name;   /* "app" */
+  gchar *action_name;         /* "quit" */
+
+  GVariant *target;
+
+  GtkActionHelperRole role;
+  gboolean can_activate;
+  gboolean enabled;
+  gboolean active;
+
+  gint reporting;
+};
+
+enum
+{
+  PROP_0,
+  PROP_ENABLED,
+  PROP_ACTIVE,
+  PROP_ROLE,
+  N_PROPS
+};
+
+static GParamSpec *gtk_action_helper_pspecs[N_PROPS];
+
+G_DEFINE_TYPE (GtkActionHelper, gtk_action_helper, G_TYPE_OBJECT)
+
+static void
+gtk_action_helper_report_change (GtkActionHelper *helper,
+                                 guint            prop_id)
+{
+  helper->reporting++;
+
+  switch (prop_id)
+    {
+    case PROP_ENABLED:
+      gtk_widget_set_sensitive (GTK_WIDGET (helper->widget), helper->enabled);
+      break;
+
+    case PROP_ACTIVE:
+      {
+        GParamSpec *pspec;
+
+        pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (helper->widget), "active");
+
+        if (pspec && G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_BOOLEAN)
+          g_object_set (G_OBJECT (helper->widget), "active", helper->active, NULL);
+      }
+      break;
+
+    case PROP_ROLE:
+      {
+        GParamSpec *pspec;
+
+        pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (helper->widget), "action-role");
+
+        if (pspec && G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_UINT)
+          g_object_set (G_OBJECT (helper->widget), "action-role", helper->role, NULL);
+      }
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  g_object_notify_by_pspec (G_OBJECT (helper), gtk_action_helper_pspecs[prop_id]);
+  helper->reporting--;
+}
+
+static void
+gtk_action_helper_action_added (GtkActionHelper    *helper,
+                                gboolean            enabled,
+                                const GVariantType *parameter_type,
+                                GVariant           *state,
+                                gboolean            should_emit_signals)
+{
+  /* we can only activate if we have the correct type of parameter */
+  helper->can_activate = (helper->target == NULL && parameter_type == NULL) ||
+                          (helper->target != NULL && parameter_type != NULL &&
+                          g_variant_is_of_type (helper->target, parameter_type));
+
+  if (!helper->can_activate)
+    return;
+
+  helper->enabled = enabled;
+
+  if (helper->target != NULL && state != NULL)
+    {
+      helper->active = g_variant_equal (state, helper->target);
+      helper->role = GTK_ACTION_HELPER_ROLE_RADIO;
+    }
+
+  else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
+    {
+      helper->active = g_variant_get_boolean (state);
+      helper->role = GTK_ACTION_HELPER_ROLE_TOGGLE;
+    }
+
+  if (should_emit_signals)
+    {
+      if (helper->enabled)
+        gtk_action_helper_report_change (helper, PROP_ENABLED);
+
+      if (helper->active)
+        gtk_action_helper_report_change (helper, PROP_ACTIVE);
+
+      if (helper->role)
+        gtk_action_helper_report_change (helper, PROP_ROLE);
+    }
+}
+
+static void
+gtk_action_helper_action_removed (GtkActionHelper *helper)
+{
+  if (!helper->can_activate)
+    return;
+
+  helper->can_activate = FALSE;
+
+  if (helper->enabled)
+    {
+      helper->enabled = FALSE;
+      gtk_action_helper_report_change (helper, PROP_ENABLED);
+    }
+
+  if (helper->active)
+    {
+      helper->enabled = FALSE;
+      gtk_action_helper_report_change (helper, PROP_ACTIVE);
+    }
+
+  if (helper->role)
+    {
+      helper->role = GTK_ACTION_HELPER_ROLE_NORMAL;
+      gtk_action_helper_report_change (helper, PROP_ROLE);
+    }
+}
+
+static void
+gtk_action_helper_action_enabled_changed (GtkActionHelper *helper,
+                                          gboolean         enabled)
+{
+  if (!helper->can_activate)
+    return;
+
+  if (helper->enabled == enabled)
+    return;
+
+  helper->enabled = enabled;
+  gtk_action_helper_report_change (helper, PROP_ENABLED);
+}
+
+static void
+gtk_action_helper_action_state_changed (GtkActionHelper *helper,
+                                        GVariant        *new_state)
+{
+  gboolean was_active;
+
+  if (!helper->can_activate)
+    return;
+
+  was_active = helper->active;
+
+  if (helper->target)
+    helper->active = g_variant_equal (new_state, helper->target);
+
+  else if (g_variant_is_of_type (new_state, G_VARIANT_TYPE_BOOLEAN))
+    helper->active = g_variant_get_boolean (new_state);
+
+  else
+    helper->active = FALSE;
+
+  if (helper->active != was_active)
+    gtk_action_helper_report_change (helper, PROP_ACTIVE);
+}
+
+static void
+gtk_action_helper_actions_changed (GtkWidget   *widget,
+                                   const gchar *action_group_name,
+                                   gpointer     user_data)
+{
+  GtkActionHelper *helper = user_data;
+  gboolean was_enabled, was_active;
+  GtkActionHelperRole old_role;
+  GActionGroup *group;
+
+  if (action_group_name && !g_str_equal (action_group_name, helper->action_group_name))
+    return;
+
+  group = gtk_widget_get_action_group_by_name (GTK_WIDGET (helper->widget), helper->action_group_name);
+
+  /* We didn't have the group before and we still don't have it now. */
+  if (group == NULL && helper->group == NULL)
+    return;
+
+  /* We already had the group and we still have the same one. */
+  if (group != NULL && helper->group != NULL && helper->group->group == group)
+    return;
+
+  /* At this point we know that a change of some kind will happen.
+   *
+   * Start by recording the current state of our properties so we know
+   * what notify signals we will need to send.
+   */
+  was_enabled = helper->enabled;
+  was_active = helper->active;
+  old_role = helper->role;
+
+  /* Tear down the existing group, if we have one. */
+  if (helper->group)
+    {
+      gtk_action_helper_group_unwatch (helper->group, helper->action_name, helper);
+      helper->role = GTK_ACTION_HELPER_ROLE_NORMAL;
+      helper->can_activate = FALSE;
+      helper->enabled = FALSE;
+      helper->active = FALSE;
+      helper->group = NULL;
+    }
+
+  /* Setup the new group, if there is one. */
+  if (group)
+    {
+      const GVariantType *parameter_type;
+      gboolean enabled;
+      GVariant *state;
+
+      helper->group = gtk_action_helper_group_get (group);
+
+      gtk_action_helper_group_watch (helper->group, helper->action_name, helper);
+
+      if (g_action_group_query_action (group, helper->action_name, &enabled, &parameter_type, NULL, NULL, &state))
+        {
+          gtk_action_helper_action_added (helper, enabled, parameter_type, state, FALSE);
+
+          if (state)
+            g_variant_unref (state);
+        }
+    }
+
+  /* Send the notifies for the properties that changed.
+   *
+   * When called during construction, widget is NULL.  We don't need to
+   * report in that case.
+   */
+  if (widget != NULL)
+    {
+      if (helper->enabled != was_enabled)
+        gtk_action_helper_report_change (helper, PROP_ENABLED);
+
+      if (helper->active != was_active)
+        gtk_action_helper_report_change (helper, PROP_ACTIVE);
+
+      if (helper->role != old_role)
+        gtk_action_helper_report_change (helper, PROP_ROLE);
+    }
+}
+
+static void
+gtk_action_helper_get_property (GObject *object, guint prop_id,
+                                GValue *value, GParamSpec *pspec)
+{
+  GtkActionHelper *helper = GTK_ACTION_HELPER (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENABLED:
+      g_value_set_boolean (value, helper->enabled);
+      break;
+
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, helper->active);
+      break;
+
+    case PROP_ROLE:
+      g_value_set_uint (value, helper->role);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gtk_action_helper_finalize (GObject *object)
+{
+  GtkActionHelper *helper = GTK_ACTION_HELPER (object);
+
+  g_signal_handlers_disconnect_by_func (helper->widget, gtk_action_helper_actions_changed, helper);
+
+  g_free (helper->action_group_name);
+  g_free (helper->action_name);
+
+  if (helper->target)
+    g_variant_unref (helper->target);
+
+  G_OBJECT_CLASS (gtk_action_helper_parent_class)
+    ->finalize (object);
+}
+
+static void
+gtk_action_helper_init (GtkActionHelper *helper)
+{
+}
+
+static void
+gtk_action_helper_class_init (GtkActionHelperClass *class)
+{
+  class->get_property = gtk_action_helper_get_property;
+  class->finalize = gtk_action_helper_finalize;
+
+  gtk_action_helper_pspecs[PROP_ENABLED] = g_param_spec_boolean ("enabled", "enabled", "enabled", FALSE,
+                                                                 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  gtk_action_helper_pspecs[PROP_ACTIVE] = g_param_spec_boolean ("active", "active", "active", FALSE,
+                                                                G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  gtk_action_helper_pspecs[PROP_ROLE] = g_param_spec_uint ("role", "role", "role", 0, 2, 0,
+                                                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+  g_object_class_install_properties (class, N_PROPS, gtk_action_helper_pspecs);
+}
+
+/*< private >
+ * gtk_action_helper_new:
+ * @widget: a #GtkWidget
+ * @action_name: an action name, as per #GtkActionable
+ * @action_target: an action target, as per #GtkActionable
+ *
+ * Creates a helper to track the state of a named action.  This will
+ * usually be used by widgets implementing #GtkActionable.
+ *
+ * This helper class is usually used by @widget itself.  In order to
+ * avoid reference cycles, the helper does not hold a reference on
+ * @widget, but will assume that it continues to exist for the duration
+ * of the life of the helper.  If you are using the helper from outside
+ * of the widget, you should take a ref on @widget for each ref you hold
+ * on the helper.
+ *
+ * This function consumes @action_target if it is floating.
+ *
+ * Returns: a new #GtkActionHelper
+ */
+GtkActionHelper *
+gtk_action_helper_new (GtkActionable *widget)
+{
+  GtkActionHelper *helper;
+
+  g_return_val_if_fail (GTK_IS_ACTIONABLE (widget), NULL);
+  helper = g_object_new (GTK_TYPE_ACTION_HELPER, NULL);
+
+  helper->widget = widget;
+
+  g_signal_connect (widget, "actions-changed", G_CALLBACK (gtk_action_helper_actions_changed), helper);
+
+  return helper;
+}
+
+void
+gtk_action_helper_set_action_name (GtkActionHelper *helper,
+                                   const gchar     *action_name)
+{
+  const gchar *name_dot = NULL;
+
+  if (g_strcmp0 (action_name, helper->action_name) == 0)
+    return;
+
+  if (action_name)
+    name_dot = strchr (action_name, '.');
+
+  /* If the name was given it must have a dot in it */
+  g_return_if_fail (action_name == NULL || name_dot != NULL);
+
+  if (helper->full_action_name)
+    {
+      g_free (helper->full_action_name);
+      g_free (helper->action_group_name);
+      g_free (helper->action_name);
+      helper->full_action_name = NULL;
+      helper->action_group_name = NULL;
+      helper->action_name = NULL;
+    }
+
+  if (action_name)
+    {
+      helper->full_action_name = g_strdup (action_name);
+      helper->action_group_name = g_strndup (action_name, name_dot - action_name);
+      helper->action_name = g_strdup (name_dot + 1);
+    }
+
+  gtk_action_helper_actions_changed (NULL, NULL, helper);
+
+  g_object_notify (G_OBJECT (helper->widget), "action-name");
+}
+
+void
+gtk_action_helper_set_action_target_value (GtkActionHelper *helper,
+                                           GVariant        *target_value)
+{
+  gboolean was_enabled;
+  gboolean was_active;
+
+  if (target_value == helper->target)
+    return;
+
+  if (target_value && helper->target && g_variant_equal (target_value, helper->target))
+    return;
+
+  if (helper->target)
+    {
+      g_variant_unref (helper->target);
+      helper->target = NULL;
+    }
+
+  if (target_value)
+    helper->target = g_variant_ref_sink (target_value);
+
+  was_enabled = helper->enabled;
+  was_active = helper->active;
+
+  /* If we are attached to an action group then it is possible that this
+   * change of the target value could impact our properties (including
+   * changes to 'can_activate' and therefore 'enabled', due to resolving
+   * a parameter type mismatch).
+   *
+   * Start over again by pretending the action gets re-added.
+   */
+  helper->can_activate = FALSE;
+  helper->enabled = FALSE;
+  helper->active = FALSE;
+
+  if (helper->group)
+    {
+      const GVariantType *parameter_type;
+      gboolean enabled;
+      GVariant *state;
+
+      if (g_action_group_query_action (helper->group->group, helper->action_name,
+                                       &enabled, &parameter_type, NULL, NULL, &state))
+        {
+          gtk_action_helper_action_added (helper, enabled, parameter_type, state, FALSE);
+
+          if (state)
+            g_variant_unref (state);
+        }
+    }
+
+  if (helper->enabled != was_enabled)
+    gtk_action_helper_report_change (helper, PROP_ENABLED);
+
+  if (helper->active != was_active)
+    gtk_action_helper_report_change (helper, PROP_ACTIVE);
+
+  g_object_notify (G_OBJECT (helper->widget), "action-target");
+}
+
+const gchar *
+gtk_action_helper_get_action_name (GtkActionHelper *helper)
+{
+  if (helper == NULL)
+    return NULL;
+
+  return helper->full_action_name;
+}
+
+GVariant *
+gtk_action_helper_get_action_target_value (GtkActionHelper *helper)
+{
+  if (helper == NULL)
+    return NULL;
+
+  return helper->target;
+}
+
+gboolean
+gtk_action_helper_get_enabled (GtkActionHelper *helper)
+{
+  g_return_val_if_fail (GTK_IS_ACTION_HELPER (helper), FALSE);
+
+  return helper->enabled;
+}
+
+gboolean
+gtk_action_helper_get_active (GtkActionHelper *helper)
+{
+  g_return_val_if_fail (GTK_IS_ACTION_HELPER (helper), FALSE);
+
+  return helper->active;
+}
+
+void
+gtk_action_helper_activate (GtkActionHelper *helper)
+{
+  g_return_if_fail (GTK_IS_ACTION_HELPER (helper));
+
+  if (!helper->can_activate || helper->reporting)
+    return;
+
+  g_action_group_activate_action (helper->group->group, helper->action_name, helper->target);
+}
diff --git a/gtk/gtkactionhelper.h b/gtk/gtkactionhelper.h
new file mode 100644
index 0000000..b3a2529
--- /dev/null
+++ b/gtk/gtkactionhelper.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright  2012 Canonical Limited
+ *
+ * This library 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ryan Lortie <desrt desrt ca>
+ */
+
+#ifndef __GTK_ACTION_HELPER_H__
+#define __GTK_ACTION_HELPER_H__
+
+#include <gtk/gtkactionable.h>
+
+#define GTK_TYPE_ACTION_HELPER                              (gtk_action_helper_get_type ())
+#define GTK_ACTION_HELPER(inst)                             (G_TYPE_CHECK_INSTANCE_CAST ((inst),                      \
+                                                             GTK_TYPE_ACTION_HELPER, GtkActionHelper))
+#define GTK_IS_ACTION_HELPER(inst)                          (G_TYPE_CHECK_INSTANCE_TYPE ((inst),                      \
+                                                             GTK_TYPE_ACTION_HELPER))
+
+typedef struct _GtkActionHelper                             GtkActionHelper;
+
+typedef enum
+{
+  GTK_ACTION_HELPER_ROLE_NORMAL,
+  GTK_ACTION_HELPER_ROLE_TOGGLE,
+  GTK_ACTION_HELPER_ROLE_RADIO
+} GtkActionHelperRole;
+
+G_GNUC_INTERNAL
+GType                   gtk_action_helper_get_type                      (void);
+
+G_GNUC_INTERNAL
+GtkActionHelper *       gtk_action_helper_new                           (GtkActionable   *widget);
+
+G_GNUC_INTERNAL
+void                    gtk_action_helper_set_action_name               (GtkActionHelper *helper,
+                                                                         const gchar     *action_name);
+G_GNUC_INTERNAL
+void                    gtk_action_helper_set_action_target_value       (GtkActionHelper *helper,
+                                                                         GVariant        *action_target);
+G_GNUC_INTERNAL
+const gchar *           gtk_action_helper_get_action_name               (GtkActionHelper *helper);
+G_GNUC_INTERNAL
+GVariant *              gtk_action_helper_get_action_target_value       (GtkActionHelper *helper);
+
+G_GNUC_INTERNAL
+GtkActionHelperRole     gtk_action_helper_get_display_role              (GtkActionHelper *helper);
+G_GNUC_INTERNAL
+gboolean                gtk_action_helper_get_enabled                   (GtkActionHelper *helper);
+G_GNUC_INTERNAL
+gboolean                gtk_action_helper_get_active                    (GtkActionHelper *helper);
+
+G_GNUC_INTERNAL
+void                    gtk_action_helper_activate                      (GtkActionHelper *helper);
+
+#endif /* __GTK_ACTION_HELPER_H__ */
diff --git a/gtk/gtkapplication.c b/gtk/gtkapplication.c
index 2b87b9c..49b331f 100644
--- a/gtk/gtkapplication.c
+++ b/gtk/gtkapplication.c
@@ -33,7 +33,6 @@
 #include "gtkmain.h"
 #include "gtkrecentmanager.h"
 #include "gtkaccelmapprivate.h"
-#include "gactionmuxer.h"
 #include "gtkintl.h"
 
 #ifdef GDK_WINDOWING_QUARTZ
diff --git a/gtk/gtkapplicationprivate.h b/gtk/gtkapplicationprivate.h
index a05752c..bd84b1a 100644
--- a/gtk/gtkapplicationprivate.h
+++ b/gtk/gtkapplicationprivate.h
@@ -21,7 +21,6 @@
 #ifndef __GTK_APPLICATION_PRIVATE_H__
 #define __GTK_APPLICATION_PRIVATE_H__
 
-#include "gsimpleactionobserver.h"
 #include "gtkapplicationwindow.h"
 
 G_GNUC_INTERNAL
@@ -34,14 +33,6 @@ G_GNUC_INTERNAL
 void                    gtk_application_window_unpublish                (GtkApplicationWindow *window);
 
 G_GNUC_INTERNAL
-GSimpleActionObserver * gtk_application_window_create_observer          (GtkApplicationWindow *window,
-                                                                         const gchar          *action_name,
-                                                                         GVariant             *target);
-
-G_GNUC_INTERNAL
-GActionObservable     * gtk_application_window_get_observable           (GtkApplicationWindow *window);
-
-G_GNUC_INTERNAL
 GtkAccelGroup         * gtk_application_window_get_accel_group          (GtkApplicationWindow *window);
 
 G_GNUC_INTERNAL
diff --git a/gtk/gtkapplicationwindow.c b/gtk/gtkapplicationwindow.c
index ab98920..798d4ab 100644
--- a/gtk/gtkapplicationwindow.c
+++ b/gtk/gtkapplicationwindow.c
@@ -24,7 +24,6 @@
 #include "gtkapplicationprivate.h"
 #include "gtkwindowprivate.h"
 #include "gtkmodelmenu.h"
-#include "gactionmuxer.h"
 #include "gtkaccelgroup.h"
 #include "gtkaccelmap.h"
 #include "gtkintl.h"
@@ -205,8 +204,6 @@ gtk_application_window_actions_new (GtkApplicationWindow *window)
 struct _GtkApplicationWindowPrivate
 {
   GSimpleActionGroup *actions;
-  GActionObservable *muxer;
-  gboolean muxer_initialised;
   GtkWidget *menubar;
   GtkAccelGroup *accels;
   GSList *accel_closures;
@@ -250,7 +247,7 @@ gtk_application_window_update_menubar (GtkApplicationWindow *window)
       g_menu_append_section (combined, NULL, G_MENU_MODEL (window->priv->app_menu_section));
       g_menu_append_section (combined, NULL, G_MENU_MODEL (window->priv->menubar_section));
 
-      window->priv->menubar = gtk_model_menu_create_menu_bar (G_MENU_MODEL (combined), window->priv->muxer, window->priv->accels);
+      window->priv->menubar = gtk_model_menu_create_menu_bar (G_MENU_MODEL (combined), window->priv->accels);
       gtk_widget_set_parent (window->priv->menubar, GTK_WIDGET (window));
       gtk_widget_show_all (window->priv->menubar);
       g_object_unref (combined);
@@ -482,7 +479,7 @@ gtk_application_window_update_accels (GtkApplicationWindow *window)
   free_accel_closures (window);
 
   data.window = window;
-  data.actions = G_ACTION_GROUP (window->priv->muxer);
+  data.actions = 0; /* XXX G_ACTION_GROUP (window->priv->muxer); */
 
   gtk_accel_map_foreach (&data, add_accel_closure);
 }
@@ -749,13 +746,6 @@ 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_initialised)
-    {
-      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);
   gtk_application_window_update_shell_shows_menubar (window, settings);
   gtk_application_window_update_menubar (window);
@@ -878,6 +868,16 @@ gtk_application_window_real_forall_internal (GtkContainer *container,
     ->forall (container, include_internal, callback, user_data);
 }
 
+static GActionGroup *
+gtk_application_window_get_action_group_by_name (GtkWidget   *widget,
+                                                 const gchar *name)
+{
+  if (g_str_equal (name, "win"))
+    return G_ACTION_GROUP (widget);
+
+  return GTK_WIDGET_CLASS (gtk_application_window_parent_class)
+    ->get_action_group_by_name (widget, name);
+}
 
 static void
 gtk_application_window_get_property (GObject    *object,
@@ -934,7 +934,6 @@ gtk_application_window_dispose (GObject *object)
   g_clear_object (&window->priv->menubar_section);
   g_clear_object (&window->priv->actions);
   g_clear_object (&window->priv->accels);
-  g_clear_object (&window->priv->muxer);
 
   G_OBJECT_CLASS (gtk_application_window_parent_class)
     ->dispose (object);
@@ -962,8 +961,6 @@ 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,6 +979,7 @@ gtk_application_window_class_init (GtkApplicationWindowClass *class)
   widget_class->realize = gtk_application_window_real_realize;
   widget_class->unrealize = gtk_application_window_real_unrealize;
   widget_class->map = gtk_application_window_real_map;
+  widget_class->get_action_group_by_name = gtk_application_window_get_action_group_by_name;
   object_class->get_property = gtk_application_window_get_property;
   object_class->set_property = gtk_application_window_set_property;
   object_class->dispose = gtk_application_window_dispose;
@@ -1072,22 +1070,6 @@ gtk_application_window_set_show_menubar (GtkApplicationWindow *window,
     }
 }
 
-GSimpleActionObserver *
-gtk_application_window_create_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);
-}
-
-GActionObservable *
-gtk_application_window_get_observable (GtkApplicationWindow *window)
-{
-  return G_ACTION_OBSERVABLE (window->priv->muxer);
-}
-
 GtkAccelGroup *
 gtk_application_window_get_accel_group (GtkApplicationWindow *window)
 {
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 545434c..621cd4f 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -57,7 +57,7 @@
 #include "gtkintl.h"
 #include "a11y/gtkbuttonaccessible.h"
 #include "gtkapplicationprivate.h"
-#include "gtkactionable.h"
+#include "gtkactionhelper.h"
 
 static const GtkBorder default_default_border = { 1, 1, 1, 1 };
 static const GtkBorder default_default_outside_border = { 0, 0, 0, 0 };
@@ -151,9 +151,6 @@ 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);
@@ -217,7 +214,6 @@ 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;
@@ -616,12 +612,6 @@ 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);
 }
 
@@ -704,7 +694,7 @@ gtk_button_dispose (GObject *object)
   GtkButton *button = GTK_BUTTON (object);
   GtkButtonPrivate *priv = button->priv;
 
-  g_clear_object (&priv->action_observer);
+  g_clear_object (&priv->action_helper);
 
   if (priv->action)
     {
@@ -715,52 +705,21 @@ 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_create_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));
   g_return_if_fail (button->priv->action == NULL);
 
-  g_free (button->priv->action_name);
-  button->priv->action_name = g_strdup (action_name);
+  if (!button->priv->action_helper)
+    button->priv->action_helper = gtk_action_helper_new (actionable);
 
-  gtk_button_update_action_observer (button);
+  g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
+  if (action_name)
+    g_signal_connect_after (button, "clicked", G_CALLBACK (gtk_real_button_clicked), NULL);
 
-  g_object_notify (G_OBJECT (button), "action-name");
+  gtk_action_helper_set_action_name (button->priv->action_helper, action_name);
 }
 
 static void
@@ -769,24 +728,10 @@ gtk_button_set_action_target_value (GtkActionable *actionable,
 {
   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);
+  if (!button->priv->action_helper)
+    button->priv->action_helper = gtk_action_helper_new (actionable);
 
-      g_object_notify (G_OBJECT (button), "action-target");
-    }
+  gtk_action_helper_set_action_target_value (button->priv->action_helper, action_target);
 }
 
 static void
@@ -896,10 +841,10 @@ gtk_button_get_property (GObject         *object,
       g_value_set_boolean (value, priv->use_action_appearance);
       break;
     case PROP_ACTION_NAME:
-      g_value_set_string (value, priv->action_name);
+      g_value_set_string (value, gtk_action_helper_get_action_name (priv->action_helper));
       break;
     case PROP_ACTION_TARGET:
-      g_value_set_variant (value, priv->action_target);
+      g_value_set_variant (value, gtk_action_helper_get_action_target_value (priv->action_helper));
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -912,7 +857,7 @@ gtk_button_get_action_name (GtkActionable *actionable)
 {
   GtkButton *button = GTK_BUTTON (actionable);
 
-  return button->priv->action_name;
+  return gtk_action_helper_get_action_name (button->priv->action_helper);
 }
 
 static GVariant *
@@ -920,7 +865,7 @@ gtk_button_get_action_target_value (GtkActionable *actionable)
 {
   GtkButton *button = GTK_BUTTON (actionable);
 
-  return button->priv->action_target;
+  return gtk_action_helper_get_action_target_value (button->priv->action_helper);
 }
 
 static void
@@ -1069,7 +1014,7 @@ gtk_button_set_related_action (GtkButton *button,
 {
   GtkButtonPrivate *priv = button->priv;
 
-  g_return_if_fail (button->priv->action_name == NULL);
+  g_return_if_fail (gtk_action_helper_get_action_name (button->priv->action_helper) == NULL);
 
   if (priv->action == action)
     return;
@@ -2027,8 +1972,8 @@ 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_helper)
+    gtk_action_helper_activate (priv->action_helper);
 
   if (priv->action)
     gtk_action_activate (priv->action);
@@ -2624,28 +2569,6 @@ 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
diff --git a/gtk/gtkbuttonprivate.h b/gtk/gtkbuttonprivate.h
index 8d7966e..ea44b71 100644
--- a/gtk/gtkbuttonprivate.h
+++ b/gtk/gtkbuttonprivate.h
@@ -19,7 +19,7 @@
 #ifndef __GTK_BUTTON_PRIVATE_H__
 #define __GTK_BUTTON_PRIVATE_H__
 
-#include "gsimpleactionobserver.h"
+#include "gtkactionhelper.h"
 #include "gtkaction.h"
 
 G_BEGIN_DECLS
@@ -29,10 +29,7 @@ struct _GtkButtonPrivate
 {
   GtkAction             *action;
   GtkWidget             *image;
-
-  gchar                 *action_name;
-  GVariant              *action_target;
-  GSimpleActionObserver *action_observer;
+  GtkActionHelper       *action_helper;
 
   GdkDevice             *grab_keyboard;
   GdkWindow             *event_window;
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index ed4c520..9e21e36 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -302,6 +302,8 @@ static void gtk_menu_get_preferred_height_for_width (GtkWidget           *widget
                                                      gint                *minimum_size,
                                                      gint                *natural_size);
 
+static GActionGroup * gtk_menu_get_action_group_by_name (GtkWidget   *widget,
+                                                         const gchar *name);
 
 static const gchar attach_data_key[] = "gtk-menu-attach-data";
 
@@ -513,6 +515,7 @@ gtk_menu_class_init (GtkMenuClass *class)
   widget_class->get_preferred_width = gtk_menu_get_preferred_width;
   widget_class->get_preferred_height = gtk_menu_get_preferred_height;
   widget_class->get_preferred_height_for_width = gtk_menu_get_preferred_height_for_width;
+  widget_class->get_action_group_by_name = gtk_menu_get_action_group_by_name;
 
   container_class->remove = gtk_menu_remove;
   container_class->get_child_property = gtk_menu_get_child_property;
@@ -1165,6 +1168,14 @@ attach_widget_screen_changed (GtkWidget *attach_widget,
     menu_change_screen (menu, gtk_widget_get_screen (attach_widget));
 }
 
+static void
+attach_widget_actions_changed (GtkWidget   *attach_widget,
+                               const gchar *action_group_name,
+                               GtkMenu     *menu)
+{
+  gtk_widget_propagate_actions_changed (GTK_WIDGET (menu), action_group_name);
+}
+
 /**
  * gtk_menu_attach_to_widget:
  * @menu: a #GtkMenu
@@ -1205,6 +1216,12 @@ gtk_menu_attach_to_widget (GtkMenu           *menu,
                     G_CALLBACK (attach_widget_screen_changed), menu);
   attach_widget_screen_changed (attach_widget, NULL, menu);
 
+  if (menu->priv->action_requested)
+    {
+      g_signal_connect (attach_widget, "actions-changed", G_CALLBACK (attach_widget_actions_changed), menu);
+      attach_widget_actions_changed (NULL, NULL, menu);
+    }
+
   data->detacher = detacher;
   g_object_set_data (G_OBJECT (menu), I_(attach_data_key), data);
   list = g_object_steal_data (G_OBJECT (attach_widget), ATTACHED_MENUS);
@@ -1278,6 +1295,9 @@ gtk_menu_detach (GtkMenu *menu)
   g_signal_handlers_disconnect_by_func (data->attach_widget,
                                         (gpointer) attach_widget_screen_changed,
                                         menu);
+  g_signal_handlers_disconnect_by_func (data->attach_widget,
+                                        (gpointer) attach_widget_actions_changed,
+                                        menu);
 
   if (data->detacher)
     data->detacher (data->attach_widget, menu);
@@ -1318,6 +1338,29 @@ gtk_menu_remove (GtkContainer *container,
   menu_queue_resize (menu);
 }
 
+static GActionGroup *
+gtk_menu_get_action_group_by_name (GtkWidget   *widget,
+                                   const gchar *name)
+{
+  GtkMenu *menu = GTK_MENU (widget);
+  GtkWidget *attach_widget;
+
+  attach_widget = gtk_menu_get_attach_widget (GTK_MENU (widget));
+
+  if (!menu->priv->action_requested)
+    {
+      if (attach_widget != NULL)
+        g_signal_connect (attach_widget, "actions-changed", G_CALLBACK (attach_widget_actions_changed), menu);
+
+      menu->priv->action_requested = TRUE;
+    }
+
+  if (attach_widget == NULL)
+    return NULL;
+
+  return gtk_widget_get_action_group_by_name (attach_widget, name);
+}
+
 /**
  * gtk_menu_new:
  *
diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c
index 5a916da..1cdb29a 100644
--- a/gtk/gtkmenuitem.c
+++ b/gtk/gtkmenuitem.c
@@ -44,7 +44,6 @@
 #include "gtktypebuiltins.h"
 #include "a11y/gtkmenuitemaccessible.h"
 
-
 /**
  * SECTION:gtkmenuitem
  * @Short_description: The widget used for item in menus
@@ -97,7 +96,10 @@ enum {
 
   /* activatable properties */
   PROP_ACTIVATABLE_RELATED_ACTION,
-  PROP_ACTIVATABLE_USE_ACTION_APPEARANCE
+  PROP_ACTIVATABLE_USE_ACTION_APPEARANCE,
+
+  PROP_ACTION_NAME,
+  PROP_ACTION_TARGET
 };
 
 
@@ -179,6 +181,7 @@ static void gtk_menu_item_buildable_custom_finished(GtkBuildable        *buildab
                                                     const gchar         *tagname,
                                                     gpointer             user_data);
 
+static void gtk_menu_item_actionable_interface_init  (GtkActionableInterface *iface);
 static void gtk_menu_item_activatable_interface_init (GtkActivatableIface  *iface);
 static void gtk_menu_item_update                     (GtkActivatable       *activatable,
                                                       GtkAction            *action,
@@ -198,7 +201,58 @@ G_DEFINE_TYPE_WITH_CODE (GtkMenuItem, gtk_menu_item, GTK_TYPE_BIN,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                 gtk_menu_item_buildable_interface_init)
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIVATABLE,
-                                                gtk_menu_item_activatable_interface_init))
+                                                gtk_menu_item_activatable_interface_init)
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE,
+                                                gtk_menu_item_actionable_interface_init))
+
+static void
+gtk_menu_item_set_action_name (GtkActionable *actionable,
+                               const gchar   *action_name)
+{
+  GtkMenuItem *menu_item = GTK_MENU_ITEM (actionable);
+
+  if (!menu_item->priv->action_helper)
+    menu_item->priv->action_helper = gtk_action_helper_new (actionable);
+
+  gtk_action_helper_set_action_name (menu_item->priv->action_helper, action_name);
+}
+
+static void
+gtk_menu_item_set_action_target_value (GtkActionable *actionable,
+                                       GVariant      *action_target)
+{
+  GtkMenuItem *menu_item = GTK_MENU_ITEM (actionable);
+
+  if (!menu_item->priv->action_helper)
+    menu_item->priv->action_helper = gtk_action_helper_new (actionable);
+
+  gtk_action_helper_set_action_target_value (menu_item->priv->action_helper, action_target);
+}
+
+static const gchar *
+gtk_menu_item_get_action_name (GtkActionable *actionable)
+{
+  GtkMenuItem *menu_item = GTK_MENU_ITEM (actionable);
+
+  return gtk_action_helper_get_action_name (menu_item->priv->action_helper);
+}
+
+static GVariant *
+gtk_menu_item_get_action_target_value (GtkActionable *actionable)
+{
+  GtkMenuItem *menu_item = GTK_MENU_ITEM (actionable);
+
+  return gtk_action_helper_get_action_target_value (menu_item->priv->action_helper);
+}
+
+static void
+gtk_menu_item_actionable_interface_init (GtkActionableInterface *iface)
+{
+  iface->set_action_name = gtk_menu_item_set_action_name;
+  iface->get_action_name = gtk_menu_item_get_action_name;
+  iface->set_action_target_value = gtk_menu_item_set_action_target_value;
+  iface->get_action_target_value = gtk_menu_item_get_action_target_value;
+}
 
 static void
 gtk_menu_item_class_init (GtkMenuItemClass *klass)
@@ -397,6 +451,9 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass)
   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");
 
+  g_object_class_override_property (gobject_class, PROP_ACTION_NAME, "action-name");
+  g_object_class_override_property (gobject_class, PROP_ACTION_TARGET, "action-target");
+
   gtk_widget_class_install_style_property_parser (widget_class,
                                                   g_param_spec_enum ("selected-shadow-type",
                                                                      "Selected Shadow Type",
@@ -602,6 +659,12 @@ gtk_menu_item_set_property (GObject      *object,
     case PROP_ACTIVATABLE_USE_ACTION_APPEARANCE:
       gtk_menu_item_set_use_action_appearance (menu_item, g_value_get_boolean (value));
       break;
+    case PROP_ACTION_NAME:
+      gtk_menu_item_set_action_name (GTK_ACTIONABLE (menu_item), g_value_get_string (value));
+      break;
+    case PROP_ACTION_TARGET:
+      gtk_menu_item_set_action_target_value (GTK_ACTIONABLE (menu_item), g_value_get_variant (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -640,6 +703,12 @@ gtk_menu_item_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, gtk_action_helper_get_action_name (priv->action_helper));
+      break;
+    case PROP_ACTION_TARGET:
+      g_value_set_variant (value, gtk_action_helper_get_action_target_value (priv->action_helper));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1749,6 +1818,9 @@ gtk_real_menu_item_activate (GtkMenuItem *menu_item)
 {
   GtkMenuItemPrivate *priv = menu_item->priv;
 
+  if (priv->action_helper)
+    gtk_action_helper_activate (priv->action_helper);
+
   if (priv->action)
     gtk_action_activate (priv->action);
 }
diff --git a/gtk/gtkmenuitemprivate.h b/gtk/gtkmenuitemprivate.h
index 6fb81f7..4833954 100644
--- a/gtk/gtkmenuitemprivate.h
+++ b/gtk/gtkmenuitemprivate.h
@@ -20,7 +20,7 @@
 
 #include <gtk/gtkmenuitem.h>
 #include <gtk/gtkaction.h>
-
+#include <gtk/gtkactionhelper.h>
 
 G_BEGIN_DECLS
 
@@ -37,6 +37,11 @@ struct _GtkMenuItemPrivate
   gchar  *accel_path;
 
   GtkAction *action;
+  GtkActionHelper *action_helper;
+
+  GtkActionHelper *helper;
+  gchar           *action_name;
+  GVariant        *action_target;
 
   guint show_submenu_indicator : 1;
   guint submenu_placement      : 1;
diff --git a/gtk/gtkmenuprivate.h b/gtk/gtkmenuprivate.h
index f458358..6dd76e6 100644
--- a/gtk/gtkmenuprivate.h
+++ b/gtk/gtkmenuprivate.h
@@ -101,6 +101,7 @@ struct _GtkMenuPrivate
   guint no_toggle_size        : 1;
   guint drag_already_pressed  : 1;
   guint drag_scroll_started   : 1;
+  guint action_requested      : 1;
 
   /* info used for the table */
   guint *heights;
diff --git a/gtk/gtkmodelmenu.c b/gtk/gtkmodelmenu.c
index 1ac533d..19cb4f1 100644
--- a/gtk/gtkmodelmenu.c
+++ b/gtk/gtkmodelmenu.c
@@ -30,7 +30,6 @@
 #include "gtkapplicationprivate.h"
 
 typedef struct {
-  GActionObservable *actions;
   GMenuModel        *model;
   GtkAccelGroup     *accels;
   GtkMenuShell      *shell;
@@ -64,8 +63,6 @@ gtk_model_menu_binding_free (gpointer data)
       binding->connected = g_slist_delete_link (binding->connected, binding->connected);
     }
 
-  if (binding->actions)
-    g_object_unref (binding->actions);
   g_object_unref (binding->model);
 
   g_slice_free (GtkModelMenuBinding, binding);
@@ -88,7 +85,7 @@ gtk_model_menu_binding_append_item (GtkModelMenuBinding  *binding,
     {
       GtkMenuItem *item;
 
-      item = gtk_model_menu_item_new (model, item_index, binding->actions, binding->accels);
+      item = gtk_model_menu_item_new (model, item_index, binding->accels);
       gtk_menu_shell_append (binding->shell, GTK_WIDGET (item));
       gtk_widget_show (GTK_WIDGET (item));
       binding->n_items++;
@@ -233,7 +230,6 @@ gtk_model_menu_bind (GtkMenuShell      *shell,
 
   binding = g_slice_new (GtkModelMenuBinding);
   binding->model = g_object_ref (model);
-  binding->actions = NULL;
   binding->accels = NULL;
   binding->shell = shell;
   binding->update_idle = 0;
@@ -245,56 +241,32 @@ gtk_model_menu_bind (GtkMenuShell      *shell,
 
 
 static void
-gtk_model_menu_populate (GtkMenuShell      *shell,
-                         GActionObservable *actions,
-                         GtkAccelGroup     *accels)
+gtk_model_menu_populate (GtkMenuShell  *shell,
+                         GtkAccelGroup *accels)
 {
   GtkModelMenuBinding *binding;
 
   binding = (GtkModelMenuBinding*) g_object_get_data (G_OBJECT (shell), "gtk-model-menu-binding");
 
-  binding->actions = g_object_ref (actions);
   binding->accels = accels;
 
   gtk_model_menu_binding_populate (binding);
 }
 
 GtkWidget *
-gtk_model_menu_create_menu (GMenuModel        *model,
-                            GActionObservable *actions,
-                            GtkAccelGroup     *accels)
+gtk_model_menu_create_menu (GMenuModel    *model,
+                            GtkAccelGroup *accels)
 {
   GtkWidget *menu;
 
   menu = gtk_menu_new ();
 
   gtk_model_menu_bind (GTK_MENU_SHELL (menu), model, TRUE);
-  gtk_model_menu_populate (GTK_MENU_SHELL (menu), actions, accels);
+  gtk_model_menu_populate (GTK_MENU_SHELL (menu), accels);
 
   return menu;
 }
 
-static void
-notify_attach (GtkMenu    *menu,
-               GParamSpec *pspec,
-               gpointer    data)
-{
-  GtkWidget *widget;
-  GtkWidget *toplevel;
-  GActionObservable *actions;
-  GtkAccelGroup *accels;
-
-  widget = gtk_menu_get_attach_widget (menu);
-  toplevel = gtk_widget_get_toplevel (widget);
-  if (GTK_IS_APPLICATION_WINDOW (toplevel))
-    {
-      actions = gtk_application_window_get_observable (GTK_APPLICATION_WINDOW (toplevel));
-      accels = gtk_application_window_get_accel_group (GTK_APPLICATION_WINDOW (toplevel));
-
-      gtk_model_menu_populate (GTK_MENU_SHELL (menu), actions, accels);
-    }
-}
-
 /**
  * gtk_menu_new_from_model:
  * @model: a #GMenuModel
@@ -318,46 +290,24 @@ gtk_menu_new_from_model (GMenuModel *model)
 
   menu = gtk_menu_new ();
   gtk_model_menu_bind (GTK_MENU_SHELL (menu), model, TRUE);
-  g_signal_connect (menu, "notify::attach-widget",
-                    G_CALLBACK (notify_attach), NULL);
 
   return menu;
 }
 
 GtkWidget *
-gtk_model_menu_create_menu_bar (GMenuModel        *model,
-                                GActionObservable *actions,
-                                GtkAccelGroup     *accels)
+gtk_model_menu_create_menu_bar (GMenuModel    *model,
+                                GtkAccelGroup *accels)
 {
   GtkWidget *menubar;
 
   menubar = gtk_menu_bar_new ();
 
   gtk_model_menu_bind (GTK_MENU_SHELL (menubar), model, FALSE);
-  gtk_model_menu_populate (GTK_MENU_SHELL (menubar), actions, accels);
+  gtk_model_menu_populate (GTK_MENU_SHELL (menubar), accels);
 
   return menubar;
 }
 
-static void
-hierarchy_changed (GtkMenuShell *shell,
-                   GObject      *previous_toplevel,
-                   gpointer      data)
-{
-  GtkWidget *toplevel;
-  GActionObservable *actions;
-  GtkAccelGroup *accels;
-
-  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (shell));
-  if (GTK_IS_APPLICATION_WINDOW (toplevel))
-    {
-      actions = gtk_application_window_get_observable (GTK_APPLICATION_WINDOW (toplevel));
-      accels = gtk_application_window_get_accel_group (GTK_APPLICATION_WINDOW (toplevel));
-
-      gtk_model_menu_populate (shell, actions, accels);
-    }
-}
-
 /**
  * gtk_menu_bar_new_from_model:
  * @model: a #GMenuModel
@@ -383,8 +333,5 @@ gtk_menu_bar_new_from_model (GMenuModel *model)
 
   gtk_model_menu_bind (GTK_MENU_SHELL (menubar), model, FALSE);
 
-  g_signal_connect (menubar, "hierarchy-changed",
-                    G_CALLBACK (hierarchy_changed), NULL);
-
   return menubar;
 }
diff --git a/gtk/gtkmodelmenu.h b/gtk/gtkmodelmenu.h
index d2df78f..05654c9 100644
--- a/gtk/gtkmodelmenu.h
+++ b/gtk/gtkmodelmenu.h
@@ -20,19 +20,16 @@
 #ifndef __GTK_MODEL_MENU_H__
 #define __GTK_MODEL_MENU_H__
 
-#include <gtk/gactionobservable.h>
 #include <gtk/gtkmenushell.h>
 #include <gtk/gtkaccelgroup.h>
 #include <gio/gio.h>
 
 G_GNUC_INTERNAL
 GtkWidget * gtk_model_menu_create_menu_bar (GMenuModel        *model,
-                                            GActionObservable *actions,
                                             GtkAccelGroup     *accels);
 
 G_GNUC_INTERNAL
 GtkWidget * gtk_model_menu_create_menu     (GMenuModel        *model,
-                                            GActionObservable *actions,
                                             GtkAccelGroup     *accels);
 
 #endif /* __GTK_MODEL_MENU_H__ */
diff --git a/gtk/gtkmodelmenuitem.c b/gtk/gtkmodelmenuitem.c
index d07f4d2..5b30167 100644
--- a/gtk/gtkmodelmenuitem.c
+++ b/gtk/gtkmodelmenuitem.c
@@ -22,33 +22,21 @@
 #include "gtkmodelmenuitem.h"
 
 #include "gtkaccelmapprivate.h"
+#include "gtkactionhelper.h"
 #include "gtkmodelmenu.h"
 
 struct _GtkModelMenuItem
 {
   GtkCheckMenuItem parent_instance;
-
-  GActionGroup *actions;
-  const gchar *action_name;
+  GtkActionHelperRole role;
   gboolean has_indicator;
-  gboolean can_activate;
-  GVariant *target;
 };
 
 typedef GtkCheckMenuItemClass GtkModelMenuItemClass;
 
-static void gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface);
-G_DEFINE_TYPE_WITH_CODE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM,
-                         G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_OBSERVER, gtk_model_menu_item_observer_iface_init))
+G_DEFINE_TYPE (GtkModelMenuItem, gtk_model_menu_item, GTK_TYPE_CHECK_MENU_ITEM)
 
-static void
-gtk_model_menu_item_activate (GtkMenuItem *menu_item)
-{
-  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (menu_item);
-
-  if (item->can_activate)
-    g_action_group_activate_action (item->actions, item->action_name, item->target);
-}
+#define PROP_ACTION_ROLE 1
 
 static void
 gtk_model_menu_item_toggle_size_request (GtkMenuItem *menu_item,
@@ -76,122 +64,9 @@ gtk_model_menu_item_draw_indicator (GtkCheckMenuItem *check_item,
 }
 
 static void
-gtk_model_menu_item_set_active (GtkModelMenuItem *item,
-                                gboolean          active)
-{
-  GtkCheckMenuItem *checkitem = GTK_CHECK_MENU_ITEM (item);
-
-  if (gtk_check_menu_item_get_active (checkitem) != active)
-    {
-      _gtk_check_menu_item_set_active (checkitem, active);
-      g_object_notify (G_OBJECT (checkitem), "active");
-      gtk_check_menu_item_toggled (checkitem);
-      gtk_widget_queue_draw (GTK_WIDGET (item));
-    }
-}
-
-static void
-gtk_model_menu_item_action_added (GActionObserver    *observer,
-                                  GActionObservable  *observable,
-                                  const gchar        *action_name,
-                                  const GVariantType *parameter_type,
-                                  gboolean            enabled,
-                                  GVariant           *state)
-{
-  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
-
-  /* we can only activate the item if we have the correct type of parameter */
-  item->can_activate = (item->target == NULL && parameter_type == NULL) ||
-                       (item->target != NULL && parameter_type != NULL &&
-                        g_variant_is_of_type (item->target, parameter_type));
-
-  if (item->can_activate)
-    {
-      if (item->target != NULL && state != NULL)
-        {
-          /* actions with states and targets are radios */
-          gboolean selected;
-
-          selected = g_variant_equal (state, item->target);
-          gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), TRUE);
-          gtk_model_menu_item_set_active (item, selected);
-          item->has_indicator = TRUE;
-        }
-
-      else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
-        {
-          /* boolean state actions without target are checks */
-          gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), FALSE);
-          gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
-          item->has_indicator = TRUE;
-        }
-
-      else
-        {
-          /* stateless items are just plain actions */
-          gtk_model_menu_item_set_active (item, FALSE);
-          item->has_indicator = FALSE;
-        }
-
-      gtk_widget_set_sensitive (GTK_WIDGET (item), enabled);
-      gtk_widget_queue_resize (GTK_WIDGET (item));
-    }
-}
-
-static void
-gtk_model_menu_item_action_enabled_changed (GActionObserver   *observer,
-                                            GActionObservable *observable,
-                                            const gchar       *action_name,
-                                            gboolean           enabled)
-{
-  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
-
-  if (!item->can_activate)
-    return;
-
-  gtk_widget_set_sensitive (GTK_WIDGET (item), item->can_activate && enabled);
-}
-
-static void
-gtk_model_menu_item_action_state_changed (GActionObserver   *observer,
-                                          GActionObservable *observable,
-                                          const gchar       *action_name,
-                                          GVariant          *state)
-{
-  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
-
-  if (!item->can_activate)
-    return;
-
-  if (item->target)
-    gtk_model_menu_item_set_active (item, g_variant_equal (state, item->target));
-
-  else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
-    gtk_model_menu_item_set_active (item, g_variant_get_boolean (state));
-}
-
-static void
-gtk_model_menu_item_action_removed (GActionObserver   *observer,
-                                    GActionObservable *observable,
-                                    const gchar       *action_name)
-{
-  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (observer);
-
-  if (!item->can_activate)
-    return;
-
-  gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
-  gtk_model_menu_item_set_active (item, FALSE);
-  item->has_indicator = FALSE;
-
-  gtk_widget_queue_resize (GTK_WIDGET (item));
-}
-
-static void
 gtk_model_menu_item_setup (GtkModelMenuItem  *item,
                            GMenuModel        *model,
                            gint               item_index,
-                           GActionObservable *actions,
                            GtkAccelGroup     *accels)
 {
   GMenuAttributeIter *iter;
@@ -201,7 +76,7 @@ gtk_model_menu_item_setup (GtkModelMenuItem  *item,
 
   if ((submenu = g_menu_model_get_item_link (model, item_index, "submenu")))
     {
-      gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), gtk_model_menu_create_menu (submenu, actions, accels));
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), gtk_model_menu_create_menu (submenu, accels));
       g_object_unref (submenu);
     }
 
@@ -212,10 +87,10 @@ gtk_model_menu_item_setup (GtkModelMenuItem  *item,
         gtk_menu_item_set_label (GTK_MENU_ITEM (item), g_variant_get_string (value, NULL));
 
       else if (g_str_equal (key, "action") && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
-        item->action_name = g_variant_get_string (value, NULL);
+        gtk_actionable_set_action_name (GTK_ACTIONABLE (item), g_variant_get_string (value, NULL));
 
       else if (g_str_equal (key, "target"))
-        item->target = g_variant_ref (value);
+        gtk_actionable_set_action_target_value (GTK_ACTIONABLE (item), value);
 
       g_variant_unref (value);
     }
@@ -223,57 +98,65 @@ gtk_model_menu_item_setup (GtkModelMenuItem  *item,
 
   gtk_menu_item_set_use_underline (GTK_MENU_ITEM (item), TRUE);
 
-  if (item->action_name)
-    {
-      const GVariantType *type;
-      gboolean enabled;
-      GVariant *state;
-      gchar *path;
-
-      /* observer already causes us to hold a hard ref on the group */
-      item->actions = G_ACTION_GROUP (actions);
-
-      if (actions)
-        {
-          g_action_observable_register_observer (actions, item->action_name, G_ACTION_OBSERVER (item));
-
-          if (g_action_group_query_action (G_ACTION_GROUP (actions), item->action_name, &enabled, &type, NULL, NULL, &state))
-            {
-              gtk_model_menu_item_action_added (G_ACTION_OBSERVER (item), actions, item->action_name, type, enabled, state);
-              if (state != NULL)
-                g_variant_unref (state);
-            }
-          else
-            gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
-        }
-      else
-        gtk_widget_set_sensitive (GTK_WIDGET (item), FALSE);
-
-      path = _gtk_accel_path_for_action (item->action_name, item->target);
-      gtk_menu_item_set_accel_path (GTK_MENU_ITEM (item), path);
-      g_free (path);
-    }
+  /* XXX accel stuff? */
 }
 
 static void
-gtk_model_menu_item_finalize (GObject *object)
+gtk_model_menu_item_set_has_indicator (GtkModelMenuItem *item,
+                                       gboolean          has_indicator)
 {
-  G_OBJECT_CLASS (gtk_model_menu_item_parent_class)
-    ->finalize (object);
+  if (has_indicator == item->has_indicator)
+    return;
+
+  item->has_indicator = has_indicator;
+
+  gtk_widget_queue_resize (GTK_WIDGET (item));
 }
 
 static void
-gtk_model_menu_item_init (GtkModelMenuItem *item)
+gtk_model_menu_item_set_property (GObject *object, guint prop_id,
+                                  const GValue *value, GParamSpec *pspec)
 {
+  GtkModelMenuItem *item = GTK_MODEL_MENU_ITEM (object);
+  GtkActionHelperRole role;
+  AtkObject *accessible;
+  AtkRole a11y_role;
+
+  g_assert (prop_id == PROP_ACTION_ROLE);
+
+  role = g_value_get_uint (value);
+
+  if (role == item->role)
+    return;
+
+  gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (item), role == GTK_ACTION_HELPER_ROLE_RADIO);
+  gtk_model_menu_item_set_has_indicator (item, role != GTK_ACTION_HELPER_ROLE_NORMAL);
+
+  accessible = gtk_widget_get_accessible (GTK_WIDGET (item));
+  switch (role)
+    {
+    case GTK_ACTION_HELPER_ROLE_NORMAL:
+      a11y_role = ATK_ROLE_MENU_ITEM;
+      break;
+
+    case GTK_ACTION_HELPER_ROLE_TOGGLE:
+      a11y_role = ATK_ROLE_CHECK_MENU_ITEM;
+      break;
+
+    case GTK_ACTION_HELPER_ROLE_RADIO:
+      a11y_role = ATK_ROLE_RADIO_MENU_ITEM;
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  atk_object_set_role (accessible, a11y_role);
 }
 
 static void
-gtk_model_menu_item_observer_iface_init (GActionObserverInterface *iface)
+gtk_model_menu_item_init (GtkModelMenuItem *item)
 {
-  iface->action_added = gtk_model_menu_item_action_added;
-  iface->action_enabled_changed = gtk_model_menu_item_action_enabled_changed;
-  iface->action_state_changed = gtk_model_menu_item_action_state_changed;
-  iface->action_removed = gtk_model_menu_item_action_removed;
 }
 
 static void
@@ -285,23 +168,25 @@ gtk_model_menu_item_class_init (GtkModelMenuItemClass *class)
 
   check_class->draw_indicator = gtk_model_menu_item_draw_indicator;
 
-  item_class->activate = gtk_model_menu_item_activate;
   item_class->toggle_size_request = gtk_model_menu_item_toggle_size_request;
 
-  object_class->finalize = gtk_model_menu_item_finalize;
+  object_class->set_property = gtk_model_menu_item_set_property;
+
+  g_object_class_install_property (object_class, PROP_ACTION_ROLE,
+                                   g_param_spec_uint ("action-role", "action role", "action role",
+                                                      0, 2, 0, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
 }
 
 GtkMenuItem *
 gtk_model_menu_item_new (GMenuModel        *model,
                          gint               item_index,
-                         GActionObservable *actions,
                          GtkAccelGroup     *accels)
 {
   GtkModelMenuItem *item;
 
   item = g_object_new (GTK_TYPE_MODEL_MENU_ITEM, NULL);
 
-  gtk_model_menu_item_setup (item, model, item_index, actions, accels);
+  gtk_model_menu_item_setup (item, model, item_index, accels);
 
   return GTK_MENU_ITEM (item);
 }
diff --git a/gtk/gtkmodelmenuitem.h b/gtk/gtkmodelmenuitem.h
index c129900..8017e3b 100644
--- a/gtk/gtkmodelmenuitem.h
+++ b/gtk/gtkmodelmenuitem.h
@@ -20,7 +20,6 @@
 #ifndef __GTK_MODEL_MENU_ITEM_H__
 #define __GTK_MODEL_MENU_ITEM_H__
 
-#include <gtk/gactionobservable.h>
 #include <gtk/gtkcheckmenuitem.h>
 
 #define GTK_TYPE_MODEL_MENU_ITEM                            (gtk_model_menu_item_get_type ())
@@ -37,7 +36,6 @@ GType                   gtk_model_menu_item_get_type                    (void) G
 G_GNUC_INTERNAL
 GtkMenuItem *           gtk_model_menu_item_new                         (GMenuModel        *model,
                                                                          gint               item_index,
-                                                                         GActionObservable *actions,
                                                                          GtkAccelGroup     *accels);
 
 #endif /* __GTK_MODEL_MENU_ITEM_H__ */
diff --git a/gtk/gtkswitch.c b/gtk/gtkswitch.c
index b3ed707..9b560cb 100644
--- a/gtk/gtkswitch.c
+++ b/gtk/gtkswitch.c
@@ -47,6 +47,7 @@
 #include "gtkapplicationprivate.h"
 #include "gtkactionable.h"
 #include "a11y/gtkswitchaccessible.h"
+#include "gtkactionhelper.h"
 
 #include <math.h>
 
@@ -57,10 +58,7 @@ struct _GtkSwitchPrivate
 {
   GdkWindow *event_window;
   GtkAction *action;
-
-  gchar                 *action_name;
-  GVariant              *action_target;
-  GSimpleActionObserver *action_observer;
+  GtkActionHelper *action_helper;
 
   gint handle_x;
   gint offset;
@@ -95,9 +93,6 @@ static guint signals[LAST_SIGNAL] = { 0 };
 
 static GParamSpec *switch_props[LAST_PROP] = { NULL, };
 
-static void gtk_switch_hierarchy_changed (GtkWidget *widget,
-                                          GtkWidget *previous_toplevel);
- 
 static void gtk_switch_actionable_iface_init (GtkActionableInterface *iface);
 static void gtk_switch_activatable_interface_init (GtkActivatableIface *iface);
 
@@ -675,44 +670,15 @@ gtk_switch_set_use_action_appearance (GtkSwitch *sw,
 }
 
 static void
-gtk_switch_update_action_observer (GtkSwitch *sw)
-{
-  GtkWidget *window;
-
-  /* we are the only owner so this will clear all the signals */
-  g_clear_object (&sw->priv->action_observer);
-
-  window = gtk_widget_get_toplevel (GTK_WIDGET (sw));
-
-  if (GTK_IS_APPLICATION_WINDOW (window) && sw->priv->action_name)
-    {
-      GSimpleActionObserver *observer;
-
-      observer = gtk_application_window_create_observer (GTK_APPLICATION_WINDOW (window),
-                                                         sw->priv->action_name,
-                                                         sw->priv->action_target);
-
-      g_object_bind_property (observer, "active", sw, "active", G_BINDING_SYNC_CREATE);
-      g_object_bind_property (observer, "enabled", sw, "sensitive", G_BINDING_SYNC_CREATE);
-
-      sw->priv->action_observer = observer;
-    }
-}
-
-static void
 gtk_switch_set_action_name (GtkActionable *actionable,
                             const gchar   *action_name)
 {
   GtkSwitch *sw = GTK_SWITCH (actionable);
 
-  g_return_if_fail (GTK_IS_SWITCH (sw));
+  if (!sw->priv->action_helper)
+    sw->priv->action_helper = gtk_action_helper_new (actionable);
 
-  g_free (sw->priv->action_name);
-  sw->priv->action_name = g_strdup (action_name);
-
-  gtk_switch_update_action_observer (sw);
-
-  g_object_notify (G_OBJECT (sw), "action-name");
+  gtk_action_helper_set_action_name (sw->priv->action_helper, action_name);
 }
 
 static void
@@ -721,24 +687,10 @@ gtk_switch_set_action_target_value (GtkActionable *actionable,
 {
   GtkSwitch *sw = GTK_SWITCH (actionable);
 
-  g_return_if_fail (GTK_IS_SWITCH (sw));
-
-  if (action_target != sw->priv->action_target &&
-      (!action_target || !sw->priv->action_target ||
-       !g_variant_equal (action_target, sw->priv->action_target)))
-    {
-      if (sw->priv->action_target)
-        g_variant_unref (sw->priv->action_target);
-
-      sw->priv->action_target = NULL;
-
-      if (action_target)
-        sw->priv->action_target = g_variant_ref_sink (action_target);
+  if (!sw->priv->action_helper)
+    sw->priv->action_helper = gtk_action_helper_new (actionable);
 
-      gtk_switch_update_action_observer (sw);
-
-      g_object_notify (G_OBJECT (sw), "action-target");
-    }
+  gtk_action_helper_set_action_target_value (sw->priv->action_helper, action_target);
 }
 
 static const gchar *
@@ -746,7 +698,7 @@ gtk_switch_get_action_name (GtkActionable *actionable)
 {
   GtkSwitch *sw = GTK_SWITCH (actionable);
 
-  return sw->priv->action_name;
+  return gtk_action_helper_get_action_name (sw->priv->action_helper);
 }
 
 static GVariant *
@@ -754,7 +706,7 @@ gtk_switch_get_action_target_value (GtkActionable *actionable)
 {
   GtkSwitch *sw = GTK_SWITCH (actionable);
 
-  return sw->priv->action_target;
+  return gtk_action_helper_get_action_target_value (sw->priv->action_helper);
 }
 
 static void
@@ -767,28 +719,6 @@ gtk_switch_actionable_iface_init (GtkActionableInterface *iface)
 }
 
 static void
-gtk_switch_hierarchy_changed (GtkWidget *widget,
-                              GtkWidget *previous_toplevel)
-{
-  GtkSwitch *sw = GTK_SWITCH (widget);
-  GtkWidgetClass *parent_class;
-
-  parent_class = GTK_WIDGET_CLASS (gtk_switch_parent_class);
-  if (parent_class->hierarchy_changed)
-    parent_class->hierarchy_changed (widget, previous_toplevel);
-
-  if (sw->priv->action_name)
-    {
-      GtkWidget *toplevel;
-
-      toplevel = gtk_widget_get_toplevel (widget);
-
-      if (toplevel != previous_toplevel)
-        gtk_switch_update_action_observer (sw);
-    }
-}
-
-static void
 gtk_switch_set_property (GObject      *gobject,
                          guint         prop_id,
                          const GValue *value,
@@ -846,11 +776,11 @@ gtk_switch_get_property (GObject    *gobject,
       break;
 
     case PROP_ACTION_NAME:
-      g_value_set_string (value, priv->action_name);
+      g_value_set_string (value, gtk_action_helper_get_action_name (priv->action_helper));
       break;
 
     case PROP_ACTION_TARGET:
-      g_value_set_variant (value, priv->action_target);
+      g_value_set_variant (value, gtk_action_helper_get_action_target_value (priv->action_helper));
       break;
 
     default:
@@ -863,13 +793,7 @@ gtk_switch_dispose (GObject *object)
 {
   GtkSwitchPrivate *priv = GTK_SWITCH (object)->priv;
 
-  g_clear_object (&priv->action_observer);
-
-  if (priv->action_name)
-    {
-      g_free (priv->action_name);
-      priv->action_name = NULL;
-    }
+  g_clear_object (&priv->action_helper);
 
   if (priv->action)
     {
@@ -931,7 +855,6 @@ gtk_switch_class_init (GtkSwitchClass *klass)
   widget_class->motion_notify_event = gtk_switch_motion;
   widget_class->enter_notify_event = gtk_switch_enter;
   widget_class->leave_notify_event = gtk_switch_leave;
-  widget_class->hierarchy_changed = gtk_switch_hierarchy_changed;
 
   klass->activate = gtk_switch_activate;
 
@@ -1026,8 +949,8 @@ gtk_switch_set_active (GtkSwitch *sw,
 
       g_object_notify_by_pspec (G_OBJECT (sw), switch_props[PROP_ACTIVE]);
 
-      if (priv->action_observer)
-        g_simple_action_observer_activate (priv->action_observer);
+      if (priv->action_helper)
+        gtk_action_helper_activate (priv->action_helper);
 
       if (priv->action)
         gtk_action_activate (priv->action);
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 63ad565..669d2b4 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -360,6 +360,9 @@ struct _GtkWidgetPrivate
   guint sizegroup_bumping     : 1;
   guint have_size_groups      : 1;
 
+  /* If any action groups have been requested */
+  guint action_requested      : 1;
+
   /* The widget's name. If the widget does not have a name
    * (the name is NULL), then its name (as returned by
    * "gtk_widget_get_name") is its class's name.
@@ -481,6 +484,7 @@ enum {
   DRAG_FAILED,
   STYLE_UPDATED,
   TOUCH_EVENT,
+  ACTIONS_CHANGED,
   LAST_SIGNAL
 };
 
@@ -857,6 +861,20 @@ gtk_widget_draw_marshallerv (GClosure     *closure,
   cairo_restore (cr);
 }
 
+static GActionGroup *
+gtk_widget_real_get_action_group_by_name (GtkWidget   *widget,
+                                          const gchar *name)
+{
+  GtkWidget *parent;
+
+  parent = gtk_widget_get_parent (widget);
+
+  if (parent == NULL)
+    return NULL;
+
+  return gtk_widget_get_action_group_by_name (parent, name);
+}
+
 static void
 gtk_widget_class_init (GtkWidgetClass *klass)
 {
@@ -977,6 +995,8 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   klass->adjust_size_request = gtk_widget_real_adjust_size_request;
   klass->adjust_size_allocation = gtk_widget_real_adjust_size_allocation;
 
+  klass->get_action_group_by_name = gtk_widget_real_get_action_group_by_name;
+
   g_object_class_install_property (gobject_class,
 				   PROP_NAME,
 				   g_param_spec_string ("name",
@@ -1941,6 +1961,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
   g_signal_set_va_marshaller (widget_signals[TOUCH_EVENT], G_TYPE_FROM_CLASS (klass),
                               _gtk_marshal_BOOLEAN__BOXEDv);
 
+
   /**
    * GtkWidget::scroll-event:
    * @widget: the object which received the signal.
@@ -3102,6 +3123,27 @@ gtk_widget_class_init (GtkWidgetClass *klass)
 		  _gtk_marshal_BOOLEAN__UINT,
                   G_TYPE_BOOLEAN, 1, G_TYPE_UINT);
 
+  /**
+   * GtkWidget::actions-changed:
+   * @widget: the object which received the signal
+   * @action_group_name: (allow none): the name of the affected action
+   *     group, or %NULL in case all groups may have been replaced
+   *
+   * Signals that the action group with the name of @action_group_name
+   * has been potentially added, removed or replaced from the widget
+   * (ie: the value returned by gtk_widget_get_action_group_by_name()
+   * may have changed).  If @action_group_name is %NULL then potentially
+   * all groups have been replaced.
+   *
+   * Since: 3.6
+   **/
+  widget_signals[ACTIONS_CHANGED] =
+    g_signal_new (I_("actions-changed"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+                  g_cclosure_marshal_VOID__STRING,
+                  G_TYPE_NONE, 1, G_TYPE_STRING);
+
   binding_set = gtk_binding_set_by_class (klass);
   gtk_binding_entry_add_signal (binding_set, GDK_KEY_F10, GDK_SHIFT_MASK,
                                 "popup-menu", 0);
@@ -8278,6 +8320,7 @@ gtk_widget_propagate_hierarchy_changed_recurse (GtkWidget *widget,
 
       g_signal_emit (widget, widget_signals[HIERARCHY_CHANGED], 0, info->previous_toplevel);
       do_screen_change (widget, info->previous_screen, info->new_screen);
+      g_signal_emit (widget, widget_signals[ACTIONS_CHANGED], 0, NULL);
 
       if (GTK_IS_CONTAINER (widget))
 	gtk_container_forall (GTK_CONTAINER (widget),
@@ -8420,6 +8463,47 @@ _gtk_widget_propagate_screen_changed (GtkWidget    *widget,
 }
 
 static void
+propagate_actions_changed (GtkWidget *widget,
+                           gpointer   user_data)
+{
+  const gchar *action_group_name = user_data;
+
+  if (!widget->priv->action_requested)
+    return;
+
+  if (GTK_IS_CONTAINER (widget))
+    gtk_container_forall (GTK_CONTAINER (widget), propagate_actions_changed, user_data);
+
+  g_signal_emit (widget, widget_signals[ACTIONS_CHANGED], 0, action_group_name);
+}
+
+/**
+ * gtk_widget_propagate_actions_changed:
+ * @widget: a #GtkWidget
+ * @action_group_name: the name of an action group
+ *
+ * Emits a #GtkWidget::actions-changed signal on @widget and all of its
+ * children, recursively.
+ *
+ * This should be done to signal that the action group with the name of
+ * @action_group_name has been potentially added, removed or replaced
+ * from the widget (ie: the value returned by
+ * gtk_widget_get_action_group_by_name() may have changed).
+ *
+ * Only widget implementations (and specifically, those that provide
+ * their own implementation of gtk_widget_get_action_group_by_name())
+ * should ever call this function.
+ *
+ * Since: 3.6
+ **/
+void
+gtk_widget_propagate_actions_changed (GtkWidget   *widget,
+                                      const gchar *action_group_name)
+{
+  propagate_actions_changed (widget, (gpointer) action_group_name);
+}
+
+static void
 reset_style_recurse (GtkWidget *widget, gpointer data)
 {
   _gtk_widget_invalidate_style_context (widget, GTK_CSS_CHANGE_ANY);
@@ -14062,3 +14146,44 @@ _gtk_widget_set_style (GtkWidget *widget,
 {
   widget->priv->style = style;
 }
+
+/**
+ * gtk_widget_get_action_group_by_name:
+ * @widget: a #GtkWidget
+ * @name: the name of an action group to lookup
+ *
+ * Gets the action group with @name as is applicable to the context of
+ * @widget.
+ *
+ * The default implementation is to ask the same question of the parent
+ * widget, returning %NULL if there is no parent.  In most cases this
+ * means that you are really querying the toplevel associated with
+ * @widget.
+ *
+ * #GtkWindow will return the associated #GtkApplication for @name equal
+ * to "app".  #GtkApplicationWindow will return itself for @name equal
+ * to "win".  #GtkMenu will query its attach-widget.
+ *
+ * Other widgets (including containers between the queried widget and
+ * its toplevel) may return other action groups for other strings (and
+ * may even override the strings "win" and "app").  #GtkMenuButton
+ * provides a method for associating an arbitrary #GActionGroup; see
+ * gtk_menu_button_add_action_group().
+ *
+ * gtk_menubar_new_from_model() and gtk_menu_new_from_model() will call
+ * this function on themselves in order to find the actions specified in
+ * the #GMenuModel.
+ *
+ * Returns: (transfer none): the action group, or %NULL if none exists
+ *
+ * Since: 3.6
+ **/
+GActionGroup *
+gtk_widget_get_action_group_by_name (GtkWidget   *widget,
+                                     const gchar *name)
+{
+  widget->priv->action_requested = TRUE;
+
+  return GTK_WIDGET_GET_CLASS (widget)
+    ->get_action_group_by_name (widget, name);
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 33a54d0..b717347 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -420,13 +420,19 @@ struct _GtkWidgetClass
 
   GtkWidgetClassPrivate *priv;
 
+  /*< public >*/
+
+  GActionGroup * (* get_action_group_by_name) (GtkWidget   *widget,
+                                               const gchar *name);
+
+  /*< private >*/
+
   /* Padding for future expansion */
+  void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
   void (*_gtk_reserved3) (void);
   void (*_gtk_reserved4) (void);
   void (*_gtk_reserved5) (void);
-  void (*_gtk_reserved6) (void);
-  void (*_gtk_reserved7) (void);
 };
 
 struct _GtkWidgetAuxInfo
@@ -886,6 +892,12 @@ GDK_AVAILABLE_IN_3_4
 GdkModifierType   gtk_widget_get_modifier_mask (GtkWidget         *widget,
                                                 GdkModifierIntent  intent);
 
+GDK_AVAILABLE_IN_3_6
+GActionGroup *          gtk_widget_get_action_group_by_name             (GtkWidget   *widget,
+                                                                         const gchar *name);
+GDK_AVAILABLE_IN_3_6
+void                    gtk_widget_propagate_actions_changed            (GtkWidget   *widget,
+                                                                         const gchar *action_group_name);
 
 G_END_DECLS
 
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 9843229..c179cf2 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -549,6 +549,21 @@ startup_id_is_fake (const gchar* startup_id)
   return strncmp (startup_id, "_TIME", 5) == 0;
 }
 
+static GActionGroup *
+gtk_window_get_action_group_by_name (GtkWidget   *widget,
+                                     const gchar *name)
+{
+  if (g_str_equal (name, "app"))
+    {
+      GtkWindow *window = GTK_WINDOW (widget);
+
+      if (window->priv->application)
+        return G_ACTION_GROUP (window->priv->application);
+    }
+
+  return NULL;
+}
+
 static void
 gtk_window_class_init (GtkWindowClass *klass)
 {
@@ -597,6 +612,7 @@ gtk_window_class_init (GtkWindowClass *klass)
   widget_class->direction_changed = gtk_window_direction_changed;
   widget_class->state_changed = gtk_window_state_changed;
   widget_class->style_updated = gtk_window_style_updated;
+  widget_class->get_action_group_by_name = gtk_window_get_action_group_by_name;
 
   container_class->check_resize = gtk_window_check_resize;
 



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