]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkactiongroup.c
Use _gtk_action_emit_activate() instead of directly emitting the activate
[~andy/gtk] / gtk / gtkactiongroup.c
index 69a2c1e355333083a39c538c36b92e67403417a8..f265bd2c298a2007ca3a2e4efaa65426519914cb 100644 (file)
 #include <config.h>
 
 #include "gtkactiongroup.h"
+#include "gtkstock.h"
 #include "gtktoggleaction.h"
 #include "gtkradioaction.h"
 #include "gtkaccelmap.h"
+#include "gtkmarshalers.h"
 #include "gtkintl.h"
 
 #define GTK_ACTION_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ACTION_GROUP, GtkActionGroupPrivate))
@@ -41,6 +43,8 @@
 struct _GtkActionGroupPrivate 
 {
   gchar           *name;
+  gboolean        sensitive;
+  gboolean        visible;
   GHashTable      *actions;
 
   GtkTranslateFunc translate_func;
@@ -48,8 +52,37 @@ struct _GtkActionGroupPrivate
   GtkDestroyNotify translate_notify;   
 };
 
-static void gtk_action_group_init       (GtkActionGroup *self);
-static void gtk_action_group_class_init (GtkActionGroupClass *class);
+enum 
+{
+  CONNECT_PROXY,
+  DISCONNECT_PROXY,
+  PRE_ACTIVATE,
+  POST_ACTIVATE,
+  LAST_SIGNAL
+};
+
+enum 
+{
+  PROP_0,
+  PROP_NAME,
+  PROP_SENSITIVE,
+  PROP_VISIBLE
+};
+
+static void       gtk_action_group_init            (GtkActionGroup      *self);
+static void       gtk_action_group_class_init      (GtkActionGroupClass *class);
+static void       gtk_action_group_finalize        (GObject             *object);
+static void       gtk_action_group_set_property    (GObject             *object,
+                                                   guint                prop_id,
+                                                   const GValue        *value,
+                                                   GParamSpec          *pspec);
+static void       gtk_action_group_get_property    (GObject             *object,
+                                                   guint                prop_id,
+                                                   GValue              *value,
+                                                   GParamSpec          *pspec);
+static GtkAction *gtk_action_group_real_get_action (GtkActionGroup      *self,
+                                                   const gchar         *name);
+
 
 GType
 gtk_action_group_get_type (void)
@@ -79,9 +112,7 @@ gtk_action_group_get_type (void)
 }
 
 static GObjectClass *parent_class = NULL;
-static void       gtk_action_group_finalize        (GObject        *object);
-static GtkAction *gtk_action_group_real_get_action (GtkActionGroup *self,
-                                                   const gchar    *name);
+static guint         action_group_signals[LAST_SIGNAL] = { 0 };
 
 static void
 gtk_action_group_class_init (GtkActionGroupClass *klass)
@@ -92,19 +123,147 @@ gtk_action_group_class_init (GtkActionGroupClass *klass)
   parent_class = g_type_class_peek_parent (klass);
 
   gobject_class->finalize = gtk_action_group_finalize;
+  gobject_class->set_property = gtk_action_group_set_property;
+  gobject_class->get_property = gtk_action_group_get_property;
   klass->get_action = gtk_action_group_real_get_action;
 
+  g_object_class_install_property (gobject_class,
+                                  PROP_NAME,
+                                  g_param_spec_string ("name",
+                                                       P_("Name"),
+                                                       P_("A name for the action group."),
+                                                       NULL,
+                                                       G_PARAM_READWRITE |
+                                                       G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (gobject_class,
+                                  PROP_SENSITIVE,
+                                  g_param_spec_boolean ("sensitive",
+                                                        P_("Sensitive"),
+                                                        P_("Whether the action group is enabled."),
+                                                        TRUE,
+                                                        G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                  PROP_VISIBLE,
+                                  g_param_spec_boolean ("visible",
+                                                        P_("Visible"),
+                                                        P_("Whether the action group is visible."),
+                                                        TRUE,
+                                                        G_PARAM_READWRITE));
+
+  /**
+   * GtkGroupAction::connect-proxy:
+   * @action_group: the group
+   * @action: the action
+   * @proxy: the proxy
+   *
+   * The connect_proxy signal is emitted after connecting a proxy to 
+   * an action in the group. Note that the proxy may have been connected 
+   * to a different action before.
+   *
+   * This is intended for simple customizations for which a custom action
+   * class would be too clumsy, e.g. showing tooltips for menuitems in the
+   * statusbar.
+   *
+   * #GtkUIManager proxies the signal and provides global notification 
+   * just before any action is connected to a proxy, which is probably more
+   * convenient to use.
+   *
+   * Since: 2.4
+   */
+  action_group_signals[CONNECT_PROXY] =
+    g_signal_new ("connect_proxy",
+                 G_OBJECT_CLASS_TYPE (klass),
+                 0, 0, NULL, NULL,
+                 _gtk_marshal_VOID__OBJECT_OBJECT,
+                 G_TYPE_NONE, 2,
+                 GTK_TYPE_ACTION, GTK_TYPE_WIDGET);
+
+  /**
+   * GtkAction::disconnect-proxy:
+   * @action_group: the group
+   * @action: the action
+   * @proxy: the proxy
+   *
+   * The disconnect_proxy signal is emitted after disconnecting a proxy 
+   * from an action in the group. 
+   *
+   * #GtkUIManager proxies the signal and provides global notification 
+   * just before any action is connected to a proxy, which is probably more
+   * convenient to use.
+   *
+   * Since: 2.4
+   */
+  action_group_signals[DISCONNECT_PROXY] =
+    g_signal_new ("disconnect_proxy",
+                 G_OBJECT_CLASS_TYPE (klass),
+                 0, 0, NULL, NULL,
+                 _gtk_marshal_VOID__OBJECT_OBJECT,
+                 G_TYPE_NONE, 2, 
+                 GTK_TYPE_ACTION, GTK_TYPE_WIDGET);
+
+  /**
+   * GtkActionGroup::pre_activate:
+   * @action_group: the group
+   * @action: the action
+   *
+   * The pre_activate signal is emitted just before the @action in the
+   * @action_group is activated
+   *
+   * This is intended for #GtkUIManager to proxy the signal and provide global
+   * notification just before any action is activated.
+   *
+   * Since: 2.4
+   */
+  action_group_signals[PRE_ACTIVATE] =
+    g_signal_new ("pre_activate",
+                 G_OBJECT_CLASS_TYPE (klass),
+                 0, 0, NULL, NULL,
+                 _gtk_marshal_VOID__OBJECT,
+                 G_TYPE_NONE, 1, 
+                 GTK_TYPE_ACTION);
+
+  /**
+   * GtkActionGroup::post_activate:
+   * @action_group: the group
+   * @action: the action
+   *
+   * The post_activate signal is emitted just after the @action in the
+   * @action_group is activated
+   *
+   * This is intended for #GtkUIManager to proxy the signal and provide global
+   * notification just after any action is activated.
+   *
+   * Since: 2.4
+   */
+  action_group_signals[POST_ACTIVATE] =
+    g_signal_new ("post_activate",
+                 G_OBJECT_CLASS_TYPE (klass),
+                 0, 0, NULL, NULL,
+                 _gtk_marshal_VOID__OBJECT,
+                 G_TYPE_NONE, 1, 
+                 GTK_TYPE_ACTION);
+
   g_type_class_add_private (gobject_class, sizeof (GtkActionGroupPrivate));
 }
 
+
+static void 
+remove_action (GtkAction *action) 
+{
+  g_object_set (action, "action_group", NULL, NULL);
+  g_object_unref (action);
+}
+
 static void
 gtk_action_group_init (GtkActionGroup *self)
 {
   self->private_data = GTK_ACTION_GROUP_GET_PRIVATE (self);
   self->private_data->name = NULL;
+  self->private_data->sensitive = TRUE;
+  self->private_data->visible = TRUE;
   self->private_data->actions = g_hash_table_new_full (g_str_hash, g_str_equal,
                                                       (GDestroyNotify) g_free,
-                                                      (GDestroyNotify) g_object_unref);
+                                                      (GDestroyNotify) remove_action);
   self->private_data->translate_func = NULL;
   self->private_data->translate_data = NULL;
   self->private_data->translate_notify = NULL;
@@ -112,9 +271,11 @@ gtk_action_group_init (GtkActionGroup *self)
 
 /**
  * gtk_action_group_new:
- * @name: the name of the action group
+ * @name: the name of the action group.
  *
- * Creates a new #GtkActionGroup object.
+ * Creates a new #GtkActionGroup object. The name of the action group
+ * is used when associating <link linkend="Action-Accel">keybindings</link> 
+ * with the actions.
  *
  * Returns: the new #GtkActionGroup
  *
@@ -151,6 +312,63 @@ gtk_action_group_finalize (GObject *object)
     (* parent_class->finalize) (object);
 }
 
+static void
+gtk_action_group_set_property (GObject         *object,
+                              guint            prop_id,
+                              const GValue    *value,
+                              GParamSpec      *pspec)
+{
+  GtkActionGroup *self;
+  gchar *tmp;
+  
+  self = GTK_ACTION_GROUP (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      tmp = self->private_data->name;
+      self->private_data->name = g_value_dup_string (value);
+      g_free (tmp);
+      break;
+    case PROP_SENSITIVE:
+      gtk_action_group_set_sensitive (self, g_value_get_boolean (value));
+      break;
+    case PROP_VISIBLE:
+      gtk_action_group_set_visible (self, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_action_group_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  GtkActionGroup *self;
+  
+  self = GTK_ACTION_GROUP (object);
+
+  switch (prop_id)
+    {
+    case PROP_NAME:
+      g_value_set_string (value, self->private_data->name);
+      break;
+    case PROP_SENSITIVE:
+      g_value_set_boolean (value, self->private_data->sensitive);
+      break;
+    case PROP_VISIBLE:
+      g_value_set_boolean (value, self->private_data->visible);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
 static GtkAction *
 gtk_action_group_real_get_action (GtkActionGroup *self,
                                  const gchar    *action_name)
@@ -176,6 +394,110 @@ gtk_action_group_get_name (GtkActionGroup *action_group)
   return action_group->private_data->name;
 }
 
+/**
+ * gtk_action_group_get_sensitive:
+ * @action_group: the action group
+ *
+ * Returns %TRUE if the group is sensitive.  The constituent actions
+ * can only be logically sensitive (see gtk_action_is_sensitive()) if
+ * they are sensitive (see gtk_action_get_sensitive()) and their group
+ * is sensitive.
+ * 
+ * Return value: %TRUE if the group is sensitive.
+ *
+ * Since: 2.4
+ */
+gboolean
+gtk_action_group_get_sensitive (GtkActionGroup *action_group)
+{
+  g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), FALSE);
+
+  return action_group->private_data->sensitive;
+}
+
+static void
+cb_set_action_sensitivity (const gchar *name, GtkAction *action)
+{
+  /* Minor optimization, the action_groups state only effects actions that are
+   * themselves sensitive */
+  if (gtk_action_get_sensitive (action))
+    g_object_notify (G_OBJECT (action), "sensitive");
+}
+
+/**
+ * gtk_action_group_set_sensitive:
+ * @action_group: the action group
+ * @sensitive: new sensitivity
+ *
+ * Changes the sensitivity of @action_group
+ * 
+ * Since: 2.4
+ */
+void
+gtk_action_group_set_sensitive (GtkActionGroup *action_group, gboolean sensitive)
+{
+  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
+
+  if (action_group->private_data->sensitive ^ sensitive)
+    {
+      action_group->private_data->sensitive = sensitive;
+      g_hash_table_foreach (action_group->private_data->actions, 
+                           (GHFunc) cb_set_action_sensitivity, NULL);
+    }
+}
+
+/**
+ * gtk_action_group_get_visible:
+ * @action_group: the action group
+ *
+ * Returns %TRUE if the group is visible.  The constituent actions
+ * can only be logically visible (see gtk_action_is_visible()) if
+ * they are visible (see gtk_action_get_visible()) and their group
+ * is visible.
+ * 
+ * Return value: %TRUE if the group is sensitive.
+ * 
+ * Since: 2.4
+ */
+gboolean
+gtk_action_group_get_visible (GtkActionGroup *action_group)
+{
+  g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), FALSE);
+
+  return action_group->private_data->visible;
+}
+
+static void
+cb_set_action_visiblity (const gchar *name, GtkAction *action)
+{
+  /* Minor optimization, the action_groups state only effects actions that are
+   * themselves sensitive */
+  if (gtk_action_get_visible (action))
+    g_object_notify (G_OBJECT (action), "visible");
+}
+
+/**
+ * gtk_action_group_set_visible:
+ * @action_group: the action group
+ * @visible: new visiblity
+ *
+ * Changes the visible of @action_group.
+ * 
+ * Since: 2.4
+ */
+void
+gtk_action_group_set_visible (GtkActionGroup *action_group, gboolean visible)
+{
+  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
+
+  if (action_group->private_data->visible ^ visible)
+    {
+      action_group->private_data->visible = visible;
+      g_hash_table_foreach (action_group->private_data->actions, 
+                           (GHFunc) cb_set_action_visiblity, NULL);
+    }
+}
+
 /**
  * gtk_action_group_get_action:
  * @action_group: the action group
@@ -203,7 +525,12 @@ gtk_action_group_get_action (GtkActionGroup *action_group,
  * @action_group: the action group
  * @action: an action
  *
- * Adds an action object to the action group.
+ * Adds an action object to the action group. Note that this function
+ * does not set up the accel path of the action, which can lead to problems
+ * if a user tries to modify the accelerator of a menuitem associated with
+ * the action. Therefore you must either set the accel path yourself with
+ * gtk_action_set_accel_path(), or use 
+ * <literal>gtk_action_group_add_action_with_accel (..., NULL)</literal>.
  *
  * Since: 2.4
  */
@@ -218,10 +545,70 @@ gtk_action_group_add_action (GtkActionGroup *action_group,
   g_hash_table_insert (action_group->private_data->actions, 
                       g_strdup (gtk_action_get_name (action)),
                        g_object_ref (action));
+  g_object_set (G_OBJECT (action), "action_group", action_group, NULL);
+}
+
+/**
+ * gtk_action_group_add_action_with_accel:
+ * @action_group: the action group 
+ * @action: the action to add 
+ * @accelerator: the accelerator for the action, in
+ *   the format understood by gtk_accelerator_parse(), or %NULL to use the
+ *   stock accelerator 
+ *
+ * Adds an action object to the action group and sets up the accelerator.
+ *
+ * If @accelerator is %NULL, attempts to use the accelerator associated 
+ * with the stock_id of the action.
+ *
+ * Accel paths are set to
+ * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.
+ *
+ * Since: 2.4
+ */
+void
+gtk_action_group_add_action_with_accel (GtkActionGroup *action_group,
+                                       GtkAction *action,
+                                       const gchar *accelerator)
+{
+  gchar *accel_path;
+  guint  accel_key = 0;
+  GdkModifierType accel_mods;
+  GtkStockItem stock_item;
+  gchar *name;
+  gchar *stock_id;
+  
+  g_object_get (action, "name", &name, "stock_id", &stock_id, NULL);
+
+  accel_path = g_strconcat ("<Actions>/",
+                           action_group->private_data->name, "/", name, NULL);
+
+  if (accelerator)
+    {
+    gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
+      if (accel_key == 0)
+       g_warning ("Unable to parse accelerator '%s' for action '%s'",
+                  accelerator, name);
+    }
+  else if (stock_id && gtk_stock_lookup (stock_id, &stock_item))
+    {
+      accel_key = stock_item.keyval;
+      accel_mods = stock_item.modifier;
+    }
+
+  if (accel_key)
+    gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
+
+  gtk_action_set_accel_path (action, accel_path);
+  gtk_action_group_add_action (action_group, action);
+
+  g_free (accel_path);
+  g_free (stock_id);
+  g_free (name);
 }
 
 /**
- * gtk_action_group_removes_action:
+ * gtk_action_group_remove_action:
  * @action_group: the action group
  * @action: an action
  *
@@ -280,45 +667,96 @@ gtk_action_group_list_actions (GtkActionGroup *action_group)
  * @action_group: the action group
  * @entries: an array of action descriptions
  * @n_entries: the number of entries
+ * @user_data: data to pass to the action callbacks
  *
- * This is a convenience routine to create a number of actions and add
- * them to the action group.  Each member of the array describes an
- * action to create.
+ * This is a convenience function to create a number of actions and add them 
+ * to the action group.
+ *
+ * The "activate" signals of the actions are connected to the callbacks and 
+ * their accel paths are set to 
+ * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
  * 
  * Since: 2.4
  */
 void
-gtk_action_group_add_actions (GtkActionGroup      *action_group,
-                             GtkActionGroupEntry *entries,
-                             guint                n_entries)
+gtk_action_group_add_actions (GtkActionGroup *action_group,
+                             GtkActionEntry *entries,
+                             guint           n_entries,
+                             gpointer        user_data)
+{
+  gtk_action_group_add_actions_full (action_group, 
+                                    entries, n_entries, 
+                                    user_data, NULL);
+}
+
+typedef struct _SharedData  SharedData;
+
+struct _SharedData {
+  guint          ref_count;
+  gpointer       data;
+  GDestroyNotify destroy;
+};
+
+static void
+shared_data_unref (gpointer data)
 {
+  SharedData *shared_data = (SharedData *)data;
+
+  shared_data->ref_count--;
+  if (shared_data->ref_count == 0)
+    {
+      if (shared_data->destroy) 
+       (*shared_data->destroy) (shared_data->data);
+      
+      g_free (shared_data);
+    }
+}
+
+
+/**
+ * gtk_action_group_add_actions_full:
+ * @action_group: the action group
+ * @entries: an array of action descriptions
+ * @n_entries: the number of entries
+ * @user_data: data to pass to the action callbacks
+ * @destroy: destroy notification callback for @user_data
+ *
+ * This variant of gtk_action_group_add_actions() adds a #GDestroyNotify
+ * callback for @user_data. 
+ * 
+ * Since: 2.4
+ */
+void
+gtk_action_group_add_actions_full (GtkActionGroup *action_group,
+                                  GtkActionEntry *entries,
+                                  guint           n_entries,
+                                  gpointer        user_data,
+                                  GDestroyNotify  destroy)
+{
+
+  /* Keep this in sync with the other 
+   * gtk_action_group_add_..._actions_full() functions.
+   */
   guint i;
+  GtkTranslateFunc translate_func;
+  gpointer translate_data;
+  SharedData *shared_data;
+
   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
-  GtkTranslateFunc translate_func = action_group->private_data->translate_func;
-  gpointer translate_data = action_group->private_data->translate_data;
+
+  translate_func = action_group->private_data->translate_func;
+  translate_data = action_group->private_data->translate_data;
+
+  shared_data = g_new0 (SharedData, 1);
+  shared_data->ref_count = 1;
+  shared_data->data = user_data;
+  shared_data->destroy = destroy;
 
   for (i = 0; i < n_entries; i++)
     {
       GtkAction *action;
-      GType action_type;
-      gchar *accel_path;
-      gchar *label;
-      gchar *tooltip;
-
-      switch (entries[i].entry_type) {
-      case GTK_ACTION_NORMAL:
-       action_type = GTK_TYPE_ACTION;
-       break;
-      case GTK_ACTION_TOGGLE:
-       action_type = GTK_TYPE_TOGGLE_ACTION;
-       break;
-      case GTK_ACTION_RADIO:
-       action_type = GTK_TYPE_RADIO_ACTION;
-       break;
-      default:
-       g_warning ("unsupported action type");
-       action_type = GTK_TYPE_ACTION;
-      }
+      const gchar *label;
+      const gchar *tooltip;
 
       if (translate_func)
        {
@@ -331,55 +769,257 @@ gtk_action_group_add_actions (GtkActionGroup      *action_group,
          tooltip = entries[i].tooltip;
        }
 
-      action = g_object_new (action_type,
-                            "name", entries[i].name,
-                            "label", label,
-                            "tooltip", tooltip,
-                            "stock_id", entries[i].stock_id,
-                            NULL);
+      action = gtk_action_new (entries[i].name,
+                              label,
+                              tooltip,
+                              entries[i].stock_id);
 
-      if (entries[i].entry_type == GTK_ACTION_RADIO &&
-         entries[i].extra_data != NULL)
+      if (entries[i].callback)
        {
-         GtkAction *radio_action;
-         GSList *group;
-
-         radio_action =
-           gtk_action_group_get_action (GTK_ACTION_GROUP (action_group),
-                                        entries[i].extra_data);
-         if (radio_action)
-           {
-             group = gtk_radio_action_get_group (GTK_RADIO_ACTION (radio_action));
-             gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
-           }
-         else
-           g_warning (G_STRLOC " could not look up `%s'", entries[i].extra_data);
+         GClosure *closure;
+
+         closure = g_cclosure_new (entries[i].callback, user_data, NULL);
+         g_closure_add_finalize_notifier (closure, shared_data, 
+                                          (GClosureNotify)shared_data_unref);
+         shared_data->ref_count++;
+
+         g_signal_connect_closure (action, "activate", closure, FALSE);
        }
+         
+      gtk_action_group_add_action_with_accel (action_group, 
+                                             action,
+                                             entries[i].accelerator);
+      g_object_unref (action);
+    }
 
-      if (entries[i].callback)
-       g_signal_connect (action, "activate",
-                         entries[i].callback, entries[i].user_data);
+  shared_data_unref (shared_data);
+}
+
+/**
+ * gtk_action_group_add_toggle_actions:
+ * @action_group: the action group
+ * @entries: an array of toggle action descriptions
+ * @n_entries: the number of entries
+ * @user_data: data to pass to the action callbacks
+ *
+ * This is a convenience function to create a number of toggle actions and add them 
+ * to the action group.
+ *
+ * The "activate" signals of the actions are connected to the callbacks and 
+ * their accel paths are set to 
+ * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
+ * 
+ * Since: 2.4
+ */
+void
+gtk_action_group_add_toggle_actions (GtkActionGroup       *action_group,
+                                    GtkToggleActionEntry *entries,
+                                    guint                 n_entries,
+                                    gpointer              user_data)
+{
+  gtk_action_group_add_toggle_actions_full (action_group, 
+                                           entries, n_entries, 
+                                           user_data, NULL);
+}
 
-      /* set the accel path for the menu item */
-      accel_path = g_strconcat ("<Actions>/", action_group->private_data->name, "/",
-                               entries[i].name, NULL);
-      if (entries[i].accelerator)
+
+/**
+ * gtk_action_group_add_toggle_actions_full:
+ * @action_group: the action group
+ * @entries: an array of toggle action descriptions
+ * @n_entries: the number of entries
+ * @user_data: data to pass to the action callbacks
+ * @destroy: destroy notification callback for @user_data
+ *
+ * This variant of gtk_action_group_add_toggle_actions() adds a 
+ * #GDestroyNotify callback for @user_data. 
+ * 
+ * Since: 2.4
+ */
+void
+gtk_action_group_add_toggle_actions_full (GtkActionGroup       *action_group,
+                                         GtkToggleActionEntry *entries,
+                                         guint                 n_entries,
+                                         gpointer              user_data,
+                                         GDestroyNotify        destroy)
+{
+  /* Keep this in sync with the other 
+   * gtk_action_group_add_..._actions_full() functions.
+   */
+  guint i;
+  GtkTranslateFunc translate_func;
+  gpointer translate_data;
+  SharedData *shared_data;
+
+  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
+
+  translate_func = action_group->private_data->translate_func;
+  translate_data = action_group->private_data->translate_data;
+
+  shared_data = g_new0 (SharedData, 1);
+  shared_data->ref_count = 1;
+  shared_data->data = user_data;
+  shared_data->destroy = destroy;
+
+  for (i = 0; i < n_entries; i++)
+    {
+      GtkToggleAction *action;
+      const gchar *label;
+      const gchar *tooltip;
+
+      if (translate_func)
+       {
+         label = translate_func (entries[i].label, translate_data);
+         tooltip = translate_func (entries[i].tooltip, translate_data);
+       }
+      else
        {
-         guint accel_key = 0;
-         GdkModifierType accel_mods;
+         label = entries[i].label;
+         tooltip = entries[i].tooltip;
+       }
 
-         gtk_accelerator_parse (entries[i].accelerator, &accel_key,
-                                &accel_mods);
-         if (accel_key)
-           gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
+      action = gtk_toggle_action_new (entries[i].name,
+                                     label,
+                                     tooltip,
+                                     entries[i].stock_id);
+
+      gtk_toggle_action_set_active (action, entries[i].is_active);
+
+      if (entries[i].callback)
+       {
+         GClosure *closure;
+
+         closure = g_cclosure_new (entries[i].callback, user_data, NULL);
+         g_closure_add_finalize_notifier (closure, shared_data, 
+                                          (GClosureNotify)shared_data_unref);
+         shared_data->ref_count++;
+
+         g_signal_connect_closure (action, "activate", closure, FALSE);
        }
+         
+      gtk_action_group_add_action_with_accel (action_group, 
+                                             GTK_ACTION (action),
+                                             entries[i].accelerator);
+      g_object_unref (action);
+    }
+
+    shared_data_unref (shared_data);
+}
+
+/**
+ * gtk_action_group_add_radio_actions:
+ * @action_group: the action group
+ * @entries: an array of radio action descriptions
+ * @n_entries: the number of entries
+ * @value: the value of the action to activate initially, or -1 if
+ *   no action should be activated
+ * @on_change: the callback to connect to the changed signal
+ * @user_data: data to pass to the action callbacks
+ * 
+ * This is a convenience routine to create a group of radio actions and
+ * add them to the action group. 
+ *
+ * The "changed" signal of the first radio action is connected to the 
+ * @on_change callback and the accel paths of the actions are set to 
+ * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
+ * 
+ * Since: 2.4
+ **/
+void            
+gtk_action_group_add_radio_actions (GtkActionGroup      *action_group,
+                                   GtkRadioActionEntry *entries,
+                                   guint                n_entries,
+                                   gint                 value,
+                                   GCallback            on_change,
+                                   gpointer             user_data)
+{
+  gtk_action_group_add_radio_actions_full (action_group, 
+                                          entries, n_entries, 
+                                          value,
+                                          on_change, user_data, NULL);
+}
 
-      gtk_action_set_accel_path (action, accel_path);
-      g_free (accel_path);
+/**
+ * gtk_action_group_add_radio_actions_full:
+ * @action_group: the action group
+ * @entries: an array of radio action descriptions
+ * @n_entries: the number of entries
+ * @value: the value of the action to activate initially, or -1 if
+ *   no action should be activated
+ * @on_change: the callback to connect to the changed signal
+ * @user_data: data to pass to the action callbacks
+ * @destroy: destroy notification callback for @user_data
+ *
+ * This variant of gtk_action_group_add_radio_actions() adds a 
+ * #GDestroyNotify callback for @user_data. 
+ * 
+ * Since: 2.4
+ **/
+void            
+gtk_action_group_add_radio_actions_full (GtkActionGroup      *action_group,
+                                        GtkRadioActionEntry *entries,
+                                        guint                n_entries,
+                                        gint                 value,
+                                        GCallback            on_change,
+                                        gpointer             user_data,
+                                        GDestroyNotify       destroy)
+{
+  /* Keep this in sync with the other 
+   * gtk_action_group_add_..._actions_full() functions.
+   */
+  guint i;
+  GtkTranslateFunc translate_func;
+  gpointer translate_data;
+  GSList *group = NULL;
+  GtkRadioAction *first_action = NULL;
+
+  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
+
+  translate_func = action_group->private_data->translate_func;
+  translate_data = action_group->private_data->translate_data;
+
+  for (i = 0; i < n_entries; i++)
+    {
+      GtkRadioAction *action;
+      const gchar *label;
+      const gchar *tooltip; 
 
-      gtk_action_group_add_action (action_group, action);
+      if (translate_func)
+       {
+         label = translate_func (entries[i].label, translate_data);
+         tooltip = translate_func (entries[i].tooltip, translate_data);
+       }
+      else
+       {
+         label = entries[i].label;
+         tooltip = entries[i].tooltip;
+       }
+
+      action = gtk_radio_action_new (entries[i].name,
+                                    label,
+                                    tooltip,
+                                    entries[i].stock_id,
+                                    entries[i].value);
+
+      if (i == 0) 
+       first_action = action;
+
+      gtk_radio_action_set_group (action, group);
+      group = gtk_radio_action_get_group (action);
+
+      if (value == entries[i].value)
+       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+
+      gtk_action_group_add_action_with_accel (action_group, 
+                                             GTK_ACTION (action),
+                                             entries[i].accelerator);
       g_object_unref (action);
     }
+
+  if (on_change && first_action)
+    g_signal_connect_data (first_action, "changed",
+                          on_change, user_data, 
+                          (GClosureNotify)destroy, 0);
 }
 
 /**
@@ -427,7 +1067,7 @@ dgettext_swapped (const gchar *msgid,
  * @domain: the translation domain to use for dgettext() calls
  * 
  * Sets the translation domain and uses dgettext() for translating the 
- * @label and @tooltip of #GtkActionGroupEntry<!-- -->s added by 
+ * @label and @tooltip of #GtkActionEntry<!-- -->s added by 
  * gtk_action_group_add_actions().
  *
  * If you're not using gettext() for localization, see 
@@ -442,7 +1082,40 @@ gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
 
   gtk_action_group_set_translate_func (action_group, 
-                                      dgettext_swapped,
+                                      (GtkTranslateFunc)dgettext_swapped,
                                       g_strdup (domain),
                                       g_free);
 } 
+
+/* Protected for use by GtkAction */
+void
+_gtk_action_group_emit_connect_proxy  (GtkActionGroup *action_group,
+                                       GtkAction      *action,
+                                       GtkWidget      *proxy)
+{
+  g_signal_emit (action_group, action_group_signals[CONNECT_PROXY], 0, 
+                 action, proxy);
+}
+
+void
+_gtk_action_group_emit_disconnect_proxy  (GtkActionGroup *action_group,
+                                          GtkAction      *action,
+                                          GtkWidget      *proxy)
+{
+  g_signal_emit (action_group, action_group_signals[DISCONNECT_PROXY], 0, 
+                 action, proxy);
+}
+
+void
+_gtk_action_group_emit_pre_activate  (GtkActionGroup *action_group,
+                                     GtkAction      *action)
+{
+  g_signal_emit (action_group, action_group_signals[PRE_ACTIVATE], 0, action);
+}
+
+void
+_gtk_action_group_emit_post_activate (GtkActionGroup *action_group,
+                                     GtkAction *action)
+{
+  g_signal_emit (action_group, action_group_signals[POST_ACTIVATE], 0, action);
+}