]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkaction.c
Disconnect the sync callback for the visibility property. (#321761, Philip
[~andy/gtk] / gtk / gtkaction.c
index ff5a718036486ad8ea30ee9bd645b4a901fe26d6..45611d34b418cf065e11b6876038738bc3ce4f1c 100644 (file)
 #include <config.h>
 
 #include "gtkaction.h"
+#include "gtkactiongroup.h"
+#include "gtkaccellabel.h"
 #include "gtkbutton.h"
-#include "gtktoolbutton.h"
-#include "gtkmenuitem.h"
-#include "gtkimagemenuitem.h"
-#include "gtkstock.h"
-#include "gtklabel.h"
 #include "gtkimage.h"
-#include "gtkaccellabel.h"
+#include "gtkimagemenuitem.h"
 #include "gtkintl.h"
+#include "gtklabel.h"
+#include "gtkmarshalers.h"
+#include "gtkmenuitem.h"
+#include "gtkstock.h"
+#include "gtktearoffmenuitem.h"
+#include "gtktoolbutton.h"
+#include "gtktoolbar.h"
+#include "gtkprivate.h"
+#include "gtkalias.h"
 
 
 #define GTK_ACTION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ACTION, GtkActionPrivate))
@@ -52,14 +58,23 @@ struct _GtkActionPrivate
   gchar *tooltip;
   gchar *stock_id; /* icon */
 
-  guint sensitive       : 1;
-  guint visible         : 1;
-  guint label_set       : 1; /* these two used so we can set label */
-  guint short_label_set : 1; /* based on stock id */
-  guint is_important    : 1;
+  guint sensitive          : 1;
+  guint visible            : 1;
+  guint label_set          : 1; /* these two used so we can set label */
+  guint short_label_set    : 1; /* based on stock id */
+  guint visible_horizontal : 1;
+  guint visible_vertical   : 1;
+  guint is_important       : 1;
+  guint hide_if_empty      : 1;
+  guint visible_overflown  : 1;
 
   /* accelerator */
-  GQuark accel_quark;
+  guint          accel_count;
+  GtkAccelGroup *accel_group;
+  GClosure      *accel_closure;
+  GQuark         accel_quark;
+
+  GtkActionGroup *action_group;
 
   /* list of proxy widgets */
   GSList *proxies;
@@ -79,16 +94,21 @@ enum
   PROP_SHORT_LABEL,
   PROP_TOOLTIP,
   PROP_STOCK_ID,
+  PROP_VISIBLE_HORIZONTAL,
+  PROP_VISIBLE_VERTICAL,
+  PROP_VISIBLE_OVERFLOWN,
   PROP_IS_IMPORTANT,
+  PROP_HIDE_IF_EMPTY,
   PROP_SENSITIVE,
   PROP_VISIBLE,
+  PROP_ACTION_GROUP
 };
 
 static void gtk_action_init       (GtkAction *action);
 static void gtk_action_class_init (GtkActionClass *class);
 
 static GQuark       accel_path_id  = 0;
-static const gchar *accel_path_key = "GtkAction::accel_path";
+static const gchar accel_path_key[] = "GtkAction::accel_path";
 
 GType
 gtk_action_get_type (void)
@@ -112,7 +132,7 @@ gtk_action_get_type (void)
       };
 
       type = g_type_register_static (G_TYPE_OBJECT,
-                                    "GtkAction",
+                                    I_("GtkAction"),
                                     &type_info, 0);
     }
   return type;
@@ -127,13 +147,21 @@ static void gtk_action_get_property (GObject         *object,
                                     guint            prop_id,
                                     GValue          *value,
                                     GParamSpec      *pspec);
+static void gtk_action_set_action_group (GtkAction         *action,
+                                        GtkActionGroup *action_group);
 
 static GtkWidget *create_menu_item    (GtkAction *action);
 static GtkWidget *create_tool_item    (GtkAction *action);
-static void       connect_proxy       (GtkAction *action,
-                                      GtkWidget *proxy);
+static void       connect_proxy       (GtkAction     *action,
+                                      GtkWidget     *proxy);
 static void       disconnect_proxy    (GtkAction *action,
                                       GtkWidget *proxy);
+static void       closure_accel_activate (GClosure     *closure,
+                                         GValue       *return_value,
+                                         guint         n_param_values,
+                                         const GValue *param_values,
+                                         gpointer      invocation_hint,
+                                         gpointer      marshal_data);
 
 static GObjectClass *parent_class = NULL;
 static guint         action_signals[LAST_SIGNAL] = { 0 };
@@ -166,61 +194,110 @@ gtk_action_class_init (GtkActionClass *klass)
   g_object_class_install_property (gobject_class,
                                   PROP_NAME,
                                   g_param_spec_string ("name",
-                                                       _("Name"),
-                                                       _("A unique name for the action."),
+                                                       P_("Name"),
+                                                       P_("A unique name for the action."),
                                                        NULL,
-                                                       G_PARAM_READWRITE |
+                                                       GTK_PARAM_READWRITE | 
                                                        G_PARAM_CONSTRUCT_ONLY));
   g_object_class_install_property (gobject_class,
                                   PROP_LABEL,
                                   g_param_spec_string ("label",
-                                                       _("Label"),
-                                                       _("The label used for menu items and buttons that activate this action."),
+                                                       P_("Label"),
+                                                       P_("The label used for menu items and buttons "
+                                                          "that activate this action."),
                                                        NULL,
-                                                       G_PARAM_READWRITE));
+                                                       GTK_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
                                   PROP_SHORT_LABEL,
-                                  g_param_spec_string ("short_label",
-                                                       _("Short label"),
-                                                       _("A shorter label that may be used on toolbar buttons."),
+                                  g_param_spec_string ("short-label",
+                                                       P_("Short label"),
+                                                       P_("A shorter label that may be used on toolbar buttons."),
                                                        NULL,
-                                                       G_PARAM_READWRITE));
+                                                       GTK_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
                                   PROP_TOOLTIP,
                                   g_param_spec_string ("tooltip",
-                                                       _("Tooltip"),
-                                                       _("A tooltip for this action."),
+                                                       P_("Tooltip"),
+                                                       P_("A tooltip for this action."),
                                                        NULL,
-                                                       G_PARAM_READWRITE));
+                                                       GTK_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
                                   PROP_STOCK_ID,
-                                  g_param_spec_string ("stock_id",
-                                                       _("Stock Icon"),
-                                                       _("The stock icon displayed in widgets representing this action."),
+                                  g_param_spec_string ("stock-id",
+                                                       P_("Stock Icon"),
+                                                       P_("The stock icon displayed in widgets representing "
+                                                          "this action."),
                                                        NULL,
-                                                       G_PARAM_READWRITE));
+                                                       GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                  PROP_VISIBLE_HORIZONTAL,
+                                  g_param_spec_boolean ("visible-horizontal",
+                                                        P_("Visible when horizontal"),
+                                                        P_("Whether the toolbar item is visible when the toolbar "
+                                                           "is in a horizontal orientation."),
+                                                        TRUE,
+                                                        GTK_PARAM_READWRITE));
+  /**
+   * GtkAction:visible-overflown:
+   *
+   * When %TRUE, toolitem proxies for this action are represented in the 
+   * toolbar overflow menu.
+   *
+   * Since: 2.6
+   */
+  g_object_class_install_property (gobject_class,
+                                  PROP_VISIBLE_OVERFLOWN,
+                                  g_param_spec_boolean ("visible-overflown",
+                                                        P_("Visible when overflown"),
+                                                        P_("When TRUE, toolitem proxies for this action "
+                                                           "are represented in the toolbar overflow menu."),
+                                                        TRUE,
+                                                        GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                  PROP_VISIBLE_VERTICAL,
+                                  g_param_spec_boolean ("visible-vertical",
+                                                        P_("Visible when vertical"),
+                                                        P_("Whether the toolbar item is visible when the toolbar "
+                                                           "is in a vertical orientation."),
+                                                        TRUE,
+                                                        GTK_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
                                   PROP_IS_IMPORTANT,
-                                  g_param_spec_boolean ("is_important",
-                                                        _("Is important"),
-                                                        _("Whether the action is considered important. When TRUE, toolitem proxies for this action show text in GTK_TOOLBAR_BOTH_HORIZ mode"),
+                                  g_param_spec_boolean ("is-important",
+                                                        P_("Is important"),
+                                                        P_("Whether the action is considered important. "
+                                                           "When TRUE, toolitem proxies for this action "
+                                                           "show text in GTK_TOOLBAR_BOTH_HORIZ mode."),
                                                         FALSE,
-                                                        G_PARAM_READWRITE));
+                                                        GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                  PROP_HIDE_IF_EMPTY,
+                                  g_param_spec_boolean ("hide-if-empty",
+                                                        P_("Hide if empty"),
+                                                        P_("When TRUE, empty menu proxies for this action are hidden."),
+                                                        TRUE,
+                                                        GTK_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
                                   PROP_SENSITIVE,
                                   g_param_spec_boolean ("sensitive",
-                                                        _("Sensitive"),
-                                                        _("Whether the action is enabled."),
+                                                        P_("Sensitive"),
+                                                        P_("Whether the action is enabled."),
                                                         TRUE,
-                                                        G_PARAM_READWRITE));
+                                                        GTK_PARAM_READWRITE));
   g_object_class_install_property (gobject_class,
                                   PROP_VISIBLE,
                                   g_param_spec_boolean ("visible",
-                                                        _("Visible"),
-                                                        _("Whether the action is visible."),
+                                                        P_("Visible"),
+                                                        P_("Whether the action is visible."),
                                                         TRUE,
-                                                        G_PARAM_READWRITE));
-
+                                                        GTK_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class,
+                                  PROP_ACTION_GROUP,
+                                  g_param_spec_object ("action-group",
+                                                        P_("Action Group"),
+                                                        P_("The GtkActionGroup this GtkAction is associated with, or NULL (for internal use)."),
+                                                        GTK_TYPE_ACTION_GROUP,
+                                                        GTK_PARAM_READWRITE));
 
   /**
    * GtkAction::activate:
@@ -231,7 +308,7 @@ gtk_action_class_init (GtkActionClass *klass)
    * Since: 2.4
    */
   action_signals[ACTIVATE] =
-    g_signal_new ("activate",
+    g_signal_new (I_("activate"),
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
                  G_STRUCT_OFFSET (GtkActionClass, activate),  NULL, NULL,
@@ -252,7 +329,11 @@ gtk_action_init (GtkAction *action)
   action->private_data->short_label = NULL;
   action->private_data->tooltip = NULL;
   action->private_data->stock_id = NULL;
+  action->private_data->visible_horizontal = TRUE;
+  action->private_data->visible_vertical   = TRUE;
+  action->private_data->visible_overflown  = TRUE;
   action->private_data->is_important = FALSE;
+  action->private_data->hide_if_empty = TRUE;
 
   action->private_data->sensitive = TRUE;
   action->private_data->visible = TRUE;
@@ -260,16 +341,60 @@ gtk_action_init (GtkAction *action)
   action->private_data->label_set = FALSE;
   action->private_data->short_label_set = FALSE;
 
+  action->private_data->accel_count = 0;
+  action->private_data->accel_group = NULL;
   action->private_data->accel_quark = 0;
+  action->private_data->accel_closure = 
+    g_closure_new_object (sizeof (GClosure), G_OBJECT (action));
+  g_closure_set_marshal (action->private_data->accel_closure, 
+                        closure_accel_activate);
+  g_closure_ref (action->private_data->accel_closure);
+  g_closure_sink (action->private_data->accel_closure);
+
+  action->private_data->action_group = NULL;
 
   action->private_data->proxies = NULL;
 }
 
+/**
+ * gtk_action_new:
+ * @name: A unique name for the action
+ * @label: the label displayed in menu items and on buttons
+ * @tooltip: a tooltip for the action
+ * @stock_id: the stock icon to display in widgets representing the action
+ *
+ * Creates a new #GtkAction object. To add the action to a
+ * #GtkActionGroup and set the accelerator for the action,
+ * call gtk_action_group_add_action_with_accel().
+ * See <xref linkend="XML-UI"/> for information on allowed action
+ * names.
+ *
+ * Return value: a new #GtkAction
+ *
+ * Since: 2.4
+ */
+GtkAction *
+gtk_action_new (const gchar *name,
+               const gchar *label,
+               const gchar *tooltip,
+               const gchar *stock_id)
+{
+  GtkAction *action;
+
+  action = g_object_new (GTK_TYPE_ACTION,
+                        "name", name,
+                        "label", label,
+                        "tooltip", tooltip,
+                        "stock_id", stock_id,
+                        NULL);
+
+  return action;
+}
+
 static void
 gtk_action_finalize (GObject *object)
 {
   GtkAction *action;
-
   action = GTK_ACTION (object);
 
   g_free (action->private_data->name);
@@ -277,6 +402,12 @@ gtk_action_finalize (GObject *object)
   g_free (action->private_data->short_label);
   g_free (action->private_data->tooltip);
   g_free (action->private_data->stock_id);
+
+  g_closure_unref (action->private_data->accel_closure);
+  if (action->private_data->accel_group)
+    g_object_unref (action->private_data->accel_group);
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);  
 }
 
 static void
@@ -316,7 +447,7 @@ gtk_action_set_property (GObject         *object,
          tmp = action->private_data->short_label;
          action->private_data->short_label = g_strdup (action->private_data->label);
          g_free (tmp);
-         g_object_notify (object, "short_label");
+         g_object_notify (object, "short-label");
        }
       break;
     case PROP_SHORT_LABEL:
@@ -356,18 +487,33 @@ gtk_action_set_property (GObject         *object,
          tmp = action->private_data->short_label;
          action->private_data->short_label = g_strdup (action->private_data->label);
          g_free (tmp);
-         g_object_notify (object, "short_label");
+         g_object_notify (object, "short-label");
        }
       break;
+    case PROP_VISIBLE_HORIZONTAL:
+      action->private_data->visible_horizontal = g_value_get_boolean (value);
+      break;
+    case PROP_VISIBLE_VERTICAL:
+      action->private_data->visible_vertical = g_value_get_boolean (value);
+      break;
+    case PROP_VISIBLE_OVERFLOWN:
+      action->private_data->visible_overflown = g_value_get_boolean (value);
+      break;
     case PROP_IS_IMPORTANT:
       action->private_data->is_important = g_value_get_boolean (value);
       break;
+    case PROP_HIDE_IF_EMPTY:
+      action->private_data->hide_if_empty = g_value_get_boolean (value);
+      break;
     case PROP_SENSITIVE:
       action->private_data->sensitive = g_value_get_boolean (value);
       break;
     case PROP_VISIBLE:
       action->private_data->visible = g_value_get_boolean (value);
       break;
+    case PROP_ACTION_GROUP:
+      gtk_action_set_action_group (action, g_value_get_object (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -401,15 +547,30 @@ gtk_action_get_property (GObject    *object,
     case PROP_STOCK_ID:
       g_value_set_string (value, action->private_data->stock_id);
       break;
+    case PROP_VISIBLE_HORIZONTAL:
+      g_value_set_boolean (value, action->private_data->visible_horizontal);
+      break;
+    case PROP_VISIBLE_VERTICAL:
+      g_value_set_boolean (value, action->private_data->visible_vertical);
+      break;
+    case PROP_VISIBLE_OVERFLOWN:
+      g_value_set_boolean (value, action->private_data->visible_overflown);
+      break;
     case PROP_IS_IMPORTANT:
       g_value_set_boolean (value, action->private_data->is_important);
       break;
+    case PROP_HIDE_IF_EMPTY:
+      g_value_set_boolean (value, action->private_data->hide_if_empty);
+      break;
     case PROP_SENSITIVE:
       g_value_set_boolean (value, action->private_data->sensitive);
       break;
     case PROP_VISIBLE:
       g_value_set_boolean (value, action->private_data->visible);
       break;
+    case PROP_ACTION_GROUP:
+      g_value_set_object (value, action->private_data->action_group);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -437,10 +598,21 @@ create_tool_item (GtkAction *action)
 }
 
 static void
-gtk_action_remove_proxy (GtkWidget *widget, 
-                        GtkAction *action)
+remove_proxy (GtkWidget *proxy, 
+             GtkAction *action)
+{
+  if (GTK_IS_MENU_ITEM (proxy))
+    gtk_action_disconnect_accelerator (action);
+
+  action->private_data->proxies = g_slist_remove (action->private_data->proxies, proxy);
+}
+
+static void
+gtk_action_sync_sensitivity (GtkAction  *action, 
+                            GParamSpec *pspec,
+                            GtkWidget  *proxy)
 {
-  action->private_data->proxies = g_slist_remove (action->private_data->proxies, widget);
+  gtk_widget_set_sensitive (proxy, gtk_action_is_sensitive (action));
 }
 
 static void
@@ -460,6 +632,68 @@ gtk_action_sync_property (GtkAction  *action,
   g_value_unset (&value);
 }
 
+/**
+ * _gtk_action_sync_menu_visible:
+ * @action: a #GtkAction, or %NULL to determine the action from @proxy
+ * @proxy: a proxy menu item
+ * @empty: whether the submenu attached to @proxy is empty
+ * 
+ * Updates the visibility of @proxy from the visibility of @action
+ * according to the following rules:
+ * <itemizedlist>
+ * <listitem><para>if @action is invisible, @proxy is too
+ * </para></listitem>
+ * <listitem><para>if @empty is %TRUE, hide @proxy unless the "hide-if-empty" 
+ *   property of @action indicates otherwise
+ * </para></listitem>
+ * </itemizedlist>
+ * 
+ * This function is used in the implementation of #GtkUIManager.
+ **/
+void
+_gtk_action_sync_menu_visible (GtkAction *action,
+                              GtkWidget *proxy,
+                              gboolean   empty)
+{
+  gboolean visible, hide_if_empty;
+
+  g_return_if_fail (GTK_IS_MENU_ITEM (proxy));
+  g_return_if_fail (action == NULL || GTK_IS_ACTION (action));
+  
+  if (action == NULL)
+    action = g_object_get_data (G_OBJECT (proxy), "gtk-action");
+
+  visible = gtk_action_is_visible (action);
+  hide_if_empty = action->private_data->hide_if_empty;
+
+  if (visible && !(empty && hide_if_empty))
+    gtk_widget_show (proxy);
+  else
+    gtk_widget_hide (proxy);
+}
+
+gboolean _gtk_menu_is_empty (GtkWidget *menu);
+
+static void
+gtk_action_sync_visible (GtkAction  *action, 
+                        GParamSpec *pspec,
+                        GtkWidget  *proxy)
+{
+  if (GTK_IS_MENU_ITEM (proxy))
+    {
+      GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy));
+
+      _gtk_action_sync_menu_visible (action, proxy, _gtk_menu_is_empty (menu));
+    }
+  else
+    {
+      if (gtk_action_is_visible (action))
+       gtk_widget_show (proxy);
+      else
+       gtk_widget_hide (proxy);
+    }
+}
+
 static void
 gtk_action_sync_label (GtkAction  *action, 
                       GParamSpec *pspec, 
@@ -482,7 +716,7 @@ gtk_action_sync_short_label (GtkAction  *action,
   GValue value = { 0, };
 
   g_value_init (&value, G_TYPE_STRING);
-  g_object_get_property (G_OBJECT (action), "short_label", &value);
+  g_object_get_property (G_OBJECT (action), "short-label", &value);
 
   g_object_set_property (G_OBJECT (proxy), "label", &value);
   g_value_unset (&value);
@@ -505,50 +739,97 @@ gtk_action_sync_stock_id (GtkAction  *action,
     }
 }
 
+static void
+gtk_action_sync_button_stock_id (GtkAction  *action, 
+                                GParamSpec *pspec,
+                                GtkWidget  *proxy)
+{
+  g_object_set (G_OBJECT (proxy),
+                "stock-id",
+                action->private_data->stock_id,
+                NULL);
+}
+
+static void
+gtk_action_sync_tooltip (GtkAction  *action, 
+                        GParamSpec *pspec, 
+                        GtkWidget  *proxy)
+{
+  g_return_if_fail (GTK_IS_TOOL_ITEM (proxy));
+
+  if (GTK_IS_TOOLBAR (gtk_widget_get_parent (proxy)))
+    {
+      GtkToolbar *toolbar = GTK_TOOLBAR (gtk_widget_get_parent (proxy));
+      
+      gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (proxy), 
+                                toolbar->tooltips,
+                                action->private_data->tooltip,
+                                NULL);
+    }
+}
+
+
 static gboolean
 gtk_action_create_menu_proxy (GtkToolItem *tool_item, 
                              GtkAction   *action)
 {
-  GtkWidget *menu_item = gtk_action_create_menu_item (action);
-
-  g_object_ref (menu_item);
-  gtk_object_sink (GTK_OBJECT (menu_item));
+  GtkWidget *menu_item;
   
-  gtk_tool_item_set_proxy_menu_item (tool_item, "gtk-action-menu-item", menu_item);
-  g_object_unref (menu_item);
+  if (action->private_data->visible_overflown)
+    {
+      menu_item = gtk_action_create_menu_item (action);
+
+      g_object_ref (menu_item);
+      gtk_object_sink (GTK_OBJECT (menu_item));
+      
+      gtk_tool_item_set_proxy_menu_item (tool_item, 
+                                        "gtk-action-menu-item", menu_item);
+      g_object_unref (menu_item);
+    }
+  else
+    gtk_tool_item_set_proxy_menu_item (tool_item, 
+                                      "gtk-action-menu-item", NULL);
 
   return TRUE;
 }
 
 static void
-connect_proxy (GtkAction *action, 
-              GtkWidget *proxy)
+connect_proxy (GtkAction     *action, 
+              GtkWidget     *proxy)
 {
   g_object_ref (action);
-  g_object_set_data_full (G_OBJECT (proxy), "gtk-action", action,
+  g_object_set_data_full (G_OBJECT (proxy), I_("gtk-action"), action,
                          g_object_unref);
 
   /* add this widget to the list of proxies */
   action->private_data->proxies = g_slist_prepend (action->private_data->proxies, proxy);
   g_signal_connect (proxy, "destroy",
-                   G_CALLBACK (gtk_action_remove_proxy), action);
+                   G_CALLBACK (remove_proxy), action);
 
   g_signal_connect_object (action, "notify::sensitive",
-                          G_CALLBACK (gtk_action_sync_property), proxy, 0);
-  gtk_widget_set_sensitive (proxy, action->private_data->sensitive);
+                          G_CALLBACK (gtk_action_sync_sensitivity), proxy, 0);
+  gtk_widget_set_sensitive (proxy, gtk_action_is_sensitive (action));
 
   g_signal_connect_object (action, "notify::visible",
-                          G_CALLBACK (gtk_action_sync_property), proxy, 0);
-  if (action->private_data->visible)
+                          G_CALLBACK (gtk_action_sync_visible), proxy, 0);
+  if (gtk_action_is_visible (action))
     gtk_widget_show (proxy);
   else
     gtk_widget_hide (proxy);
+  gtk_widget_set_no_show_all (proxy, TRUE);
 
   if (GTK_IS_MENU_ITEM (proxy))
     {
       GtkWidget *label;
       /* menu item specific synchronisers ... */
       
+      if (action->private_data->accel_quark)
+       {
+         gtk_action_connect_accelerator (action);
+         gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy),
+                                       g_quark_to_string (action->private_data->accel_quark));
+       }
+      
       label = GTK_BIN (proxy)->child;
 
       /* make sure label is a label */
@@ -557,16 +838,20 @@ connect_proxy (GtkAction *action,
          gtk_container_remove (GTK_CONTAINER (proxy), label);
          label = NULL;
        }
+
       if (!label)
-       {
-         label = g_object_new (GTK_TYPE_ACCEL_LABEL,
-                               "use_underline", TRUE,
-                               "xalign", 0.0,
-                               "visible", TRUE,
-                               "parent", proxy,
-                               "accel_widget", proxy,
-                               NULL);
-       }
+       label = g_object_new (GTK_TYPE_ACCEL_LABEL,
+                             "use_underline", TRUE,
+                             "xalign", 0.0,
+                             "visible", TRUE,
+                             "parent", proxy,
+                             NULL);
+      
+      if (GTK_IS_ACCEL_LABEL (label) && action->private_data->accel_quark)
+       g_object_set (label,
+                     "accel_closure", action->private_data->accel_closure,
+                     NULL);
+
       gtk_label_set_label (GTK_LABEL (label), action->private_data->label);
       g_signal_connect_object (action, "notify::label",
                               G_CALLBACK (gtk_action_sync_label), proxy, 0);
@@ -591,79 +876,120 @@ connect_proxy (GtkAction *action,
            }
          gtk_image_set_from_stock (GTK_IMAGE (image),
                                    action->private_data->stock_id, GTK_ICON_SIZE_MENU);
-         g_signal_connect_object (action, "notify::stock_id",
+         g_signal_connect_object (action, "notify::stock-id",
                                   G_CALLBACK (gtk_action_sync_stock_id),
                                   proxy, 0);
        }
 
-      if (action->private_data->accel_quark)
-       {
-         gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy),
-                                       g_quark_to_string (action->private_data->accel_quark));
-       }
+      if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)) == NULL)
+       g_signal_connect_object (proxy, "activate",
+                                G_CALLBACK (gtk_action_activate), action,
+                                G_CONNECT_SWAPPED);
 
-      g_signal_connect_object (proxy, "activate",
-                              G_CALLBACK (gtk_action_activate), action,
-                              G_CONNECT_SWAPPED);
     }
-  else if (GTK_IS_TOOL_BUTTON (proxy))
+  else if (GTK_IS_TOOL_ITEM (proxy))
     {
-      /* toolbar button specific synchronisers ... */
+      GParamSpec *pspec;
 
-      g_object_set (G_OBJECT (proxy),
-                   "label", action->private_data->short_label,
-                   "use_underline", TRUE,
-                   "stock_id", action->private_data->stock_id,
+      /* toolbar item specific synchronisers ... */
+
+      g_object_set (proxy,
+                   "visible_horizontal", action->private_data->visible_horizontal,
+                   "visible_vertical",   action->private_data->visible_vertical,
                    "is_important", action->private_data->is_important,
                    NULL);
-      g_signal_connect_object (action, "notify::short_label",
-                              G_CALLBACK (gtk_action_sync_short_label),
-                              proxy, 0);      
-      g_signal_connect_object (action, "notify::stock_id",
+
+      pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (action),
+                                            "tooltip");
+      gtk_action_sync_tooltip (action, pspec, proxy);
+
+      g_signal_connect_object (action, "notify::visible-horizontal",
                               G_CALLBACK (gtk_action_sync_property), 
                               proxy, 0);
-      g_signal_connect_object (action, "notify::is_important",
+      g_signal_connect_object (action, "notify::visible-vertical",
                               G_CALLBACK (gtk_action_sync_property), 
                               proxy, 0);
+      g_signal_connect_object (action, "notify::is-important",
+                              G_CALLBACK (gtk_action_sync_property), 
+                              proxy, 0);
+      g_signal_connect_object (action, "notify::tooltip",
+                              G_CALLBACK (gtk_action_sync_tooltip), 
+                              proxy, 0);
 
       g_signal_connect_object (proxy, "create_menu_proxy",
                               G_CALLBACK (gtk_action_create_menu_proxy),
                               action, 0);
 
-      g_signal_connect_object (proxy, "clicked",
-                              G_CALLBACK (gtk_action_activate), action,
-                              G_CONNECT_SWAPPED);
+      gtk_tool_item_rebuild_menu (GTK_TOOL_ITEM (proxy));
+
+      /* toolbar button specific synchronisers ... */
+      if (GTK_IS_TOOL_BUTTON (proxy))
+       {
+         g_object_set (proxy,
+                       "label", action->private_data->short_label,
+                       "use_underline", TRUE,
+                       "stock_id", action->private_data->stock_id,
+                       NULL);
+
+         g_signal_connect_object (action, "notify::short-label",
+                                  G_CALLBACK (gtk_action_sync_short_label),
+                                  proxy, 0);      
+         g_signal_connect_object (action, "notify::stock-id",
+                                  G_CALLBACK (gtk_action_sync_property), 
+                                  proxy, 0);
+         g_signal_connect_object (proxy, "clicked",
+                                  G_CALLBACK (gtk_action_activate), action,
+                                  G_CONNECT_SWAPPED);
+       }
     }
   else if (GTK_IS_BUTTON (proxy))
     {
       /* button specific synchronisers ... */
-
-      /* synchronise the label */
-      g_object_set (G_OBJECT (proxy),
-                   "label", action->private_data->short_label,
-                   "use_underline", TRUE,
-                   NULL);
-      g_signal_connect_object (action, "notify::short_label",
-                              G_CALLBACK (gtk_action_sync_short_label),
-                              proxy, 0);
+      if (gtk_button_get_use_stock (GTK_BUTTON (proxy)))
+       {
+         /* synchronise stock-id */
+         g_object_set (proxy,
+                       "stock-id", action->private_data->stock_id,
+                       NULL);
+         g_signal_connect_object (action, "notify::stock-id",
+                                  G_CALLBACK (gtk_action_sync_button_stock_id),
+                                  proxy, 0);
+       }
+      else if (GTK_BIN (proxy)->child == NULL || 
+              GTK_IS_LABEL (GTK_BIN (proxy)->child))
+       {
+         /* synchronise the label */
+         g_object_set (proxy,
+                       "label", action->private_data->short_label,
+                       "use_underline", TRUE,
+                       NULL);
+         g_signal_connect_object (action, "notify::short-label",
+                                  G_CALLBACK (gtk_action_sync_short_label),
+                                  proxy, 0);
+         
+       }
       
+      /* we leave the button alone if there is a custom child */
       g_signal_connect_object (proxy, "clicked",
                               G_CALLBACK (gtk_action_activate), action,
                               G_CONNECT_SWAPPED);
     }
+
+  if (action->private_data->action_group)
+    _gtk_action_group_emit_connect_proxy (action->private_data->action_group, action, proxy);
 }
 
 static void
 disconnect_proxy (GtkAction *action, 
                  GtkWidget *proxy)
 {
-  g_object_set_data (G_OBJECT (proxy), "gtk-action", NULL);
+  g_object_set_data (G_OBJECT (proxy), I_("gtk-action"), NULL);
 
   /* remove proxy from list of proxies */
   g_signal_handlers_disconnect_by_func (proxy,
-                                       G_CALLBACK (gtk_action_remove_proxy),
+                                       G_CALLBACK (remove_proxy),
                                        action);
-  gtk_action_remove_proxy (proxy, action);
+  remove_proxy (proxy, action);
 
   /* disconnect the activate handler */
   g_signal_handlers_disconnect_by_func (proxy,
@@ -671,9 +997,15 @@ disconnect_proxy (GtkAction *action,
                                        action);
 
   /* disconnect handlers for notify::* signals */
-  g_signal_handlers_disconnect_by_func (proxy,
+  g_signal_handlers_disconnect_by_func (action,
+                                       G_CALLBACK (gtk_action_sync_sensitivity),
+                                       proxy);
+  g_signal_handlers_disconnect_by_func (action,
+                                       G_CALLBACK (gtk_action_sync_visible),
+                                       proxy);
+  g_signal_handlers_disconnect_by_func (action,
                                        G_CALLBACK (gtk_action_sync_property),
-                                       action);
+                                       proxy);
 
   g_signal_handlers_disconnect_by_func (action,
                                G_CALLBACK (gtk_action_sync_stock_id), proxy);
@@ -683,9 +1015,6 @@ disconnect_proxy (GtkAction *action,
                                        G_CALLBACK (gtk_action_sync_label),
                                        proxy);
   
-  if (GTK_IS_MENU_ITEM (widget))
-    gtk_menu_item_set_accel_path (GTK_MENU_ITEM (widget), NULL);
-
   /* toolbar button specific synchronisers ... */
   g_signal_handlers_disconnect_by_func (action,
                                        G_CALLBACK (gtk_action_sync_short_label),
@@ -694,14 +1023,38 @@ disconnect_proxy (GtkAction *action,
   g_signal_handlers_disconnect_by_func (proxy,
                                        G_CALLBACK (gtk_action_create_menu_proxy),
                                        action);
+
+  if (action->private_data->action_group)
+    _gtk_action_group_emit_disconnect_proxy (action->private_data->action_group, action, proxy);
+}
+
+void
+_gtk_action_emit_activate (GtkAction *action)
+{
+  GtkActionGroup *group = action->private_data->action_group;
+
+  if (group != NULL) 
+    {
+      g_object_ref (group);
+      _gtk_action_group_emit_pre_activate (group, action);
+    }
+
+    g_signal_emit (action, action_signals[ACTIVATE], 0);
+
+  if (group != NULL) 
+    {
+      _gtk_action_group_emit_post_activate (group, action);
+      g_object_unref (group);
+    }
 }
 
 /**
  * gtk_action_activate:
  * @action: the action object
  *
- * Emits the "activate" signal on the specified action. 
- * This gets called by the proxy widgets when they get activated.
+ * Emits the "activate" signal on the specified action, if it isn't 
+ * insensitive. This gets called by the proxy widgets when they get 
+ * activated.
  *
  * It can also be used to manually activate an action.
  *
@@ -710,7 +1063,10 @@ disconnect_proxy (GtkAction *action,
 void
 gtk_action_activate (GtkAction *action)
 {
-  g_signal_emit (action, action_signals[ACTIVATE], 0);
+  g_return_if_fail (GTK_IS_ACTION (action));
+  
+  if (gtk_action_is_sensitive (action))
+    _gtk_action_emit_activate (action);
 }
 
 /**
@@ -833,7 +1189,7 @@ gtk_action_disconnect_proxy (GtkAction *action,
   g_return_if_fail (GTK_IS_ACTION (action));
   g_return_if_fail (GTK_IS_WIDGET (proxy));
 
-  g_return_if_fail (g_object_get_data (G_OBJECT (proxy), "gtk-action") != action);
+  g_return_if_fail (g_object_get_data (G_OBJECT (proxy), "gtk-action") == action);
 
   (* GTK_ACTION_GET_CLASS (action)->disconnect_proxy) (action, proxy);  
 }
@@ -869,7 +1225,7 @@ gtk_action_get_proxies (GtkAction *action)
  *
  * Since: 2.4
  **/
-const gchar *
+G_CONST_RETURN gchar *
 gtk_action_get_name (GtkAction *action)
 {
   g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
@@ -877,6 +1233,148 @@ gtk_action_get_name (GtkAction *action)
   return action->private_data->name;
 }
 
+/**
+ * gtk_action_is_sensitive:
+ * @action: the action object
+ * 
+ * Returns whether the action is effectively sensitive.
+ *
+ * Return value: %TRUE if the action and its associated action group 
+ * are both sensitive.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_action_is_sensitive (GtkAction *action)
+{
+  GtkActionPrivate *priv;
+  g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
+
+  priv = action->private_data;
+  return priv->sensitive &&
+    (priv->action_group == NULL ||
+     gtk_action_group_get_sensitive (priv->action_group));
+}
+
+/**
+ * gtk_action_get_sensitive:
+ * @action: the action object
+ * 
+ * Returns whether the action itself is sensitive. Note that this doesn't 
+ * necessarily mean effective sensitivity. See gtk_action_is_sensitive() 
+ * for that.
+ *
+ * Return value: %TRUE if the action itself is sensitive.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_action_get_sensitive (GtkAction *action)
+{
+  g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
+
+  return action->private_data->sensitive;
+}
+
+/**
+ * gtk_action_set_sensitive:
+ * @action: the action object
+ * @sensitive: %TRUE to make the action sensitive
+ * 
+ * Sets the ::sensitive property of the action to @sensitive. Note that 
+ * this doesn't necessarily mean effective sensitivity. See 
+ * gtk_action_is_sensitive() 
+ * for that.
+ *
+ * Since: 2.6
+ **/
+void
+gtk_action_set_sensitive (GtkAction *action,
+                         gboolean   sensitive)
+{
+  g_return_if_fail (GTK_IS_ACTION (action));
+
+  sensitive = sensitive != FALSE;
+  
+  if (action->private_data->sensitive != sensitive)
+    {
+      action->private_data->sensitive = sensitive;
+
+      g_object_notify (G_OBJECT (action), "sensitive");
+    }
+}
+
+/**
+ * gtk_action_is_visible:
+ * @action: the action object
+ * 
+ * Returns whether the action is effectively visible.
+ *
+ * Return value: %TRUE if the action and its associated action group 
+ * are both visible.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_action_is_visible (GtkAction *action)
+{
+  GtkActionPrivate *priv;
+  g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
+
+  priv = action->private_data;
+  return priv->visible &&
+    (priv->action_group == NULL ||
+     gtk_action_group_get_visible (priv->action_group));
+}
+
+/**
+ * gtk_action_get_visible:
+ * @action: the action object
+ * 
+ * Returns whether the action itself is visible. Note that this doesn't 
+ * necessarily mean effective visibility. See gtk_action_is_sensitive() 
+ * for that.
+ *
+ * Return value: %TRUE if the action itself is visible.
+ *
+ * Since: 2.4
+ **/
+gboolean
+gtk_action_get_visible (GtkAction *action)
+{
+  g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
+
+  return action->private_data->visible;
+}
+
+/**
+ * gtk_action_set_visible:
+ * @action: the action object
+ * @visible: %TRUE to make the action visible
+ * 
+ * Sets the ::visible property of the action to @visible. Note that 
+ * this doesn't necessarily mean effective visibility. See 
+ * gtk_action_is_visible() 
+ * for that.
+ *
+ * Since: 2.6
+ **/
+void
+gtk_action_set_visible (GtkAction *action,
+                       gboolean   visible)
+{
+  g_return_if_fail (GTK_IS_ACTION (action));
+
+  visible = visible != FALSE;
+  
+  if (action->private_data->visible != visible)
+    {
+      action->private_data->visible = visible;
+
+      g_object_notify (G_OBJECT (action), "visible");
+    }
+}
+
 /**
  * gtk_action_block_activate_from:
  * @action: the action object
@@ -923,6 +1421,37 @@ gtk_action_unblock_activate_from (GtkAction *action,
                                     action);
 }
 
+static void
+closure_accel_activate (GClosure     *closure,
+                        GValue       *return_value,
+                        guint         n_param_values,
+                        const GValue *param_values,
+                        gpointer      invocation_hint,
+                        gpointer      marshal_data)
+{
+  if (gtk_action_is_sensitive (GTK_ACTION (closure->data)))
+    {
+      _gtk_action_emit_activate (GTK_ACTION (closure->data));
+      
+      /* we handled the accelerator */
+      g_value_set_boolean (return_value, TRUE);
+    }
+}
+
+static void
+gtk_action_set_action_group (GtkAction     *action,
+                            GtkActionGroup *action_group)
+{
+  g_return_if_fail (GTK_IS_ACTION (action));
+
+  if (action->private_data->action_group == NULL)
+    g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
+  else
+    g_return_if_fail (action_group == NULL);
+
+  action->private_data->action_group = action_group;
+}
+
 /**
  * gtk_action_set_accel_path:
  * @action: the action object
@@ -938,5 +1467,139 @@ void
 gtk_action_set_accel_path (GtkAction   *action, 
                           const gchar *accel_path)
 {
+  g_return_if_fail (GTK_IS_ACTION (action));
+
   action->private_data->accel_quark = g_quark_from_string (accel_path);
 }
+
+/**
+ * gtk_action_get_accel_path:
+ * @action: the action object
+ *
+ * Returns the accel path for this action.  
+ *
+ * Since: 2.6
+ *
+ * Returns: the accel path for this action, or %NULL
+ *   if none is set. The returned string is owned by GTK+ 
+ *   and must not be freed or modified.
+ */
+G_CONST_RETURN gchar *
+gtk_action_get_accel_path (GtkAction *action)
+{
+  g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
+
+  if (action->private_data->accel_quark)
+    return g_quark_to_string (action->private_data->accel_quark);
+  else
+    return NULL;
+}
+
+/**
+ * gtk_action_get_accel_closure:
+ * @action: the action object
+ *
+ * Returns the accel closure for this action.
+ *
+ * Since: 2.8
+ *
+ * Returns: the accel closure for this action. The returned closure is
+ *          owned by GTK+ and must not be unreffed or modified.
+ */
+GClosure *
+gtk_action_get_accel_closure (GtkAction *action)
+{
+  g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
+
+  return action->private_data->accel_closure;
+}
+
+
+/**
+ * gtk_action_set_accel_group:
+ * @action: the action object
+ * @accel_group: a #GtkAccelGroup or %NULL
+ * 
+ * Sets the #GtkAccelGroup in which the accelerator for this action
+ * will be installed.
+ *
+ * Since: 2.4
+ **/
+void
+gtk_action_set_accel_group (GtkAction     *action,
+                           GtkAccelGroup *accel_group)
+{
+  g_return_if_fail (GTK_IS_ACTION (action));
+  g_return_if_fail (accel_group == NULL || GTK_IS_ACCEL_GROUP (accel_group));
+  
+  if (accel_group)
+    g_object_ref (accel_group);
+  if (action->private_data->accel_group)
+    g_object_unref (action->private_data->accel_group);
+
+  action->private_data->accel_group = accel_group;
+}
+
+/**
+ * gtk_action_connect_accelerator:
+ * @action: a #GtkAction
+ * 
+ * Installs the accelerator for @action if @action has an
+ * accel path and group. See gtk_action_set_accel_path() and 
+ * gtk_action_set_accel_group()
+ *
+ * Since multiple proxies may independently trigger the installation
+ * of the accelerator, the @action counts the number of times this
+ * function has been called and doesn't remove the accelerator until
+ * gtk_action_disconnect_accelerator() has been called as many times.
+ *
+ * Since: 2.4
+ **/
+void 
+gtk_action_connect_accelerator (GtkAction *action)
+{
+  g_return_if_fail (GTK_IS_ACTION (action));
+
+  if (!action->private_data->accel_quark ||
+      !action->private_data->accel_group)
+    return;
+
+  if (action->private_data->accel_count == 0)
+    {
+      const gchar *accel_path = 
+       g_quark_to_string (action->private_data->accel_quark);
+      
+      gtk_accel_group_connect_by_path (action->private_data->accel_group,
+                                      accel_path,
+                                      action->private_data->accel_closure);
+    }
+
+  action->private_data->accel_count++;
+}
+
+/**
+ * gtk_action_disconnect_accelerator:
+ * @action: a #GtkAction
+ * 
+ * Undoes the effect of one call to gtk_action_connect_accelerator().
+ *
+ * Since: 2.4
+ **/
+void 
+gtk_action_disconnect_accelerator (GtkAction *action)
+{
+  g_return_if_fail (GTK_IS_ACTION (action));
+
+  if (!action->private_data->accel_quark ||
+      !action->private_data->accel_group)
+    return;
+
+  action->private_data->accel_count--;
+
+  if (action->private_data->accel_count == 0)
+    gtk_accel_group_disconnect (action->private_data->accel_group,
+                               action->private_data->accel_closure);
+}
+
+#define __GTK_ACTION_C__
+#include "gtkaliasdef.c"