]> Pileus Git - ~andy/gtk/blob - gtk/gtkaction.c
Avoid connecting to notify on our own properties.
[~andy/gtk] / gtk / gtkaction.c
1 /*
2  * GTK - The GIMP Toolkit
3  * Copyright (C) 1998, 1999 Red Hat, Inc.
4  * All rights reserved.
5  *
6  * This Library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This Library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * Author: James Henstridge <james@daa.com.au>
24  *
25  * Modified by the GTK+ Team and others 2003.  See the AUTHORS
26  * file for a list of people on the GTK+ Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
29  */
30
31 #include <config.h>
32
33 #include "gtkaction.h"
34 #include "gtkactiongroup.h"
35 #include "gtkaccellabel.h"
36 #include "gtkbutton.h"
37 #include "gtkimage.h"
38 #include "gtkimagemenuitem.h"
39 #include "gtkintl.h"
40 #include "gtklabel.h"
41 #include "gtkmarshalers.h"
42 #include "gtkmenuitem.h"
43 #include "gtkstock.h"
44 #include "gtktearoffmenuitem.h"
45 #include "gtktoolbutton.h"
46 #include "gtktoolbar.h"
47 #include "gtkprivate.h"
48 #include "gtkalias.h"
49
50
51 #define GTK_ACTION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ACTION, GtkActionPrivate))
52
53 struct _GtkActionPrivate 
54 {
55   gchar *name;
56   gchar *label;
57   gchar *short_label;
58   gchar *tooltip;
59   gchar *stock_id; /* icon */
60
61   guint sensitive          : 1;
62   guint visible            : 1;
63   guint label_set          : 1; /* these two used so we can set label */
64   guint short_label_set    : 1; /* based on stock id */
65   guint visible_horizontal : 1;
66   guint visible_vertical   : 1;
67   guint is_important       : 1;
68   guint hide_if_empty      : 1;
69   guint visible_overflown  : 1;
70
71   /* accelerator */
72   guint          accel_count;
73   GtkAccelGroup *accel_group;
74   GClosure      *accel_closure;
75   GQuark         accel_quark;
76
77   GtkActionGroup *action_group;
78
79   /* list of proxy widgets */
80   GSList *proxies;
81 };
82
83 enum 
84 {
85   ACTIVATE,
86   LAST_SIGNAL
87 };
88
89 enum 
90 {
91   PROP_0,
92   PROP_NAME,
93   PROP_LABEL,
94   PROP_SHORT_LABEL,
95   PROP_TOOLTIP,
96   PROP_STOCK_ID,
97   PROP_VISIBLE_HORIZONTAL,
98   PROP_VISIBLE_VERTICAL,
99   PROP_VISIBLE_OVERFLOWN,
100   PROP_IS_IMPORTANT,
101   PROP_HIDE_IF_EMPTY,
102   PROP_SENSITIVE,
103   PROP_VISIBLE,
104   PROP_ACTION_GROUP
105 };
106
107 static void gtk_action_init       (GtkAction *action);
108 static void gtk_action_class_init (GtkActionClass *class);
109
110 static GQuark       accel_path_id  = 0;
111 static const gchar accel_path_key[] = "GtkAction::accel_path";
112
113 GType
114 gtk_action_get_type (void)
115 {
116   static GtkType type = 0;
117
118   if (!type)
119     {
120       static const GTypeInfo type_info =
121       {
122         sizeof (GtkActionClass),
123         (GBaseInitFunc) NULL,
124         (GBaseFinalizeFunc) NULL,
125         (GClassInitFunc) gtk_action_class_init,
126         (GClassFinalizeFunc) NULL,
127         NULL,
128         
129         sizeof (GtkAction),
130         0, /* n_preallocs */
131         (GInstanceInitFunc) gtk_action_init,
132       };
133
134       type = g_type_register_static (G_TYPE_OBJECT,
135                                      I_("GtkAction"),
136                                      &type_info, 0);
137     }
138   return type;
139 }
140
141 static void gtk_action_finalize     (GObject *object);
142 static void gtk_action_set_property (GObject         *object,
143                                      guint            prop_id,
144                                      const GValue    *value,
145                                      GParamSpec      *pspec);
146 static void gtk_action_get_property (GObject         *object,
147                                      guint            prop_id,
148                                      GValue          *value,
149                                      GParamSpec      *pspec);
150 static void gtk_action_set_action_group (GtkAction      *action,
151                                          GtkActionGroup *action_group);
152 static void gtk_action_set_is_important (GtkAction      *action,
153                                          gboolean        is_important);
154 static void gtk_action_set_label        (GtkAction      *action,
155                                          const gchar    *label);
156 static void gtk_action_set_short_label  (GtkAction      *action,
157                                          const gchar    *label);
158 static void gtk_action_set_visible_horizontal (GtkAction *action,
159                                                gboolean   visible_horizontal);
160 static void gtk_action_set_visible_vertical   (GtkAction *action,
161                                                gboolean   visible_vertical);
162 static void gtk_action_set_tooltip      (GtkAction      *action,
163                                          const gchar    *tooltip);
164 static void gtk_action_set_stock_id     (GtkAction      *action,
165                                          const gchar    *stock_id);
166 static void gtk_action_sync_tooltip     (GtkAction      *action,
167                                          GtkWidget      *proxy);
168
169 static GtkWidget *create_menu_item    (GtkAction *action);
170 static GtkWidget *create_tool_item    (GtkAction *action);
171 static void       connect_proxy       (GtkAction     *action,
172                                        GtkWidget     *proxy);
173 static void       disconnect_proxy    (GtkAction *action,
174                                        GtkWidget *proxy);
175 static void       closure_accel_activate (GClosure     *closure,
176                                           GValue       *return_value,
177                                           guint         n_param_values,
178                                           const GValue *param_values,
179                                           gpointer      invocation_hint,
180                                           gpointer      marshal_data);
181
182 static GObjectClass *parent_class = NULL;
183 static guint         action_signals[LAST_SIGNAL] = { 0 };
184
185
186 static void
187 gtk_action_class_init (GtkActionClass *klass)
188 {
189   GObjectClass *gobject_class;
190
191   accel_path_id = g_quark_from_static_string (accel_path_key);
192
193   parent_class = g_type_class_peek_parent (klass);
194   gobject_class = G_OBJECT_CLASS (klass);
195
196   gobject_class->finalize     = gtk_action_finalize;
197   gobject_class->set_property = gtk_action_set_property;
198   gobject_class->get_property = gtk_action_get_property;
199
200   klass->activate = NULL;
201
202   klass->create_menu_item = create_menu_item;
203   klass->create_tool_item = create_tool_item;
204   klass->connect_proxy = connect_proxy;
205   klass->disconnect_proxy = disconnect_proxy;
206
207   klass->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM;
208   klass->toolbar_item_type = GTK_TYPE_TOOL_BUTTON;
209
210   g_object_class_install_property (gobject_class,
211                                    PROP_NAME,
212                                    g_param_spec_string ("name",
213                                                         P_("Name"),
214                                                         P_("A unique name for the action."),
215                                                         NULL,
216                                                         GTK_PARAM_READWRITE | 
217                                                         G_PARAM_CONSTRUCT_ONLY));
218   g_object_class_install_property (gobject_class,
219                                    PROP_LABEL,
220                                    g_param_spec_string ("label",
221                                                         P_("Label"),
222                                                         P_("The label used for menu items and buttons "
223                                                            "that activate this action."),
224                                                         NULL,
225                                                         GTK_PARAM_READWRITE));
226   g_object_class_install_property (gobject_class,
227                                    PROP_SHORT_LABEL,
228                                    g_param_spec_string ("short-label",
229                                                         P_("Short label"),
230                                                         P_("A shorter label that may be used on toolbar buttons."),
231                                                         NULL,
232                                                         GTK_PARAM_READWRITE));
233   g_object_class_install_property (gobject_class,
234                                    PROP_TOOLTIP,
235                                    g_param_spec_string ("tooltip",
236                                                         P_("Tooltip"),
237                                                         P_("A tooltip for this action."),
238                                                         NULL,
239                                                         GTK_PARAM_READWRITE));
240   g_object_class_install_property (gobject_class,
241                                    PROP_STOCK_ID,
242                                    g_param_spec_string ("stock-id",
243                                                         P_("Stock Icon"),
244                                                         P_("The stock icon displayed in widgets representing "
245                                                            "this action."),
246                                                         NULL,
247                                                         GTK_PARAM_READWRITE));
248   g_object_class_install_property (gobject_class,
249                                    PROP_VISIBLE_HORIZONTAL,
250                                    g_param_spec_boolean ("visible-horizontal",
251                                                          P_("Visible when horizontal"),
252                                                          P_("Whether the toolbar item is visible when the toolbar "
253                                                             "is in a horizontal orientation."),
254                                                          TRUE,
255                                                          GTK_PARAM_READWRITE));
256   /**
257    * GtkAction:visible-overflown:
258    *
259    * When %TRUE, toolitem proxies for this action are represented in the 
260    * toolbar overflow menu.
261    *
262    * Since: 2.6
263    */
264   g_object_class_install_property (gobject_class,
265                                    PROP_VISIBLE_OVERFLOWN,
266                                    g_param_spec_boolean ("visible-overflown",
267                                                          P_("Visible when overflown"),
268                                                          P_("When TRUE, toolitem proxies for this action "
269                                                             "are represented in the toolbar overflow menu."),
270                                                          TRUE,
271                                                          GTK_PARAM_READWRITE));
272   g_object_class_install_property (gobject_class,
273                                    PROP_VISIBLE_VERTICAL,
274                                    g_param_spec_boolean ("visible-vertical",
275                                                          P_("Visible when vertical"),
276                                                          P_("Whether the toolbar item is visible when the toolbar "
277                                                             "is in a vertical orientation."),
278                                                          TRUE,
279                                                          GTK_PARAM_READWRITE));
280   g_object_class_install_property (gobject_class,
281                                    PROP_IS_IMPORTANT,
282                                    g_param_spec_boolean ("is-important",
283                                                          P_("Is important"),
284                                                          P_("Whether the action is considered important. "
285                                                             "When TRUE, toolitem proxies for this action "
286                                                             "show text in GTK_TOOLBAR_BOTH_HORIZ mode."),
287                                                          FALSE,
288                                                          GTK_PARAM_READWRITE));
289   g_object_class_install_property (gobject_class,
290                                    PROP_HIDE_IF_EMPTY,
291                                    g_param_spec_boolean ("hide-if-empty",
292                                                          P_("Hide if empty"),
293                                                          P_("When TRUE, empty menu proxies for this action are hidden."),
294                                                          TRUE,
295                                                          GTK_PARAM_READWRITE));
296   g_object_class_install_property (gobject_class,
297                                    PROP_SENSITIVE,
298                                    g_param_spec_boolean ("sensitive",
299                                                          P_("Sensitive"),
300                                                          P_("Whether the action is enabled."),
301                                                          TRUE,
302                                                          GTK_PARAM_READWRITE));
303   g_object_class_install_property (gobject_class,
304                                    PROP_VISIBLE,
305                                    g_param_spec_boolean ("visible",
306                                                          P_("Visible"),
307                                                          P_("Whether the action is visible."),
308                                                          TRUE,
309                                                          GTK_PARAM_READWRITE));
310   g_object_class_install_property (gobject_class,
311                                    PROP_ACTION_GROUP,
312                                    g_param_spec_object ("action-group",
313                                                          P_("Action Group"),
314                                                          P_("The GtkActionGroup this GtkAction is associated with, or NULL (for internal use)."),
315                                                          GTK_TYPE_ACTION_GROUP,
316                                                          GTK_PARAM_READWRITE));
317
318   /**
319    * GtkAction::activate:
320    * @action: the #GtkAction
321    *
322    * The "activate" signal is emitted when the action is activated.
323    *
324    * Since: 2.4
325    */
326   action_signals[ACTIVATE] =
327     g_signal_new (I_("activate"),
328                   G_OBJECT_CLASS_TYPE (klass),
329                   G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
330                   G_STRUCT_OFFSET (GtkActionClass, activate),  NULL, NULL,
331                   g_cclosure_marshal_VOID__VOID,
332                   G_TYPE_NONE, 0);
333
334   g_type_class_add_private (gobject_class, sizeof (GtkActionPrivate));
335 }
336
337
338 static void
339 gtk_action_init (GtkAction *action)
340 {
341   action->private_data = GTK_ACTION_GET_PRIVATE (action);
342
343   action->private_data->name = NULL;
344   action->private_data->label = NULL;
345   action->private_data->short_label = NULL;
346   action->private_data->tooltip = NULL;
347   action->private_data->stock_id = NULL;
348   action->private_data->visible_horizontal = TRUE;
349   action->private_data->visible_vertical   = TRUE;
350   action->private_data->visible_overflown  = TRUE;
351   action->private_data->is_important = FALSE;
352   action->private_data->hide_if_empty = TRUE;
353
354   action->private_data->sensitive = TRUE;
355   action->private_data->visible = TRUE;
356
357   action->private_data->label_set = FALSE;
358   action->private_data->short_label_set = FALSE;
359
360   action->private_data->accel_count = 0;
361   action->private_data->accel_group = NULL;
362   action->private_data->accel_quark = 0;
363   action->private_data->accel_closure = 
364     g_closure_new_object (sizeof (GClosure), G_OBJECT (action));
365   g_closure_set_marshal (action->private_data->accel_closure, 
366                          closure_accel_activate);
367   g_closure_ref (action->private_data->accel_closure);
368   g_closure_sink (action->private_data->accel_closure);
369
370   action->private_data->action_group = NULL;
371
372   action->private_data->proxies = NULL;
373 }
374
375 /**
376  * gtk_action_new:
377  * @name: A unique name for the action
378  * @label: the label displayed in menu items and on buttons
379  * @tooltip: a tooltip for the action
380  * @stock_id: the stock icon to display in widgets representing the action
381  *
382  * Creates a new #GtkAction object. To add the action to a
383  * #GtkActionGroup and set the accelerator for the action,
384  * call gtk_action_group_add_action_with_accel().
385  * See <xref linkend="XML-UI"/> for information on allowed action
386  * names.
387  *
388  * Return value: a new #GtkAction
389  *
390  * Since: 2.4
391  */
392 GtkAction *
393 gtk_action_new (const gchar *name,
394                 const gchar *label,
395                 const gchar *tooltip,
396                 const gchar *stock_id)
397 {
398   GtkAction *action;
399
400   action = g_object_new (GTK_TYPE_ACTION,
401                          "name", name,
402                          "label", label,
403                          "tooltip", tooltip,
404                          "stock_id", stock_id,
405                          NULL);
406
407   return action;
408 }
409
410 static void
411 gtk_action_finalize (GObject *object)
412 {
413   GtkAction *action;
414   action = GTK_ACTION (object);
415
416   g_free (action->private_data->name);
417   g_free (action->private_data->label);
418   g_free (action->private_data->short_label);
419   g_free (action->private_data->tooltip);
420   g_free (action->private_data->stock_id);
421
422   g_closure_unref (action->private_data->accel_closure);
423   if (action->private_data->accel_group)
424     g_object_unref (action->private_data->accel_group);
425
426   G_OBJECT_CLASS (parent_class)->finalize (object);  
427 }
428
429 static void
430 gtk_action_set_property (GObject         *object,
431                          guint            prop_id,
432                          const GValue    *value,
433                          GParamSpec      *pspec)
434 {
435   GtkAction *action;
436   gchar *tmp;
437   
438   action = GTK_ACTION (object);
439
440   switch (prop_id)
441     {
442     case PROP_NAME:
443       tmp = action->private_data->name;
444       action->private_data->name = g_value_dup_string (value);
445       g_free (tmp);
446       break;
447     case PROP_LABEL:
448       gtk_action_set_label (action, g_value_get_string (value));
449       break;
450     case PROP_SHORT_LABEL:
451       gtk_action_set_short_label (action, g_value_get_string (value));
452       break;
453     case PROP_TOOLTIP:
454       gtk_action_set_tooltip (action, g_value_get_string (value));
455       break;
456     case PROP_STOCK_ID:
457       gtk_action_set_stock_id (action, g_value_get_string (value));
458       break;
459     case PROP_VISIBLE_HORIZONTAL:
460       gtk_action_set_visible_horizontal (action, g_value_get_boolean (value));
461       break;
462     case PROP_VISIBLE_VERTICAL:
463       gtk_action_set_visible_vertical (action, g_value_get_boolean (value));
464       break;
465     case PROP_VISIBLE_OVERFLOWN:
466       action->private_data->visible_overflown = g_value_get_boolean (value);
467       break;
468     case PROP_IS_IMPORTANT:
469       gtk_action_set_is_important (action, g_value_get_boolean (value));
470       break;
471     case PROP_HIDE_IF_EMPTY:
472       action->private_data->hide_if_empty = g_value_get_boolean (value);
473       break;
474     case PROP_SENSITIVE:
475       gtk_action_set_sensitive (action, g_value_get_boolean (value));
476       break;
477     case PROP_VISIBLE:
478       gtk_action_set_visible (action, g_value_get_boolean (value));
479       break;
480     case PROP_ACTION_GROUP:
481       gtk_action_set_action_group (action, g_value_get_object (value));
482       break;
483     default:
484       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
485       break;
486     }
487 }
488
489 static void
490 gtk_action_get_property (GObject    *object,
491                          guint       prop_id,
492                          GValue     *value,
493                          GParamSpec *pspec)
494 {
495   GtkAction *action;
496
497   action = GTK_ACTION (object);
498
499   switch (prop_id)
500     {
501     case PROP_NAME:
502       g_value_set_string (value, action->private_data->name);
503       break;
504     case PROP_LABEL:
505       g_value_set_string (value, action->private_data->label);
506       break;
507     case PROP_SHORT_LABEL:
508       g_value_set_string (value, action->private_data->short_label);
509       break;
510     case PROP_TOOLTIP:
511       g_value_set_string (value, action->private_data->tooltip);
512       break;
513     case PROP_STOCK_ID:
514       g_value_set_string (value, action->private_data->stock_id);
515       break;
516     case PROP_VISIBLE_HORIZONTAL:
517       g_value_set_boolean (value, action->private_data->visible_horizontal);
518       break;
519     case PROP_VISIBLE_VERTICAL:
520       g_value_set_boolean (value, action->private_data->visible_vertical);
521       break;
522     case PROP_VISIBLE_OVERFLOWN:
523       g_value_set_boolean (value, action->private_data->visible_overflown);
524       break;
525     case PROP_IS_IMPORTANT:
526       g_value_set_boolean (value, action->private_data->is_important);
527       break;
528     case PROP_HIDE_IF_EMPTY:
529       g_value_set_boolean (value, action->private_data->hide_if_empty);
530       break;
531     case PROP_SENSITIVE:
532       g_value_set_boolean (value, action->private_data->sensitive);
533       break;
534     case PROP_VISIBLE:
535       g_value_set_boolean (value, action->private_data->visible);
536       break;
537     case PROP_ACTION_GROUP:
538       g_value_set_object (value, action->private_data->action_group);
539       break;
540     default:
541       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
542       break;
543     }
544 }
545
546 static GtkWidget *
547 create_menu_item (GtkAction *action)
548 {
549   GType menu_item_type;
550
551   menu_item_type = GTK_ACTION_GET_CLASS (action)->menu_item_type;
552
553   return g_object_new (menu_item_type, NULL);
554 }
555
556 static GtkWidget *
557 create_tool_item (GtkAction *action)
558 {
559   GType toolbar_item_type;
560
561   toolbar_item_type = GTK_ACTION_GET_CLASS (action)->toolbar_item_type;
562
563   return g_object_new (toolbar_item_type, NULL);
564 }
565
566 static void
567 remove_proxy (GtkWidget *proxy, 
568               GtkAction *action)
569 {
570   if (GTK_IS_MENU_ITEM (proxy))
571     gtk_action_disconnect_accelerator (action);
572
573   action->private_data->proxies = g_slist_remove (action->private_data->proxies, proxy);
574 }
575
576 static void
577 gtk_action_sync_property (GtkAction  *action, 
578                           GParamSpec *pspec,
579                           GtkWidget  *proxy)
580 {
581   const gchar *property;
582   GValue value = { 0, };
583
584   property = g_param_spec_get_name (pspec);
585
586   g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
587   g_object_get_property (G_OBJECT (action), property, &value);
588
589   g_object_set_property (G_OBJECT (proxy), property, &value);
590   g_value_unset (&value);
591 }
592
593 /**
594  * _gtk_action_sync_menu_visible:
595  * @action: a #GtkAction, or %NULL to determine the action from @proxy
596  * @proxy: a proxy menu item
597  * @empty: whether the submenu attached to @proxy is empty
598  * 
599  * Updates the visibility of @proxy from the visibility of @action
600  * according to the following rules:
601  * <itemizedlist>
602  * <listitem><para>if @action is invisible, @proxy is too
603  * </para></listitem>
604  * <listitem><para>if @empty is %TRUE, hide @proxy unless the "hide-if-empty" 
605  *   property of @action indicates otherwise
606  * </para></listitem>
607  * </itemizedlist>
608  * 
609  * This function is used in the implementation of #GtkUIManager.
610  **/
611 void
612 _gtk_action_sync_menu_visible (GtkAction *action,
613                                GtkWidget *proxy,
614                                gboolean   empty)
615 {
616   gboolean visible, hide_if_empty;
617
618   g_return_if_fail (GTK_IS_MENU_ITEM (proxy));
619   g_return_if_fail (action == NULL || GTK_IS_ACTION (action));
620   
621   if (action == NULL)
622     action = g_object_get_data (G_OBJECT (proxy), "gtk-action");
623
624   visible = gtk_action_is_visible (action);
625   hide_if_empty = action->private_data->hide_if_empty;
626
627   if (visible && !(empty && hide_if_empty))
628     gtk_widget_show (proxy);
629   else
630     gtk_widget_hide (proxy);
631 }
632
633 gboolean _gtk_menu_is_empty (GtkWidget *menu);
634
635 static void
636 gtk_action_sync_stock_id (GtkAction  *action, 
637                           GParamSpec *pspec,
638                           GtkWidget  *proxy)
639 {
640   GtkWidget *image = NULL;
641
642   if (GTK_IS_IMAGE_MENU_ITEM (proxy))
643     {
644       image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy));
645
646       if (GTK_IS_IMAGE (image))
647         gtk_image_set_from_stock (GTK_IMAGE (image),
648                                   action->private_data->stock_id, GTK_ICON_SIZE_MENU);
649     }
650 }
651
652 static gboolean
653 gtk_action_create_menu_proxy (GtkToolItem *tool_item, 
654                               GtkAction   *action)
655 {
656   GtkWidget *menu_item;
657   
658   if (action->private_data->visible_overflown)
659     {
660       menu_item = gtk_action_create_menu_item (action);
661
662       g_object_ref_sink (menu_item);
663       
664       gtk_tool_item_set_proxy_menu_item (tool_item, 
665                                          "gtk-action-menu-item", menu_item);
666       g_object_unref (menu_item);
667     }
668   else
669     gtk_tool_item_set_proxy_menu_item (tool_item, 
670                                        "gtk-action-menu-item", NULL);
671
672   return TRUE;
673 }
674
675 static void
676 connect_proxy (GtkAction     *action, 
677                GtkWidget     *proxy)
678 {
679   g_object_ref (action);
680   g_object_set_data_full (G_OBJECT (proxy), I_("gtk-action"), action,
681                           g_object_unref);
682
683   /* add this widget to the list of proxies */
684   action->private_data->proxies = g_slist_prepend (action->private_data->proxies, proxy);
685   g_signal_connect (proxy, "destroy",
686                     G_CALLBACK (remove_proxy), action);
687
688   gtk_widget_set_sensitive (proxy, gtk_action_is_sensitive (action));
689   if (gtk_action_is_visible (action))
690     gtk_widget_show (proxy);
691   else
692     gtk_widget_hide (proxy);
693   gtk_widget_set_no_show_all (proxy, TRUE);
694
695   if (GTK_IS_MENU_ITEM (proxy))
696     {
697       GtkWidget *label;
698       /* menu item specific synchronisers ... */
699       
700       if (action->private_data->accel_quark)
701         {
702           gtk_action_connect_accelerator (action);
703           gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy),
704                                         g_quark_to_string (action->private_data->accel_quark));
705         }
706       
707       label = GTK_BIN (proxy)->child;
708
709       /* make sure label is a label */
710       if (label && !GTK_IS_LABEL (label))
711         {
712           gtk_container_remove (GTK_CONTAINER (proxy), label);
713           label = NULL;
714         }
715
716       if (!label)
717         label = g_object_new (GTK_TYPE_ACCEL_LABEL,
718                               "use_underline", TRUE,
719                               "xalign", 0.0,
720                               "visible", TRUE,
721                               "parent", proxy,
722                               NULL);
723       
724       if (GTK_IS_ACCEL_LABEL (label) && action->private_data->accel_quark)
725         g_object_set (label,
726                       "accel_closure", action->private_data->accel_closure,
727                       NULL);
728
729       gtk_label_set_label (GTK_LABEL (label), action->private_data->label);
730
731       if (GTK_IS_IMAGE_MENU_ITEM (proxy))
732         {
733           GtkWidget *image;
734
735           image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy));
736           if (image && !GTK_IS_IMAGE (image))
737             {
738               gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (proxy), NULL);
739               image = NULL;
740             }
741           if (!image)
742             {
743               image = gtk_image_new_from_stock (NULL,
744                                                 GTK_ICON_SIZE_MENU);
745               gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (proxy),
746                                              image);
747               gtk_widget_show (image);
748             }
749           gtk_image_set_from_stock (GTK_IMAGE (image),
750                                     action->private_data->stock_id, GTK_ICON_SIZE_MENU);
751         }
752
753       if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy)) == NULL)
754         g_signal_connect_object (proxy, "activate",
755                                  G_CALLBACK (gtk_action_activate), action,
756                                  G_CONNECT_SWAPPED);
757
758     }
759   else if (GTK_IS_TOOL_ITEM (proxy))
760     {
761       /* toolbar item specific synchronisers ... */
762
763       g_object_set (proxy,
764                     "visible_horizontal", action->private_data->visible_horizontal,
765                     "visible_vertical", action->private_data->visible_vertical,
766                     "is_important", action->private_data->is_important,
767                     NULL);
768
769       gtk_action_sync_tooltip (action, proxy);
770
771       g_signal_connect_object (proxy, "create_menu_proxy",
772                                G_CALLBACK (gtk_action_create_menu_proxy),
773                                action, 0);
774
775       gtk_tool_item_rebuild_menu (GTK_TOOL_ITEM (proxy));
776
777       /* toolbar button specific synchronisers ... */
778       if (GTK_IS_TOOL_BUTTON (proxy))
779         {
780           g_object_set (proxy,
781                         "label", action->private_data->short_label,
782                         "use_underline", TRUE,
783                         "stock_id", action->private_data->stock_id,
784                         NULL);
785
786           g_signal_connect_object (proxy, "clicked",
787                                    G_CALLBACK (gtk_action_activate), action,
788                                    G_CONNECT_SWAPPED);
789         }
790     }
791   else if (GTK_IS_BUTTON (proxy))
792     {
793       /* button specific synchronisers ... */
794       if (gtk_button_get_use_stock (GTK_BUTTON (proxy)))
795         {
796           /* synchronise stock-id */
797           g_object_set (proxy,
798                         "label", action->private_data->stock_id,
799                         NULL);
800         }
801       else if (GTK_BIN (proxy)->child == NULL || 
802                GTK_IS_LABEL (GTK_BIN (proxy)->child))
803         {
804           /* synchronise the label */
805           g_object_set (proxy,
806                         "label", action->private_data->short_label,
807                         "use_underline", TRUE,
808                         NULL);
809         }
810       
811       /* we leave the button alone if there is a custom child */
812       g_signal_connect_object (proxy, "clicked",
813                                G_CALLBACK (gtk_action_activate), action,
814                                G_CONNECT_SWAPPED);
815     }
816
817   if (action->private_data->action_group)
818     _gtk_action_group_emit_connect_proxy (action->private_data->action_group, action, proxy);
819 }
820
821 static void
822 disconnect_proxy (GtkAction *action, 
823                   GtkWidget *proxy)
824 {
825   g_object_set_data (G_OBJECT (proxy), I_("gtk-action"), NULL);
826
827   /* remove proxy from list of proxies */
828   g_signal_handlers_disconnect_by_func (proxy,
829                                         G_CALLBACK (remove_proxy),
830                                         action);
831   remove_proxy (proxy, action);
832
833   /* disconnect the activate handler */
834   g_signal_handlers_disconnect_by_func (proxy,
835                                         G_CALLBACK (gtk_action_activate),
836                                         action);
837
838   /* disconnect handlers for notify::* signals */
839   g_signal_handlers_disconnect_by_func (action,
840                                         G_CALLBACK (gtk_action_sync_property),
841                                         proxy);
842
843   g_signal_handlers_disconnect_by_func (action,
844                                 G_CALLBACK (gtk_action_sync_stock_id), proxy);
845
846   /* toolbar button specific synchronisers ... */
847   g_signal_handlers_disconnect_by_func (proxy,
848                                         G_CALLBACK (gtk_action_create_menu_proxy),
849                                         action);
850
851   if (action->private_data->action_group)
852     _gtk_action_group_emit_disconnect_proxy (action->private_data->action_group, action, proxy);
853 }
854
855 void
856 _gtk_action_emit_activate (GtkAction *action)
857 {
858   GtkActionGroup *group = action->private_data->action_group;
859
860   if (group != NULL) 
861     {
862       g_object_ref (group);
863       _gtk_action_group_emit_pre_activate (group, action);
864     }
865
866     g_signal_emit (action, action_signals[ACTIVATE], 0);
867
868   if (group != NULL) 
869     {
870       _gtk_action_group_emit_post_activate (group, action);
871       g_object_unref (group);
872     }
873 }
874
875 /**
876  * gtk_action_activate:
877  * @action: the action object
878  *
879  * Emits the "activate" signal on the specified action, if it isn't 
880  * insensitive. This gets called by the proxy widgets when they get 
881  * activated.
882  *
883  * It can also be used to manually activate an action.
884  *
885  * Since: 2.4
886  */
887 void
888 gtk_action_activate (GtkAction *action)
889 {
890   g_return_if_fail (GTK_IS_ACTION (action));
891   
892   if (gtk_action_is_sensitive (action))
893     _gtk_action_emit_activate (action);
894 }
895
896 /**
897  * gtk_action_create_icon:
898  * @action: the action object
899  * @icon_size: the size of the icon that should be created.
900  *
901  * This function is intended for use by action implementations to
902  * create icons displayed in the proxy widgets.
903  *
904  * Returns: a widget that displays the icon for this action.
905  *
906  * Since: 2.4
907  */
908 GtkWidget *
909 gtk_action_create_icon (GtkAction *action, GtkIconSize icon_size)
910 {
911   g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
912
913   if (action->private_data->stock_id)
914     return gtk_image_new_from_stock (action->private_data->stock_id, icon_size);
915   else
916     return NULL;
917 }
918
919 /**
920  * gtk_action_create_menu_item:
921  * @action: the action object
922  *
923  * Creates a menu item widget that proxies for the given action.
924  *
925  * Returns: a menu item connected to the action.
926  *
927  * Since: 2.4
928  */
929 GtkWidget *
930 gtk_action_create_menu_item (GtkAction *action)
931 {
932   GtkWidget *menu_item;
933
934   g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
935
936   menu_item = (* GTK_ACTION_GET_CLASS (action)->create_menu_item) (action);
937
938   (* GTK_ACTION_GET_CLASS (action)->connect_proxy) (action, menu_item);
939
940   return menu_item;
941 }
942
943 /**
944  * gtk_action_create_tool_item:
945  * @action: the action object
946  *
947  * Creates a toolbar item widget that proxies for the given action.
948  *
949  * Returns: a toolbar item connected to the action.
950  *
951  * Since: 2.4
952  */
953 GtkWidget *
954 gtk_action_create_tool_item (GtkAction *action)
955 {
956   GtkWidget *button;
957
958   g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
959
960   button = (* GTK_ACTION_GET_CLASS (action)->create_tool_item) (action);
961
962   (* GTK_ACTION_GET_CLASS (action)->connect_proxy) (action, button);
963
964   return button;
965 }
966
967 /**
968  * gtk_action_connect_proxy:
969  * @action: the action object
970  * @proxy: the proxy widget
971  *
972  * Connects a widget to an action object as a proxy.  Synchronises 
973  * various properties of the action with the widget (such as label 
974  * text, icon, tooltip, etc), and attaches a callback so that the 
975  * action gets activated when the proxy widget does.
976  *
977  * If the widget is already connected to an action, it is disconnected
978  * first.
979  *
980  * Since: 2.4
981  */
982 void
983 gtk_action_connect_proxy (GtkAction *action,
984                           GtkWidget *proxy)
985 {
986   GtkAction *prev_action;
987
988   g_return_if_fail (GTK_IS_ACTION (action));
989   g_return_if_fail (GTK_IS_WIDGET (proxy));
990
991   prev_action = g_object_get_data (G_OBJECT (proxy), "gtk-action");
992
993   if (prev_action)
994     (* GTK_ACTION_GET_CLASS (action)->disconnect_proxy) (prev_action, proxy);  
995
996   (* GTK_ACTION_GET_CLASS (action)->connect_proxy) (action, proxy);
997 }
998
999 /**
1000  * gtk_action_disconnect_proxy:
1001  * @action: the action object
1002  * @proxy: the proxy widget
1003  *
1004  * Disconnects a proxy widget from an action.  
1005  * Does <emphasis>not</emphasis> destroy the widget, however.
1006  *
1007  * Since: 2.4
1008  */
1009 void
1010 gtk_action_disconnect_proxy (GtkAction *action,
1011                              GtkWidget *proxy)
1012 {
1013   g_return_if_fail (GTK_IS_ACTION (action));
1014   g_return_if_fail (GTK_IS_WIDGET (proxy));
1015
1016   g_return_if_fail (g_object_get_data (G_OBJECT (proxy), "gtk-action") == action);
1017
1018   (* GTK_ACTION_GET_CLASS (action)->disconnect_proxy) (action, proxy);  
1019 }
1020
1021 /**
1022  * gtk_action_get_proxies:
1023  * @action: the action object
1024  * 
1025  * Returns the proxy widgets for an action.
1026  * 
1027  * Return value: a #GSList of proxy widgets. The list is owned by the action and
1028  * must not be modified.
1029  *
1030  * Since: 2.4
1031  **/
1032 GSList*
1033 gtk_action_get_proxies (GtkAction *action)
1034 {
1035   g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
1036
1037   return action->private_data->proxies;
1038 }
1039
1040
1041 /**
1042  * gtk_action_get_name:
1043  * @action: the action object
1044  * 
1045  * Returns the name of the action.
1046  * 
1047  * Return value: the name of the action. The string belongs to GTK+ and should not
1048  *   be freed.
1049  *
1050  * Since: 2.4
1051  **/
1052 G_CONST_RETURN gchar *
1053 gtk_action_get_name (GtkAction *action)
1054 {
1055   g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
1056
1057   return action->private_data->name;
1058 }
1059
1060 /**
1061  * gtk_action_is_sensitive:
1062  * @action: the action object
1063  * 
1064  * Returns whether the action is effectively sensitive.
1065  *
1066  * Return value: %TRUE if the action and its associated action group 
1067  * are both sensitive.
1068  *
1069  * Since: 2.4
1070  **/
1071 gboolean
1072 gtk_action_is_sensitive (GtkAction *action)
1073 {
1074   GtkActionPrivate *priv;
1075   g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
1076
1077   priv = action->private_data;
1078   return priv->sensitive &&
1079     (priv->action_group == NULL ||
1080      gtk_action_group_get_sensitive (priv->action_group));
1081 }
1082
1083 /**
1084  * gtk_action_get_sensitive:
1085  * @action: the action object
1086  * 
1087  * Returns whether the action itself is sensitive. Note that this doesn't 
1088  * necessarily mean effective sensitivity. See gtk_action_is_sensitive() 
1089  * for that.
1090  *
1091  * Return value: %TRUE if the action itself is sensitive.
1092  *
1093  * Since: 2.4
1094  **/
1095 gboolean
1096 gtk_action_get_sensitive (GtkAction *action)
1097 {
1098   g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
1099
1100   return action->private_data->sensitive;
1101 }
1102
1103 /**
1104  * gtk_action_set_sensitive:
1105  * @action: the action object
1106  * @sensitive: %TRUE to make the action sensitive
1107  * 
1108  * Sets the ::sensitive property of the action to @sensitive. Note that 
1109  * this doesn't necessarily mean effective sensitivity. See 
1110  * gtk_action_is_sensitive() 
1111  * for that.
1112  *
1113  * Since: 2.6
1114  **/
1115 void
1116 gtk_action_set_sensitive (GtkAction *action,
1117                           gboolean   sensitive)
1118 {
1119   GSList *p;
1120   GtkWidget *proxy;
1121
1122   g_return_if_fail (GTK_IS_ACTION (action));
1123
1124   sensitive = sensitive != FALSE;
1125   
1126   if (action->private_data->sensitive != sensitive)
1127     {
1128       action->private_data->sensitive = sensitive;
1129
1130       for (p = action->private_data->proxies; p; p = p->next)
1131         {
1132           proxy = (GtkWidget *)p->data;
1133           gtk_widget_set_sensitive (proxy, sensitive);
1134         }
1135       
1136       g_object_notify (G_OBJECT (action), "sensitive");
1137     }
1138 }
1139
1140 /**
1141  * gtk_action_is_visible:
1142  * @action: the action object
1143  * 
1144  * Returns whether the action is effectively visible.
1145  *
1146  * Return value: %TRUE if the action and its associated action group 
1147  * are both visible.
1148  *
1149  * Since: 2.4
1150  **/
1151 gboolean
1152 gtk_action_is_visible (GtkAction *action)
1153 {
1154   GtkActionPrivate *priv;
1155   g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
1156
1157   priv = action->private_data;
1158   return priv->visible &&
1159     (priv->action_group == NULL ||
1160      gtk_action_group_get_visible (priv->action_group));
1161 }
1162
1163 /**
1164  * gtk_action_get_visible:
1165  * @action: the action object
1166  * 
1167  * Returns whether the action itself is visible. Note that this doesn't 
1168  * necessarily mean effective visibility. See gtk_action_is_sensitive() 
1169  * for that.
1170  *
1171  * Return value: %TRUE if the action itself is visible.
1172  *
1173  * Since: 2.4
1174  **/
1175 gboolean
1176 gtk_action_get_visible (GtkAction *action)
1177 {
1178   g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
1179
1180   return action->private_data->visible;
1181 }
1182
1183 /**
1184  * gtk_action_set_visible:
1185  * @action: the action object
1186  * @visible: %TRUE to make the action visible
1187  * 
1188  * Sets the ::visible property of the action to @visible. Note that 
1189  * this doesn't necessarily mean effective visibility. See 
1190  * gtk_action_is_visible() 
1191  * for that.
1192  *
1193  * Since: 2.6
1194  **/
1195 void
1196 gtk_action_set_visible (GtkAction *action,
1197                         gboolean   visible)
1198 {
1199   GSList *p;
1200   GtkWidget *proxy;
1201
1202   g_return_if_fail (GTK_IS_ACTION (action));
1203
1204   visible = visible != FALSE;
1205   
1206   if (action->private_data->visible != visible)
1207     {
1208       action->private_data->visible = visible;
1209
1210       for (p = action->private_data->proxies; p; p = p->next)
1211         {
1212           proxy = (GtkWidget *)p->data;
1213
1214           if (GTK_IS_MENU_ITEM (proxy))
1215             {
1216               GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (proxy));
1217               
1218               _gtk_action_sync_menu_visible (action, proxy, _gtk_menu_is_empty (menu));
1219             }
1220           else
1221             {
1222               if (visible)
1223                 gtk_widget_show (proxy);
1224               else
1225                 gtk_widget_hide (proxy);
1226             }
1227         }
1228       
1229       g_object_notify (G_OBJECT (action), "visible");
1230     }
1231 }
1232
1233 static void 
1234 gtk_action_set_is_important (GtkAction *action,
1235                              gboolean   is_important)
1236 {
1237   GSList *p;
1238   GtkWidget *proxy;
1239
1240   is_important = is_important != FALSE;
1241   
1242   if (action->private_data->is_important != is_important)
1243     {
1244       action->private_data->is_important = is_important;
1245
1246       for (p = action->private_data->proxies; p; p = p->next)
1247         {
1248           proxy = (GtkWidget *)p->data;
1249
1250           if (GTK_IS_TOOL_ITEM (proxy))
1251             gtk_tool_item_set_is_important (GTK_TOOL_ITEM (proxy),
1252                                             is_important);
1253         }
1254       
1255       g_object_notify (G_OBJECT (action), "is-important");
1256     }  
1257 }
1258
1259 static void 
1260 gtk_action_set_label (GtkAction   *action,
1261                       const gchar *label)
1262 {
1263   GSList *p;
1264   GtkWidget *proxy, *child;
1265   gchar *tmp;
1266   
1267   tmp = action->private_data->label;
1268   action->private_data->label = g_strdup (label);
1269   g_free (tmp);
1270   action->private_data->label_set = (action->private_data->label != NULL);
1271   /* if label is unset, then use the label from the stock item */
1272   if (!action->private_data->label_set && action->private_data->stock_id)
1273     {
1274       GtkStockItem stock_item;
1275       
1276       if (gtk_stock_lookup (action->private_data->stock_id, &stock_item))
1277         action->private_data->label = g_strdup (stock_item.label);
1278     }
1279   
1280   for (p = action->private_data->proxies; p; p = p->next)
1281     {
1282       proxy = (GtkWidget *)p->data;
1283       
1284       if (GTK_IS_MENU_ITEM (proxy))
1285         {
1286           child = GTK_BIN (proxy)->child;
1287           
1288           if (GTK_IS_LABEL (child))
1289             gtk_label_set_label (GTK_LABEL (child), 
1290                                  action->private_data->label);
1291         }
1292     }
1293   
1294   g_object_notify (G_OBJECT (action), "label");
1295   
1296   /* if short_label is unset, set short_label=label */
1297   if (!action->private_data->short_label_set)
1298     {
1299       gtk_action_set_short_label (action, action->private_data->label);
1300       action->private_data->short_label_set = FALSE;
1301     }
1302 }
1303
1304 static void 
1305 gtk_action_set_short_label (GtkAction   *action,
1306                             const gchar *label)
1307 {
1308   GSList *p;
1309   GtkWidget *proxy, *child;
1310   gchar *tmp;
1311
1312   tmp = action->private_data->short_label;
1313   action->private_data->short_label = g_strdup (label);
1314   g_free (tmp);
1315   action->private_data->short_label_set = (action->private_data->short_label != NULL);
1316   /* if short_label is unset, then use the value of label */
1317   if (!action->private_data->short_label_set)
1318     action->private_data->short_label = g_strdup (action->private_data->label);
1319
1320   for (p = action->private_data->proxies; p; p = p->next)
1321     {
1322       proxy = (GtkWidget *)p->data;
1323
1324       if (GTK_IS_TOOL_BUTTON (proxy))
1325         gtk_tool_button_set_label (GTK_TOOL_BUTTON (proxy), 
1326                                    action->private_data->label);
1327       else if (GTK_IS_BUTTON (proxy) &&
1328                !gtk_button_get_use_stock (GTK_BUTTON (proxy)))
1329         {
1330           child = GTK_BIN (proxy)->child;
1331           
1332           if (child == NULL || GTK_IS_LABEL (child))
1333             gtk_button_set_label (GTK_BUTTON (proxy), 
1334                                   action->private_data->label);
1335         }
1336     }
1337
1338   g_object_notify (G_OBJECT (action), "short-label");
1339 }
1340
1341 static void 
1342 gtk_action_set_visible_horizontal (GtkAction *action,
1343                                    gboolean   visible_horizontal)
1344 {
1345   GSList *p;
1346   GtkWidget *proxy;
1347
1348   visible_horizontal = visible_horizontal != FALSE;
1349   
1350   if (action->private_data->visible_horizontal != visible_horizontal)
1351     {
1352       action->private_data->visible_horizontal = visible_horizontal;
1353
1354       for (p = action->private_data->proxies; p; p = p->next)
1355         {
1356           proxy = (GtkWidget *)p->data;
1357
1358           if (GTK_IS_TOOL_ITEM (proxy))
1359             gtk_tool_item_set_visible_horizontal (GTK_TOOL_ITEM (proxy),
1360                                                   visible_horizontal);
1361         }
1362       
1363       g_object_notify (G_OBJECT (action), "visible-horizontal");
1364     }  
1365 }
1366
1367 static void 
1368 gtk_action_set_visible_vertical (GtkAction *action,
1369                                  gboolean   visible_vertical)
1370 {
1371   GSList *p;
1372   GtkWidget *proxy;
1373
1374   visible_vertical = visible_vertical != FALSE;
1375   
1376   if (action->private_data->visible_vertical != visible_vertical)
1377     {
1378       action->private_data->visible_vertical = visible_vertical;
1379
1380       for (p = action->private_data->proxies; p; p = p->next)
1381         {
1382           proxy = (GtkWidget *)p->data;
1383
1384           if (GTK_IS_TOOL_ITEM (proxy))
1385             gtk_tool_item_set_visible_vertical (GTK_TOOL_ITEM (proxy),
1386                                                 visible_vertical);
1387         }
1388       
1389       g_object_notify (G_OBJECT (action), "visible-vertical");
1390     }  
1391 }
1392
1393 static void 
1394 gtk_action_sync_tooltip (GtkAction *action,
1395                          GtkWidget *proxy)
1396 {
1397   GtkWidget *parent;
1398
1399   parent = gtk_widget_get_parent (proxy);
1400   
1401   if (GTK_IS_TOOL_ITEM (proxy) && GTK_IS_TOOLBAR (parent))
1402     gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (proxy), 
1403                                GTK_TOOLBAR (parent)->tooltips,
1404                                action->private_data->tooltip,
1405                                NULL);
1406 }
1407
1408 static void 
1409 gtk_action_set_tooltip (GtkAction   *action,
1410                         const gchar *tooltip)
1411 {
1412   GSList *p;
1413   GtkWidget *proxy;
1414   gchar *tmp;
1415
1416   tmp = action->private_data->tooltip;
1417   action->private_data->tooltip = g_strdup (tooltip);
1418   g_free (tmp);
1419
1420   for (p = action->private_data->proxies; p; p = p->next)
1421     {
1422       proxy = (GtkWidget *)p->data;
1423       
1424       gtk_action_sync_tooltip (action, proxy);
1425     }
1426
1427   g_object_notify (G_OBJECT (action), "tooltip");
1428 }
1429
1430 static void 
1431 gtk_action_set_stock_id (GtkAction   *action,
1432                          const gchar *stock_id)
1433 {
1434   GSList *p;
1435   GtkWidget *proxy, *image;
1436   gchar *tmp;
1437   
1438   tmp = action->private_data->stock_id;
1439   action->private_data->stock_id = g_strdup (stock_id);
1440   g_free (tmp);
1441
1442   for (p = action->private_data->proxies; p; p = p->next)
1443     {
1444       proxy = (GtkWidget *)p->data;
1445       
1446       if (GTK_IS_IMAGE_MENU_ITEM (proxy))
1447         {
1448           image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy));
1449           
1450           if (GTK_IS_IMAGE (image))
1451             gtk_image_set_from_stock (GTK_IMAGE (image),
1452                                       action->private_data->stock_id, GTK_ICON_SIZE_MENU);
1453         } 
1454       else if (GTK_IS_TOOL_BUTTON (proxy))
1455         {
1456           gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (proxy),
1457                                         action->private_data->stock_id);
1458         }
1459       else if (GTK_IS_BUTTON (proxy) &&
1460                gtk_button_get_use_stock (GTK_BUTTON (proxy)))
1461         {
1462           gtk_button_set_label (GTK_BUTTON (proxy),
1463                                 action->private_data->stock_id);
1464         }
1465     }
1466
1467   g_object_notify (G_OBJECT (action), "stock-id");
1468   
1469   /* update label and short_label if appropriate */
1470   if (!action->private_data->label_set)
1471     {
1472       GtkStockItem stock_item;
1473       
1474       if (gtk_stock_lookup (action->private_data->stock_id, &stock_item))
1475         gtk_action_set_label (action, stock_item.label);
1476       else 
1477         gtk_action_set_label (action, NULL);
1478       
1479       action->private_data->label_set = FALSE;
1480     }
1481 }
1482
1483
1484 /**
1485  * gtk_action_block_activate_from:
1486  * @action: the action object
1487  * @proxy: a proxy widget
1488  *
1489  * Disables calls to the gtk_action_activate()
1490  * function by signals on the given proxy widget.  This is used to
1491  * break notification loops for things like check or radio actions.
1492  *
1493  * This function is intended for use by action implementations.
1494  * 
1495  * Since: 2.4
1496  */
1497 void
1498 gtk_action_block_activate_from (GtkAction *action, 
1499                                 GtkWidget *proxy)
1500 {
1501   g_return_if_fail (GTK_IS_ACTION (action));
1502   
1503   g_signal_handlers_block_by_func (proxy, G_CALLBACK (gtk_action_activate),
1504                                    action);
1505 }
1506
1507 /**
1508  * gtk_action_unblock_activate_from:
1509  * @action: the action object
1510  * @proxy: a proxy widget
1511  *
1512  * Re-enables calls to the gtk_action_activate()
1513  * function by signals on the given proxy widget.  This undoes the
1514  * blocking done by gtk_action_block_activate_from().
1515  *
1516  * This function is intended for use by action implementations.
1517  * 
1518  * Since: 2.4
1519  */
1520 void
1521 gtk_action_unblock_activate_from (GtkAction *action, 
1522                                   GtkWidget *proxy)
1523 {
1524   g_return_if_fail (GTK_IS_ACTION (action));
1525
1526   g_signal_handlers_unblock_by_func (proxy, G_CALLBACK (gtk_action_activate),
1527                                      action);
1528 }
1529
1530 static void
1531 closure_accel_activate (GClosure     *closure,
1532                         GValue       *return_value,
1533                         guint         n_param_values,
1534                         const GValue *param_values,
1535                         gpointer      invocation_hint,
1536                         gpointer      marshal_data)
1537 {
1538   if (gtk_action_is_sensitive (GTK_ACTION (closure->data)))
1539     {
1540       _gtk_action_emit_activate (GTK_ACTION (closure->data));
1541       
1542       /* we handled the accelerator */
1543       g_value_set_boolean (return_value, TRUE);
1544     }
1545 }
1546
1547 static void
1548 gtk_action_set_action_group (GtkAction      *action,
1549                              GtkActionGroup *action_group)
1550 {
1551   g_return_if_fail (GTK_IS_ACTION (action));
1552
1553   if (action->private_data->action_group == NULL)
1554     g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1555   else
1556     g_return_if_fail (action_group == NULL);
1557
1558   action->private_data->action_group = action_group;
1559 }
1560
1561 /**
1562  * gtk_action_set_accel_path:
1563  * @action: the action object
1564  * @accel_path: the accelerator path
1565  *
1566  * Sets the accel path for this action.  All proxy widgets associated
1567  * with the action will have this accel path, so that their
1568  * accelerators are consistent.
1569  *
1570  * Since: 2.4
1571  */
1572 void
1573 gtk_action_set_accel_path (GtkAction   *action, 
1574                            const gchar *accel_path)
1575 {
1576   g_return_if_fail (GTK_IS_ACTION (action));
1577
1578   action->private_data->accel_quark = g_quark_from_string (accel_path);
1579 }
1580
1581 /**
1582  * gtk_action_get_accel_path:
1583  * @action: the action object
1584  *
1585  * Returns the accel path for this action.  
1586  *
1587  * Since: 2.6
1588  *
1589  * Returns: the accel path for this action, or %NULL
1590  *   if none is set. The returned string is owned by GTK+ 
1591  *   and must not be freed or modified.
1592  */
1593 G_CONST_RETURN gchar *
1594 gtk_action_get_accel_path (GtkAction *action)
1595 {
1596   g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
1597
1598   if (action->private_data->accel_quark)
1599     return g_quark_to_string (action->private_data->accel_quark);
1600   else
1601     return NULL;
1602 }
1603
1604 /**
1605  * gtk_action_get_accel_closure:
1606  * @action: the action object
1607  *
1608  * Returns the accel closure for this action.
1609  *
1610  * Since: 2.8
1611  *
1612  * Returns: the accel closure for this action. The returned closure is
1613  *          owned by GTK+ and must not be unreffed or modified.
1614  */
1615 GClosure *
1616 gtk_action_get_accel_closure (GtkAction *action)
1617 {
1618   g_return_val_if_fail (GTK_IS_ACTION (action), NULL);
1619
1620   return action->private_data->accel_closure;
1621 }
1622
1623
1624 /**
1625  * gtk_action_set_accel_group:
1626  * @action: the action object
1627  * @accel_group: a #GtkAccelGroup or %NULL
1628  * 
1629  * Sets the #GtkAccelGroup in which the accelerator for this action
1630  * will be installed.
1631  *
1632  * Since: 2.4
1633  **/
1634 void
1635 gtk_action_set_accel_group (GtkAction     *action,
1636                             GtkAccelGroup *accel_group)
1637 {
1638   g_return_if_fail (GTK_IS_ACTION (action));
1639   g_return_if_fail (accel_group == NULL || GTK_IS_ACCEL_GROUP (accel_group));
1640   
1641   if (accel_group)
1642     g_object_ref (accel_group);
1643   if (action->private_data->accel_group)
1644     g_object_unref (action->private_data->accel_group);
1645
1646   action->private_data->accel_group = accel_group;
1647 }
1648
1649 /**
1650  * gtk_action_connect_accelerator:
1651  * @action: a #GtkAction
1652  * 
1653  * Installs the accelerator for @action if @action has an
1654  * accel path and group. See gtk_action_set_accel_path() and 
1655  * gtk_action_set_accel_group()
1656  *
1657  * Since multiple proxies may independently trigger the installation
1658  * of the accelerator, the @action counts the number of times this
1659  * function has been called and doesn't remove the accelerator until
1660  * gtk_action_disconnect_accelerator() has been called as many times.
1661  *
1662  * Since: 2.4
1663  **/
1664 void 
1665 gtk_action_connect_accelerator (GtkAction *action)
1666 {
1667   g_return_if_fail (GTK_IS_ACTION (action));
1668
1669   if (!action->private_data->accel_quark ||
1670       !action->private_data->accel_group)
1671     return;
1672
1673   if (action->private_data->accel_count == 0)
1674     {
1675       const gchar *accel_path = 
1676         g_quark_to_string (action->private_data->accel_quark);
1677       
1678       gtk_accel_group_connect_by_path (action->private_data->accel_group,
1679                                        accel_path,
1680                                        action->private_data->accel_closure);
1681     }
1682
1683   action->private_data->accel_count++;
1684 }
1685
1686 /**
1687  * gtk_action_disconnect_accelerator:
1688  * @action: a #GtkAction
1689  * 
1690  * Undoes the effect of one call to gtk_action_connect_accelerator().
1691  *
1692  * Since: 2.4
1693  **/
1694 void 
1695 gtk_action_disconnect_accelerator (GtkAction *action)
1696 {
1697   g_return_if_fail (GTK_IS_ACTION (action));
1698
1699   if (!action->private_data->accel_quark ||
1700       !action->private_data->accel_group)
1701     return;
1702
1703   action->private_data->accel_count--;
1704
1705   if (action->private_data->accel_count == 0)
1706     gtk_accel_group_disconnect (action->private_data->accel_group,
1707                                 action->private_data->accel_closure);
1708 }
1709
1710 #define __GTK_ACTION_C__
1711 #include "gtkaliasdef.c"