]> Pileus Git - ~andy/gtk/blob - gtk/gtkactiongroup.c
Seal GtkActionGroup
[~andy/gtk] / gtk / gtkactiongroup.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 #include <string.h>
33
34 #include "gtkactiongroup.h"
35 #include "gtkbuildable.h"
36 #include "gtkiconfactory.h"
37 #include "gtkicontheme.h"
38 #include "gtkstock.h"
39 #include "gtktoggleaction.h"
40 #include "gtkradioaction.h"
41 #include "gtkaccelmap.h"
42 #include "gtkmarshalers.h"
43 #include "gtkbuilderprivate.h"
44 #include "gtkprivate.h"
45 #include "gtkintl.h"
46 #include "gtkalias.h"
47
48 #define GTK_ACTION_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ACTION_GROUP, GtkActionGroupPrivate))
49
50 struct _GtkActionGroupPrivate 
51 {
52   gchar           *name;
53   gboolean         sensitive;
54   gboolean         visible;
55   GHashTable      *actions;
56
57   GtkTranslateFunc translate_func;
58   gpointer         translate_data;
59   GDestroyNotify   translate_notify;
60 };
61
62 enum 
63 {
64   CONNECT_PROXY,
65   DISCONNECT_PROXY,
66   PRE_ACTIVATE,
67   POST_ACTIVATE,
68   LAST_SIGNAL
69 };
70
71 enum 
72 {
73   PROP_0,
74   PROP_NAME,
75   PROP_SENSITIVE,
76   PROP_VISIBLE
77 };
78
79 static void       gtk_action_group_init            (GtkActionGroup      *self);
80 static void       gtk_action_group_class_init      (GtkActionGroupClass *class);
81 static void       gtk_action_group_finalize        (GObject             *object);
82 static void       gtk_action_group_set_property    (GObject             *object,
83                                                     guint                prop_id,
84                                                     const GValue        *value,
85                                                     GParamSpec          *pspec);
86 static void       gtk_action_group_get_property    (GObject             *object,
87                                                     guint                prop_id,
88                                                     GValue              *value,
89                                                     GParamSpec          *pspec);
90 static GtkAction *gtk_action_group_real_get_action (GtkActionGroup      *self,
91                                                     const gchar         *name);
92
93 /* GtkBuildable */
94 static void gtk_action_group_buildable_init (GtkBuildableIface *iface);
95 static void gtk_action_group_buildable_add_child (GtkBuildable  *buildable,
96                                                   GtkBuilder    *builder,
97                                                   GObject       *child,
98                                                   const gchar   *type);
99 static void gtk_action_group_buildable_set_name (GtkBuildable *buildable,
100                                                  const gchar  *name);
101 static const gchar* gtk_action_group_buildable_get_name (GtkBuildable *buildable);
102 static gboolean gtk_action_group_buildable_custom_tag_start (GtkBuildable     *buildable,
103                                                              GtkBuilder       *builder,
104                                                              GObject          *child,
105                                                              const gchar      *tagname,
106                                                              GMarkupParser    *parser,
107                                                              gpointer         *data);
108 static void gtk_action_group_buildable_custom_tag_end (GtkBuildable *buildable,
109                                                        GtkBuilder   *builder,
110                                                        GObject      *child,
111                                                        const gchar  *tagname,
112                                                        gpointer     *user_data);
113
114 GType
115 gtk_action_group_get_type (void)
116 {
117   static GType type = 0;
118
119   if (!type)
120     {
121       const GTypeInfo type_info =
122       {
123         sizeof (GtkActionGroupClass),
124         NULL,           /* base_init */
125         NULL,           /* base_finalize */
126         (GClassInitFunc) gtk_action_group_class_init,
127         NULL,           /* class_finalize */
128         NULL,           /* class_data */
129         sizeof (GtkActionGroup),
130         0, /* n_preallocs */
131         (GInstanceInitFunc) gtk_action_group_init,
132       };
133
134       static const GInterfaceInfo buildable_info =
135       {
136         (GInterfaceInitFunc) gtk_action_group_buildable_init,
137         NULL,
138         NULL
139       };
140
141       type = g_type_register_static (G_TYPE_OBJECT, I_("GtkActionGroup"),
142                                      &type_info, 0);
143
144       g_type_add_interface_static (type,
145                                    GTK_TYPE_BUILDABLE,
146                                    &buildable_info);
147     }
148   return type;
149 }
150
151 static GObjectClass *parent_class = NULL;
152 static guint         action_group_signals[LAST_SIGNAL] = { 0 };
153
154 static void
155 gtk_action_group_class_init (GtkActionGroupClass *klass)
156 {
157   GObjectClass *gobject_class;
158
159   gobject_class = G_OBJECT_CLASS (klass);
160   parent_class = g_type_class_peek_parent (klass);
161
162   gobject_class->finalize = gtk_action_group_finalize;
163   gobject_class->set_property = gtk_action_group_set_property;
164   gobject_class->get_property = gtk_action_group_get_property;
165   klass->get_action = gtk_action_group_real_get_action;
166
167   g_object_class_install_property (gobject_class,
168                                    PROP_NAME,
169                                    g_param_spec_string ("name",
170                                                         P_("Name"),
171                                                         P_("A name for the action group."),
172                                                         NULL,
173                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
174   g_object_class_install_property (gobject_class,
175                                    PROP_SENSITIVE,
176                                    g_param_spec_boolean ("sensitive",
177                                                          P_("Sensitive"),
178                                                          P_("Whether the action group is enabled."),
179                                                          TRUE,
180                                                          GTK_PARAM_READWRITE));
181   g_object_class_install_property (gobject_class,
182                                    PROP_VISIBLE,
183                                    g_param_spec_boolean ("visible",
184                                                          P_("Visible"),
185                                                          P_("Whether the action group is visible."),
186                                                          TRUE,
187                                                          GTK_PARAM_READWRITE));
188
189   /**
190    * GtkActionGroup::connect-proxy:
191    * @action_group: the group
192    * @action: the action
193    * @proxy: the proxy
194    *
195    * The connect_proxy signal is emitted after connecting a proxy to 
196    * an action in the group. Note that the proxy may have been connected 
197    * to a different action before.
198    *
199    * This is intended for simple customizations for which a custom action
200    * class would be too clumsy, e.g. showing tooltips for menuitems in the
201    * statusbar.
202    *
203    * #GtkUIManager proxies the signal and provides global notification 
204    * just before any action is connected to a proxy, which is probably more
205    * convenient to use.
206    *
207    * Since: 2.4
208    */
209   action_group_signals[CONNECT_PROXY] =
210     g_signal_new (I_("connect_proxy"),
211                   G_OBJECT_CLASS_TYPE (klass),
212                   0, 0, NULL, NULL,
213                   _gtk_marshal_VOID__OBJECT_OBJECT,
214                   G_TYPE_NONE, 2,
215                   GTK_TYPE_ACTION, GTK_TYPE_WIDGET);
216
217   /**
218    * GtkActionGroup::disconnect-proxy:
219    * @action_group: the group
220    * @action: the action
221    * @proxy: the proxy
222    *
223    * The disconnect_proxy signal is emitted after disconnecting a proxy 
224    * from an action in the group. 
225    *
226    * #GtkUIManager proxies the signal and provides global notification 
227    * just before any action is connected to a proxy, which is probably more
228    * convenient to use.
229    *
230    * Since: 2.4
231    */
232   action_group_signals[DISCONNECT_PROXY] =
233     g_signal_new (I_("disconnect_proxy"),
234                   G_OBJECT_CLASS_TYPE (klass),
235                   0, 0, NULL, NULL,
236                   _gtk_marshal_VOID__OBJECT_OBJECT,
237                   G_TYPE_NONE, 2, 
238                   GTK_TYPE_ACTION, GTK_TYPE_WIDGET);
239
240   /**
241    * GtkActionGroup::pre-activate:
242    * @action_group: the group
243    * @action: the action
244    *
245    * The pre_activate signal is emitted just before the @action in the
246    * @action_group is activated
247    *
248    * This is intended for #GtkUIManager to proxy the signal and provide global
249    * notification just before any action is activated.
250    *
251    * Since: 2.4
252    */
253   action_group_signals[PRE_ACTIVATE] =
254     g_signal_new (I_("pre_activate"),
255                   G_OBJECT_CLASS_TYPE (klass),
256                   0, 0, NULL, NULL,
257                   _gtk_marshal_VOID__OBJECT,
258                   G_TYPE_NONE, 1, 
259                   GTK_TYPE_ACTION);
260
261   /**
262    * GtkActionGroup::post-activate:
263    * @action_group: the group
264    * @action: the action
265    *
266    * The post_activate signal is emitted just after the @action in the
267    * @action_group is activated
268    *
269    * This is intended for #GtkUIManager to proxy the signal and provide global
270    * notification just after any action is activated.
271    *
272    * Since: 2.4
273    */
274   action_group_signals[POST_ACTIVATE] =
275     g_signal_new (I_("post_activate"),
276                   G_OBJECT_CLASS_TYPE (klass),
277                   0, 0, NULL, NULL,
278                   _gtk_marshal_VOID__OBJECT,
279                   G_TYPE_NONE, 1, 
280                   GTK_TYPE_ACTION);
281
282   g_type_class_add_private (gobject_class, sizeof (GtkActionGroupPrivate));
283 }
284
285
286 static void 
287 remove_action (GtkAction *action) 
288 {
289   g_object_set (action, I_("action-group"), NULL, NULL);
290   g_object_unref (action);
291 }
292
293 static void
294 gtk_action_group_init (GtkActionGroup *self)
295 {
296   GtkActionGroupPrivate *private;
297
298   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
299
300   private->name = NULL;
301   private->sensitive = TRUE;
302   private->visible = TRUE;
303   private->actions = g_hash_table_new_full (g_str_hash, g_str_equal,
304                                             NULL,
305                                             (GDestroyNotify) remove_action);
306   private->translate_func = NULL;
307   private->translate_data = NULL;
308   private->translate_notify = NULL;
309 }
310
311 static void
312 gtk_action_group_buildable_init (GtkBuildableIface *iface)
313 {
314   iface->add_child = gtk_action_group_buildable_add_child;
315   iface->set_name = gtk_action_group_buildable_set_name;
316   iface->get_name = gtk_action_group_buildable_get_name;
317   iface->custom_tag_start = gtk_action_group_buildable_custom_tag_start;
318   iface->custom_tag_end = gtk_action_group_buildable_custom_tag_end;
319 }
320
321 static void
322 gtk_action_group_buildable_add_child (GtkBuildable  *buildable,
323                                       GtkBuilder    *builder,
324                                       GObject       *child,
325                                       const gchar   *type)
326 {
327   gtk_action_group_add_action_with_accel (GTK_ACTION_GROUP (buildable),
328                                           GTK_ACTION (child), NULL);
329 }
330
331 static void
332 gtk_action_group_buildable_set_name (GtkBuildable *buildable,
333                                      const gchar  *name)
334 {
335   GtkActionGroup *self = GTK_ACTION_GROUP (buildable);
336   GtkActionGroupPrivate *private = GTK_ACTION_GROUP_GET_PRIVATE (self);
337
338   private->name = g_strdup (name);
339 }
340
341 static const gchar *
342 gtk_action_group_buildable_get_name (GtkBuildable *buildable)
343 {
344   GtkActionGroup *self = GTK_ACTION_GROUP (buildable);
345   GtkActionGroupPrivate *private = GTK_ACTION_GROUP_GET_PRIVATE (self);
346   return private->name;
347 }
348
349 typedef struct {
350   GObject         *child;
351   guint            key;
352   GdkModifierType  modifiers;
353 } AcceleratorParserData;
354
355 static void
356 accelerator_start_element (GMarkupParseContext *context,
357                            const gchar         *element_name,
358                            const gchar        **names,
359                            const gchar        **values,
360                            gpointer             user_data,
361                            GError             **error)
362 {
363   gint i;
364   guint key = 0;
365   GdkModifierType modifiers = 0;
366   AcceleratorParserData *parser_data = (AcceleratorParserData*)user_data;
367
368   if (strcmp (element_name, "accelerator") != 0)
369     g_warning ("Unknown <accelerator> tag: %s", element_name);
370
371   for (i = 0; names[i]; i++)
372     {
373       if (strcmp (names[i], "key") == 0)
374         key = gdk_keyval_from_name (values[i]);
375       else if (strcmp (names[i], "modifiers") == 0)
376         {
377           if (!_gtk_builder_flags_from_string (GDK_TYPE_MODIFIER_TYPE,
378                                                values[i],
379                                                &modifiers,
380                                                error))
381               return;
382         }
383     }
384
385   if (key == 0)
386     {
387       g_warning ("<accelerator> requires a key attribute");
388       return;
389     }
390   parser_data->key = key;
391   parser_data->modifiers = modifiers;
392 }
393
394 static const GMarkupParser accelerator_parser =
395   {
396     accelerator_start_element
397   };
398
399 static gboolean
400 gtk_action_group_buildable_custom_tag_start (GtkBuildable     *buildable,
401                                              GtkBuilder       *builder,
402                                              GObject          *child,
403                                              const gchar      *tagname,
404                                              GMarkupParser    *parser,
405                                              gpointer         *user_data)
406 {
407   AcceleratorParserData *parser_data;
408
409   if (child && strcmp (tagname, "accelerator") == 0)
410     {
411       parser_data = g_slice_new0 (AcceleratorParserData);
412       parser_data->child = child;
413       *user_data = parser_data;
414       *parser = accelerator_parser;
415
416       return TRUE;
417     }
418   return FALSE;
419 }
420
421 static void
422 gtk_action_group_buildable_custom_tag_end (GtkBuildable *buildable,
423                                            GtkBuilder   *builder,
424                                            GObject      *child,
425                                            const gchar  *tagname,
426                                            gpointer     *user_data)
427 {
428   AcceleratorParserData *data;
429   
430   if (strcmp (tagname, "accelerator") == 0)
431     {
432       GtkActionGroup *action_group;
433       GtkActionGroupPrivate *private;
434       GtkAction *action;
435       gchar *accel_path;
436       
437       data = (AcceleratorParserData*)user_data;
438       action_group = GTK_ACTION_GROUP (buildable);
439       private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
440       action = GTK_ACTION (child);
441         
442       accel_path = g_strconcat ("<Actions>/",
443                                 private->name, "/",
444                                 gtk_action_get_name (action), NULL);
445
446       if (gtk_accel_map_lookup_entry (accel_path, NULL))
447         gtk_accel_map_change_entry (accel_path, data->key, data->modifiers, TRUE);
448       else
449         gtk_accel_map_add_entry (accel_path, data->key, data->modifiers);
450
451       gtk_action_set_accel_path (action, accel_path);
452       
453       g_free (accel_path);
454       g_slice_free (AcceleratorParserData, data);
455     }
456 }
457
458 /**
459  * gtk_action_group_new:
460  * @name: the name of the action group.
461  *
462  * Creates a new #GtkActionGroup object. The name of the action group
463  * is used when associating <link linkend="Action-Accel">keybindings</link> 
464  * with the actions.
465  *
466  * Returns: the new #GtkActionGroup
467  *
468  * Since: 2.4
469  */
470 GtkActionGroup *
471 gtk_action_group_new (const gchar *name)
472 {
473   GtkActionGroup *self;
474   GtkActionGroupPrivate *private;
475
476   self = g_object_new (GTK_TYPE_ACTION_GROUP, NULL);
477   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
478   private->name = g_strdup (name);
479
480   return self;
481 }
482
483 static void
484 gtk_action_group_finalize (GObject *object)
485 {
486   GtkActionGroup *self;
487   GtkActionGroupPrivate *private;
488
489   self = GTK_ACTION_GROUP (object);
490   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
491
492   g_free (private->name);
493   private->name = NULL;
494
495   g_hash_table_destroy (private->actions);
496   private->actions = NULL;
497
498   if (private->translate_notify)
499     private->translate_notify (private->translate_data);
500
501   if (parent_class->finalize)
502     (* parent_class->finalize) (object);
503 }
504
505 static void
506 gtk_action_group_set_property (GObject         *object,
507                                guint            prop_id,
508                                const GValue    *value,
509                                GParamSpec      *pspec)
510 {
511   GtkActionGroup *self;
512   GtkActionGroupPrivate *private;
513   gchar *tmp;
514   
515   self = GTK_ACTION_GROUP (object);
516   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
517
518   switch (prop_id)
519     {
520     case PROP_NAME:
521       tmp = private->name;
522       private->name = g_value_dup_string (value);
523       g_free (tmp);
524       break;
525     case PROP_SENSITIVE:
526       gtk_action_group_set_sensitive (self, g_value_get_boolean (value));
527       break;
528     case PROP_VISIBLE:
529       gtk_action_group_set_visible (self, g_value_get_boolean (value));
530       break;
531     default:
532       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
533       break;
534     }
535 }
536
537 static void
538 gtk_action_group_get_property (GObject    *object,
539                                guint       prop_id,
540                                GValue     *value,
541                                GParamSpec *pspec)
542 {
543   GtkActionGroup *self;
544   GtkActionGroupPrivate *private;
545   
546   self = GTK_ACTION_GROUP (object);
547   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
548
549   switch (prop_id)
550     {
551     case PROP_NAME:
552       g_value_set_string (value, private->name);
553       break;
554     case PROP_SENSITIVE:
555       g_value_set_boolean (value, private->sensitive);
556       break;
557     case PROP_VISIBLE:
558       g_value_set_boolean (value, private->visible);
559       break;
560     default:
561       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
562       break;
563     }
564 }
565
566 static GtkAction *
567 gtk_action_group_real_get_action (GtkActionGroup *self,
568                                   const gchar    *action_name)
569 {
570   GtkActionGroupPrivate *private;
571
572   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
573
574   return g_hash_table_lookup (private->actions, action_name);
575 }
576
577 /**
578  * gtk_action_group_get_name:
579  * @action_group: the action group
580  *
581  * Gets the name of the action group.
582  *
583  * Returns: the name of the action group.
584  * 
585  * Since: 2.4
586  */
587 G_CONST_RETURN gchar *
588 gtk_action_group_get_name (GtkActionGroup *action_group)
589 {
590   GtkActionGroupPrivate *private;
591
592   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
593
594   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
595
596   return private->name;
597 }
598
599 /**
600  * gtk_action_group_get_sensitive:
601  * @action_group: the action group
602  *
603  * Returns %TRUE if the group is sensitive.  The constituent actions
604  * can only be logically sensitive (see gtk_action_is_sensitive()) if
605  * they are sensitive (see gtk_action_get_sensitive()) and their group
606  * is sensitive.
607  * 
608  * Return value: %TRUE if the group is sensitive.
609  *
610  * Since: 2.4
611  */
612 gboolean
613 gtk_action_group_get_sensitive (GtkActionGroup *action_group)
614 {
615   GtkActionGroupPrivate *private;
616
617   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), FALSE);
618
619   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
620
621   return private->sensitive;
622 }
623
624 static void
625 cb_set_action_sensitivity (const gchar *name, 
626                            GtkAction   *action)
627 {
628   /* Minor optimization, the action_groups state only affects actions 
629    * that are themselves sensitive */
630   if (gtk_action_get_sensitive (action))
631     _gtk_action_sync_sensitive (action);
632 }
633
634 /**
635  * gtk_action_group_set_sensitive:
636  * @action_group: the action group
637  * @sensitive: new sensitivity
638  *
639  * Changes the sensitivity of @action_group
640  * 
641  * Since: 2.4
642  */
643 void
644 gtk_action_group_set_sensitive (GtkActionGroup *action_group, 
645                                 gboolean        sensitive)
646 {
647   GtkActionGroupPrivate *private;
648
649   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
650
651   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
652   sensitive = sensitive != FALSE;
653
654   if (private->sensitive != sensitive)
655     {
656       private->sensitive = sensitive;
657       g_hash_table_foreach (private->actions, 
658                             (GHFunc) cb_set_action_sensitivity, NULL);
659
660       g_object_notify (G_OBJECT (action_group), "sensitive");
661     }
662 }
663
664 /**
665  * gtk_action_group_get_visible:
666  * @action_group: the action group
667  *
668  * Returns %TRUE if the group is visible.  The constituent actions
669  * can only be logically visible (see gtk_action_is_visible()) if
670  * they are visible (see gtk_action_get_visible()) and their group
671  * is visible.
672  * 
673  * Return value: %TRUE if the group is visible.
674  * 
675  * Since: 2.4
676  */
677 gboolean
678 gtk_action_group_get_visible (GtkActionGroup *action_group)
679 {
680   GtkActionGroupPrivate *private;
681
682   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), FALSE);
683
684   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
685
686   return private->visible;
687 }
688
689 static void
690 cb_set_action_visiblity (const gchar *name, 
691                          GtkAction   *action)
692 {
693   /* Minor optimization, the action_groups state only affects actions 
694    * that are themselves visible */
695   if (gtk_action_get_visible (action))
696     _gtk_action_sync_visible (action);
697 }
698
699 /**
700  * gtk_action_group_set_visible:
701  * @action_group: the action group
702  * @visible: new visiblity
703  *
704  * Changes the visible of @action_group.
705  * 
706  * Since: 2.4
707  */
708 void
709 gtk_action_group_set_visible (GtkActionGroup *action_group, 
710                               gboolean        visible)
711 {
712   GtkActionGroupPrivate *private;
713
714   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
715
716   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
717   visible = visible != FALSE;
718
719   if (private->visible != visible)
720     {
721       private->visible = visible;
722       g_hash_table_foreach (private->actions, 
723                             (GHFunc) cb_set_action_visiblity, NULL);
724
725       g_object_notify (G_OBJECT (action_group), "visible");
726     }
727 }
728
729 /**
730  * gtk_action_group_get_action:
731  * @action_group: the action group
732  * @action_name: the name of the action
733  *
734  * Looks up an action in the action group by name.
735  *
736  * Returns: the action, or %NULL if no action by that name exists
737  *
738  * Since: 2.4
739  */
740 GtkAction *
741 gtk_action_group_get_action (GtkActionGroup *action_group,
742                              const gchar    *action_name)
743 {
744   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
745   g_return_val_if_fail (GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action != NULL, NULL);
746
747   return (* GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action)
748     (action_group, action_name);
749 }
750
751 static gboolean
752 check_unique_action (GtkActionGroup *action_group,
753                      const gchar    *action_name)
754 {
755   if (gtk_action_group_get_action (action_group, action_name) != NULL)
756     {
757       GtkActionGroupPrivate *private;
758
759       private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
760
761       g_warning ("Refusing to add non-unique action '%s' to action group '%s'",
762                  action_name,
763                  private->name);
764       return FALSE;
765     }
766
767   return TRUE;
768 }
769
770 /**
771  * gtk_action_group_add_action:
772  * @action_group: the action group
773  * @action: an action
774  *
775  * Adds an action object to the action group. Note that this function
776  * does not set up the accel path of the action, which can lead to problems
777  * if a user tries to modify the accelerator of a menuitem associated with
778  * the action. Therefore you must either set the accel path yourself with
779  * gtk_action_set_accel_path(), or use 
780  * <literal>gtk_action_group_add_action_with_accel (..., NULL)</literal>.
781  *
782  * Since: 2.4
783  */
784 void
785 gtk_action_group_add_action (GtkActionGroup *action_group,
786                              GtkAction      *action)
787 {
788   GtkActionGroupPrivate *private;
789   const gchar *name;
790
791   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
792   g_return_if_fail (GTK_IS_ACTION (action));
793
794   name = gtk_action_get_name (action);
795   g_return_if_fail (name != NULL);
796   
797   if (!check_unique_action (action_group, name))
798     return;
799
800   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
801
802   g_hash_table_insert (private->actions, 
803                        (gpointer) name,
804                        g_object_ref (action));
805   g_object_set (action, I_("action-group"), action_group, NULL);
806 }
807
808 /**
809  * gtk_action_group_add_action_with_accel:
810  * @action_group: the action group 
811  * @action: the action to add 
812  * @accelerator: the accelerator for the action, in
813  *   the format understood by gtk_accelerator_parse(), or "" for no accelerator, or 
814  *   %NULL to use the stock accelerator 
815  *
816  * Adds an action object to the action group and sets up the accelerator.
817  *
818  * If @accelerator is %NULL, attempts to use the accelerator associated 
819  * with the stock_id of the action. 
820  *
821  * Accel paths are set to
822  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.
823  *
824  * Since: 2.4
825  */
826 void
827 gtk_action_group_add_action_with_accel (GtkActionGroup *action_group,
828                                         GtkAction      *action,
829                                         const gchar    *accelerator)
830 {
831   GtkActionGroupPrivate *private;
832   gchar *accel_path;
833   guint  accel_key = 0;
834   GdkModifierType accel_mods;
835   const gchar *name;
836
837   name = gtk_action_get_name (action);
838   if (!check_unique_action (action_group, name))
839     return;
840
841   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
842   accel_path = g_strconcat ("<Actions>/",
843                             private->name, "/", name, NULL);
844
845   if (accelerator)
846     {
847       if (accelerator[0] == 0) 
848         accel_key = 0;
849       else
850         {
851           gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
852           if (accel_key == 0)
853             g_warning ("Unable to parse accelerator '%s' for action '%s'",
854                        accelerator, name);
855         }
856     }
857   else 
858     {
859       gchar *stock_id;
860       GtkStockItem stock_item;
861
862       g_object_get (action, "stock-id", &stock_id, NULL);
863
864       if (stock_id && gtk_stock_lookup (stock_id, &stock_item))
865         {
866           accel_key = stock_item.keyval;
867           accel_mods = stock_item.modifier;
868         }
869
870       g_free (stock_id);
871     }
872
873   if (accel_key)
874     gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
875
876   gtk_action_set_accel_path (action, accel_path);
877   gtk_action_group_add_action (action_group, action);
878
879   g_free (accel_path);
880 }
881
882 /**
883  * gtk_action_group_remove_action:
884  * @action_group: the action group
885  * @action: an action
886  *
887  * Removes an action object from the action group.
888  *
889  * Since: 2.4
890  */
891 void
892 gtk_action_group_remove_action (GtkActionGroup *action_group,
893                                 GtkAction      *action)
894 {
895   GtkActionGroupPrivate *private;
896   const gchar *name;
897
898   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
899   g_return_if_fail (GTK_IS_ACTION (action));
900
901   name = gtk_action_get_name (action);
902   g_return_if_fail (name != NULL);
903
904   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
905
906   g_hash_table_remove (private->actions, name);
907 }
908
909 static void
910 add_single_action (gpointer key, 
911                    gpointer value, 
912                    gpointer user_data)
913 {
914   GList **list = user_data;
915
916   *list = g_list_prepend (*list, value);
917 }
918
919 /**
920  * gtk_action_group_list_actions:
921  * @action_group: the action group
922  *
923  * Lists the actions in the action group.
924  *
925  * Returns: an allocated list of the action objects in the action group
926  * 
927  * Since: 2.4
928  */
929 GList *
930 gtk_action_group_list_actions (GtkActionGroup *action_group)
931 {
932   GtkActionGroupPrivate *private;
933   GList *actions = NULL;
934
935   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
936
937   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
938   
939   g_hash_table_foreach (private->actions, add_single_action, &actions);
940
941   return g_list_reverse (actions);
942 }
943
944
945 /**
946  * gtk_action_group_add_actions:
947  * @action_group: the action group
948  * @entries: an array of action descriptions
949  * @n_entries: the number of entries
950  * @user_data: data to pass to the action callbacks
951  *
952  * This is a convenience function to create a number of actions and add them 
953  * to the action group.
954  *
955  * The "activate" signals of the actions are connected to the callbacks and 
956  * their accel paths are set to 
957  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
958  * 
959  * Since: 2.4
960  */
961 void
962 gtk_action_group_add_actions (GtkActionGroup       *action_group,
963                               const GtkActionEntry *entries,
964                               guint                 n_entries,
965                               gpointer              user_data)
966 {
967   gtk_action_group_add_actions_full (action_group, 
968                                      entries, n_entries, 
969                                      user_data, NULL);
970 }
971
972 typedef struct _SharedData  SharedData;
973
974 struct _SharedData {
975   guint          ref_count;
976   gpointer       data;
977   GDestroyNotify destroy;
978 };
979
980 static void
981 shared_data_unref (gpointer data)
982 {
983   SharedData *shared_data = (SharedData *)data;
984
985   shared_data->ref_count--;
986   if (shared_data->ref_count == 0)
987     {
988       if (shared_data->destroy) 
989         (*shared_data->destroy) (shared_data->data);
990       
991       g_slice_free (SharedData, shared_data);
992     }
993 }
994
995
996 /**
997  * gtk_action_group_add_actions_full:
998  * @action_group: the action group
999  * @entries: an array of action descriptions
1000  * @n_entries: the number of entries
1001  * @user_data: data to pass to the action callbacks
1002  * @destroy: destroy notification callback for @user_data
1003  *
1004  * This variant of gtk_action_group_add_actions() adds a #GDestroyNotify
1005  * callback for @user_data. 
1006  * 
1007  * Since: 2.4
1008  */
1009 void
1010 gtk_action_group_add_actions_full (GtkActionGroup       *action_group,
1011                                    const GtkActionEntry *entries,
1012                                    guint                 n_entries,
1013                                    gpointer              user_data,
1014                                    GDestroyNotify        destroy)
1015 {
1016
1017   /* Keep this in sync with the other 
1018    * gtk_action_group_add_..._actions_full() functions.
1019    */
1020   guint i;
1021   SharedData *shared_data;
1022
1023   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1024
1025   shared_data = g_slice_new0 (SharedData);
1026   shared_data->ref_count = 1;
1027   shared_data->data = user_data;
1028   shared_data->destroy = destroy;
1029
1030   for (i = 0; i < n_entries; i++)
1031     {
1032       GtkAction *action;
1033       const gchar *label;
1034       const gchar *tooltip;
1035
1036       if (!check_unique_action (action_group, entries[i].name))
1037         continue;
1038
1039       label = gtk_action_group_translate_string (action_group, entries[i].label);
1040       tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
1041
1042       action = gtk_action_new (entries[i].name,
1043                                label,
1044                                tooltip,
1045                                NULL);
1046
1047       if (entries[i].stock_id) 
1048         {
1049           g_object_set (action, "stock-id", entries[i].stock_id, NULL);
1050           if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), 
1051                                        entries[i].stock_id))
1052             g_object_set (action, "icon-name", entries[i].stock_id, NULL);
1053         }
1054           
1055       if (entries[i].callback)
1056         {
1057           GClosure *closure;
1058
1059           closure = g_cclosure_new (entries[i].callback, user_data, NULL);
1060           g_closure_add_finalize_notifier (closure, shared_data, 
1061                                            (GClosureNotify)shared_data_unref);
1062           shared_data->ref_count++;
1063
1064           g_signal_connect_closure (action, "activate", closure, FALSE);
1065         }
1066           
1067       gtk_action_group_add_action_with_accel (action_group, 
1068                                               action,
1069                                               entries[i].accelerator);
1070       g_object_unref (action);
1071     }
1072
1073   shared_data_unref (shared_data);
1074 }
1075
1076 /**
1077  * gtk_action_group_add_toggle_actions:
1078  * @action_group: the action group
1079  * @entries: an array of toggle action descriptions
1080  * @n_entries: the number of entries
1081  * @user_data: data to pass to the action callbacks
1082  *
1083  * This is a convenience function to create a number of toggle actions and add them 
1084  * to the action group.
1085  *
1086  * The "activate" signals of the actions are connected to the callbacks and 
1087  * their accel paths are set to 
1088  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
1089  * 
1090  * Since: 2.4
1091  */
1092 void
1093 gtk_action_group_add_toggle_actions (GtkActionGroup             *action_group,
1094                                      const GtkToggleActionEntry *entries,
1095                                      guint                       n_entries,
1096                                      gpointer                    user_data)
1097 {
1098   gtk_action_group_add_toggle_actions_full (action_group, 
1099                                             entries, n_entries, 
1100                                             user_data, NULL);
1101 }
1102
1103
1104 /**
1105  * gtk_action_group_add_toggle_actions_full:
1106  * @action_group: the action group
1107  * @entries: an array of toggle action descriptions
1108  * @n_entries: the number of entries
1109  * @user_data: data to pass to the action callbacks
1110  * @destroy: destroy notification callback for @user_data
1111  *
1112  * This variant of gtk_action_group_add_toggle_actions() adds a 
1113  * #GDestroyNotify callback for @user_data. 
1114  * 
1115  * Since: 2.4
1116  */
1117 void
1118 gtk_action_group_add_toggle_actions_full (GtkActionGroup             *action_group,
1119                                           const GtkToggleActionEntry *entries,
1120                                           guint                       n_entries,
1121                                           gpointer                    user_data,
1122                                           GDestroyNotify              destroy)
1123 {
1124   /* Keep this in sync with the other 
1125    * gtk_action_group_add_..._actions_full() functions.
1126    */
1127   guint i;
1128   SharedData *shared_data;
1129
1130   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1131
1132   shared_data = g_slice_new0 (SharedData);
1133   shared_data->ref_count = 1;
1134   shared_data->data = user_data;
1135   shared_data->destroy = destroy;
1136
1137   for (i = 0; i < n_entries; i++)
1138     {
1139       GtkToggleAction *action;
1140       const gchar *label;
1141       const gchar *tooltip;
1142
1143       if (!check_unique_action (action_group, entries[i].name))
1144         continue;
1145
1146       label = gtk_action_group_translate_string (action_group, entries[i].label);
1147       tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
1148
1149       action = gtk_toggle_action_new (entries[i].name,
1150                                       label,
1151                                       tooltip,
1152                                       NULL);
1153
1154       if (entries[i].stock_id) 
1155         {
1156           if (gtk_icon_factory_lookup_default (entries[i].stock_id))
1157             g_object_set (action, "stock-id", entries[i].stock_id, NULL);
1158           else
1159             g_object_set (action, "icon-name", entries[i].stock_id, NULL);
1160         }
1161
1162       gtk_toggle_action_set_active (action, entries[i].is_active);
1163
1164       if (entries[i].callback)
1165         {
1166           GClosure *closure;
1167
1168           closure = g_cclosure_new (entries[i].callback, user_data, NULL);
1169           g_closure_add_finalize_notifier (closure, shared_data, 
1170                                            (GClosureNotify)shared_data_unref);
1171           shared_data->ref_count++;
1172
1173           g_signal_connect_closure (action, "activate", closure, FALSE);
1174         }
1175           
1176       gtk_action_group_add_action_with_accel (action_group, 
1177                                               GTK_ACTION (action),
1178                                               entries[i].accelerator);
1179       g_object_unref (action);
1180     }
1181
1182   shared_data_unref (shared_data);
1183 }
1184
1185 /**
1186  * gtk_action_group_add_radio_actions:
1187  * @action_group: the action group
1188  * @entries: an array of radio action descriptions
1189  * @n_entries: the number of entries
1190  * @value: the value of the action to activate initially, or -1 if
1191  *   no action should be activated
1192  * @on_change: the callback to connect to the changed signal
1193  * @user_data: data to pass to the action callbacks
1194  * 
1195  * This is a convenience routine to create a group of radio actions and
1196  * add them to the action group. 
1197  *
1198  * The "changed" signal of the first radio action is connected to the 
1199  * @on_change callback and the accel paths of the actions are set to 
1200  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
1201  * 
1202  * Since: 2.4
1203  **/
1204 void            
1205 gtk_action_group_add_radio_actions (GtkActionGroup            *action_group,
1206                                     const GtkRadioActionEntry *entries,
1207                                     guint                      n_entries,
1208                                     gint                       value,
1209                                     GCallback                  on_change,
1210                                     gpointer                   user_data)
1211 {
1212   gtk_action_group_add_radio_actions_full (action_group, 
1213                                            entries, n_entries, 
1214                                            value,
1215                                            on_change, user_data, NULL);
1216 }
1217
1218 /**
1219  * gtk_action_group_add_radio_actions_full:
1220  * @action_group: the action group
1221  * @entries: an array of radio action descriptions
1222  * @n_entries: the number of entries
1223  * @value: the value of the action to activate initially, or -1 if
1224  *   no action should be activated
1225  * @on_change: the callback to connect to the changed signal
1226  * @user_data: data to pass to the action callbacks
1227  * @destroy: destroy notification callback for @user_data
1228  *
1229  * This variant of gtk_action_group_add_radio_actions() adds a 
1230  * #GDestroyNotify callback for @user_data. 
1231  * 
1232  * Since: 2.4
1233  **/
1234 void            
1235 gtk_action_group_add_radio_actions_full (GtkActionGroup            *action_group,
1236                                          const GtkRadioActionEntry *entries,
1237                                          guint                      n_entries,
1238                                          gint                       value,
1239                                          GCallback                  on_change,
1240                                          gpointer                   user_data,
1241                                          GDestroyNotify             destroy)
1242 {
1243   /* Keep this in sync with the other 
1244    * gtk_action_group_add_..._actions_full() functions.
1245    */
1246   guint i;
1247   GSList *group = NULL;
1248   GtkRadioAction *first_action = NULL;
1249
1250   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1251
1252   for (i = 0; i < n_entries; i++)
1253     {
1254       GtkRadioAction *action;
1255       const gchar *label;
1256       const gchar *tooltip; 
1257
1258       if (!check_unique_action (action_group, entries[i].name))
1259         continue;
1260
1261       label = gtk_action_group_translate_string (action_group, entries[i].label);
1262       tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
1263
1264       action = gtk_radio_action_new (entries[i].name,
1265                                      label,
1266                                      tooltip,
1267                                      NULL,
1268                                      entries[i].value);
1269
1270       if (entries[i].stock_id) 
1271         {
1272           if (gtk_icon_factory_lookup_default (entries[i].stock_id))
1273             g_object_set (action, "stock-id", entries[i].stock_id, NULL);
1274           else
1275             g_object_set (action, "icon-name", entries[i].stock_id, NULL);
1276         }
1277
1278       if (i == 0) 
1279         first_action = action;
1280
1281       gtk_radio_action_set_group (action, group);
1282       group = gtk_radio_action_get_group (action);
1283
1284       if (value == entries[i].value)
1285         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1286
1287       gtk_action_group_add_action_with_accel (action_group, 
1288                                               GTK_ACTION (action),
1289                                               entries[i].accelerator);
1290       g_object_unref (action);
1291     }
1292
1293   if (on_change && first_action)
1294     g_signal_connect_data (first_action, "changed",
1295                            on_change, user_data, 
1296                            (GClosureNotify)destroy, 0);
1297 }
1298
1299 /**
1300  * gtk_action_group_set_translate_func:
1301  * @action_group: a #GtkActionGroup
1302  * @func: a #GtkTranslateFunc
1303  * @data: data to be passed to @func and @notify
1304  * @notify: a #GDestroyNotify function to be called when @action_group is
1305  *   destroyed and when the translation function is changed again
1306  *
1307  * Sets a function to be used for translating the @label and @tooltip of 
1308  * #GtkActionGroupEntry<!-- -->s added by gtk_action_group_add_actions().
1309  *
1310  * If you're using gettext(), it is enough to set the translation domain
1311  * with gtk_action_group_set_translation_domain().
1312  *
1313  * Since: 2.4 
1314  **/
1315 void
1316 gtk_action_group_set_translate_func (GtkActionGroup   *action_group,
1317                                      GtkTranslateFunc  func,
1318                                      gpointer          data,
1319                                      GDestroyNotify    notify)
1320 {
1321   GtkActionGroupPrivate *private;
1322
1323   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1324   
1325   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
1326
1327   if (private->translate_notify)
1328     private->translate_notify (private->translate_data);
1329       
1330   private->translate_func = func;
1331   private->translate_data = data;
1332   private->translate_notify = notify;
1333 }
1334
1335 static gchar *
1336 dgettext_swapped (const gchar *msgid, 
1337                   const gchar *domainname)
1338 {
1339   /* Pass through g_dgettext if and only if msgid is nonempty. */
1340   if (msgid && *msgid) 
1341     return (gchar*) g_dgettext (domainname, msgid); 
1342   else
1343     return (gchar*) msgid;
1344 }
1345
1346 /**
1347  * gtk_action_group_set_translation_domain:
1348  * @action_group: a #GtkActionGroup
1349  * @domain: the translation domain to use for g_dgettext() calls
1350  * 
1351  * Sets the translation domain and uses g_dgettext() for translating the 
1352  * @label and @tooltip of #GtkActionEntry<!-- -->s added by 
1353  * gtk_action_group_add_actions().
1354  *
1355  * If you're not using gettext() for localization, see 
1356  * gtk_action_group_set_translate_func().
1357  *
1358  * Since: 2.4
1359  **/
1360 void 
1361 gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
1362                                          const gchar    *domain)
1363 {
1364   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1365
1366   gtk_action_group_set_translate_func (action_group, 
1367                                        (GtkTranslateFunc)dgettext_swapped,
1368                                        g_strdup (domain),
1369                                        g_free);
1370
1371
1372
1373 /**
1374  * gtk_action_group_translate_string:
1375  * @action_group: a #GtkActionGroup
1376  * @string: a string
1377  *
1378  * Translates a string using the specified translate_func(). This
1379  * is mainly intended for language bindings.
1380  *
1381  * Returns: the translation of @string
1382  *
1383  * Since: 2.6
1384  **/
1385 G_CONST_RETURN gchar *
1386 gtk_action_group_translate_string (GtkActionGroup *action_group,
1387                                    const gchar    *string)
1388 {
1389   GtkActionGroupPrivate *private;
1390   GtkTranslateFunc translate_func;
1391   gpointer translate_data;
1392   
1393   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), string);
1394   
1395   if (string == NULL)
1396     return NULL;
1397
1398   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
1399
1400   translate_func = private->translate_func;
1401   translate_data = private->translate_data;
1402   
1403   if (translate_func)
1404     return translate_func (string, translate_data);
1405   else
1406     return string;
1407 }
1408
1409 /* Protected for use by GtkAction */
1410 void
1411 _gtk_action_group_emit_connect_proxy  (GtkActionGroup *action_group,
1412                                        GtkAction      *action,
1413                                        GtkWidget      *proxy)
1414 {
1415   g_signal_emit (action_group, action_group_signals[CONNECT_PROXY], 0, 
1416                  action, proxy);
1417 }
1418
1419 void
1420 _gtk_action_group_emit_disconnect_proxy  (GtkActionGroup *action_group,
1421                                           GtkAction      *action,
1422                                           GtkWidget      *proxy)
1423 {
1424   g_signal_emit (action_group, action_group_signals[DISCONNECT_PROXY], 0, 
1425                  action, proxy);
1426 }
1427
1428 void
1429 _gtk_action_group_emit_pre_activate  (GtkActionGroup *action_group,
1430                                       GtkAction      *action)
1431 {
1432   g_signal_emit (action_group, action_group_signals[PRE_ACTIVATE], 0, action);
1433 }
1434
1435 void
1436 _gtk_action_group_emit_post_activate (GtkActionGroup *action_group,
1437                                       GtkAction      *action)
1438 {
1439   g_signal_emit (action_group, action_group_signals[POST_ACTIVATE], 0, action);
1440 }
1441
1442 #define __GTK_ACTION_GROUP_C__
1443 #include "gtkaliasdef.c"