]> Pileus Git - ~andy/gtk/blob - gtk/gtkactiongroup.c
[introspection] Merge in Gtk-custom.c annotations
[~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       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   parent_class->finalize (object);
502 }
503
504 static void
505 gtk_action_group_set_property (GObject         *object,
506                                guint            prop_id,
507                                const GValue    *value,
508                                GParamSpec      *pspec)
509 {
510   GtkActionGroup *self;
511   GtkActionGroupPrivate *private;
512   gchar *tmp;
513   
514   self = GTK_ACTION_GROUP (object);
515   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
516
517   switch (prop_id)
518     {
519     case PROP_NAME:
520       tmp = private->name;
521       private->name = g_value_dup_string (value);
522       g_free (tmp);
523       break;
524     case PROP_SENSITIVE:
525       gtk_action_group_set_sensitive (self, g_value_get_boolean (value));
526       break;
527     case PROP_VISIBLE:
528       gtk_action_group_set_visible (self, g_value_get_boolean (value));
529       break;
530     default:
531       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
532       break;
533     }
534 }
535
536 static void
537 gtk_action_group_get_property (GObject    *object,
538                                guint       prop_id,
539                                GValue     *value,
540                                GParamSpec *pspec)
541 {
542   GtkActionGroup *self;
543   GtkActionGroupPrivate *private;
544   
545   self = GTK_ACTION_GROUP (object);
546   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
547
548   switch (prop_id)
549     {
550     case PROP_NAME:
551       g_value_set_string (value, private->name);
552       break;
553     case PROP_SENSITIVE:
554       g_value_set_boolean (value, private->sensitive);
555       break;
556     case PROP_VISIBLE:
557       g_value_set_boolean (value, private->visible);
558       break;
559     default:
560       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
561       break;
562     }
563 }
564
565 static GtkAction *
566 gtk_action_group_real_get_action (GtkActionGroup *self,
567                                   const gchar    *action_name)
568 {
569   GtkActionGroupPrivate *private;
570
571   private = GTK_ACTION_GROUP_GET_PRIVATE (self);
572
573   return g_hash_table_lookup (private->actions, action_name);
574 }
575
576 /**
577  * gtk_action_group_get_name:
578  * @action_group: the action group
579  *
580  * Gets the name of the action group.
581  *
582  * Returns: the name of the action group.
583  * 
584  * Since: 2.4
585  */
586 G_CONST_RETURN gchar *
587 gtk_action_group_get_name (GtkActionGroup *action_group)
588 {
589   GtkActionGroupPrivate *private;
590
591   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
592
593   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
594
595   return private->name;
596 }
597
598 /**
599  * gtk_action_group_get_sensitive:
600  * @action_group: the action group
601  *
602  * Returns %TRUE if the group is sensitive.  The constituent actions
603  * can only be logically sensitive (see gtk_action_is_sensitive()) if
604  * they are sensitive (see gtk_action_get_sensitive()) and their group
605  * is sensitive.
606  * 
607  * Return value: %TRUE if the group is sensitive.
608  *
609  * Since: 2.4
610  */
611 gboolean
612 gtk_action_group_get_sensitive (GtkActionGroup *action_group)
613 {
614   GtkActionGroupPrivate *private;
615
616   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), FALSE);
617
618   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
619
620   return private->sensitive;
621 }
622
623 static void
624 cb_set_action_sensitivity (const gchar *name, 
625                            GtkAction   *action)
626 {
627   /* Minor optimization, the action_groups state only affects actions 
628    * that are themselves sensitive */
629   g_object_notify (G_OBJECT (action), "sensitive");
630
631 }
632
633 /**
634  * gtk_action_group_set_sensitive:
635  * @action_group: the action group
636  * @sensitive: new sensitivity
637  *
638  * Changes the sensitivity of @action_group
639  * 
640  * Since: 2.4
641  */
642 void
643 gtk_action_group_set_sensitive (GtkActionGroup *action_group, 
644                                 gboolean        sensitive)
645 {
646   GtkActionGroupPrivate *private;
647
648   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
649
650   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
651   sensitive = sensitive != FALSE;
652
653   if (private->sensitive != sensitive)
654     {
655       private->sensitive = sensitive;
656       g_hash_table_foreach (private->actions, 
657                             (GHFunc) cb_set_action_sensitivity, NULL);
658
659       g_object_notify (G_OBJECT (action_group), "sensitive");
660     }
661 }
662
663 /**
664  * gtk_action_group_get_visible:
665  * @action_group: the action group
666  *
667  * Returns %TRUE if the group is visible.  The constituent actions
668  * can only be logically visible (see gtk_action_is_visible()) if
669  * they are visible (see gtk_action_get_visible()) and their group
670  * is visible.
671  * 
672  * Return value: %TRUE if the group is visible.
673  * 
674  * Since: 2.4
675  */
676 gboolean
677 gtk_action_group_get_visible (GtkActionGroup *action_group)
678 {
679   GtkActionGroupPrivate *private;
680
681   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), FALSE);
682
683   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
684
685   return private->visible;
686 }
687
688 static void
689 cb_set_action_visiblity (const gchar *name, 
690                          GtkAction   *action)
691 {
692   /* Minor optimization, the action_groups state only affects actions 
693    * that are themselves visible */
694   g_object_notify (G_OBJECT (action), "visible");
695 }
696
697 /**
698  * gtk_action_group_set_visible:
699  * @action_group: the action group
700  * @visible: new visiblity
701  *
702  * Changes the visible of @action_group.
703  * 
704  * Since: 2.4
705  */
706 void
707 gtk_action_group_set_visible (GtkActionGroup *action_group, 
708                               gboolean        visible)
709 {
710   GtkActionGroupPrivate *private;
711
712   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
713
714   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
715   visible = visible != FALSE;
716
717   if (private->visible != visible)
718     {
719       private->visible = visible;
720       g_hash_table_foreach (private->actions, 
721                             (GHFunc) cb_set_action_visiblity, NULL);
722
723       g_object_notify (G_OBJECT (action_group), "visible");
724     }
725 }
726
727 /**
728  * gtk_action_group_get_action:
729  * @action_group: the action group
730  * @action_name: the name of the action
731  *
732  * Looks up an action in the action group by name.
733  *
734  * Returns: (transfer-none): the action, or %NULL if no action by that name exists
735  *
736  * Since: 2.4
737  */
738 GtkAction *
739 gtk_action_group_get_action (GtkActionGroup *action_group,
740                              const gchar    *action_name)
741 {
742   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
743   g_return_val_if_fail (GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action != NULL, NULL);
744
745   return GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action (action_group,
746                                                                 action_name);
747 }
748
749 static gboolean
750 check_unique_action (GtkActionGroup *action_group,
751                      const gchar    *action_name)
752 {
753   if (gtk_action_group_get_action (action_group, action_name) != NULL)
754     {
755       GtkActionGroupPrivate *private;
756
757       private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
758
759       g_warning ("Refusing to add non-unique action '%s' to action group '%s'",
760                  action_name,
761                  private->name);
762       return FALSE;
763     }
764
765   return TRUE;
766 }
767
768 /**
769  * gtk_action_group_add_action:
770  * @action_group: the action group
771  * @action: an action
772  *
773  * Adds an action object to the action group. Note that this function
774  * does not set up the accel path of the action, which can lead to problems
775  * if a user tries to modify the accelerator of a menuitem associated with
776  * the action. Therefore you must either set the accel path yourself with
777  * gtk_action_set_accel_path(), or use 
778  * <literal>gtk_action_group_add_action_with_accel (..., NULL)</literal>.
779  *
780  * Since: 2.4
781  */
782 void
783 gtk_action_group_add_action (GtkActionGroup *action_group,
784                              GtkAction      *action)
785 {
786   GtkActionGroupPrivate *private;
787   const gchar *name;
788
789   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
790   g_return_if_fail (GTK_IS_ACTION (action));
791
792   name = gtk_action_get_name (action);
793   g_return_if_fail (name != NULL);
794   
795   if (!check_unique_action (action_group, name))
796     return;
797
798   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
799
800   g_hash_table_insert (private->actions, 
801                        (gpointer) name,
802                        g_object_ref (action));
803   g_object_set (action, I_("action-group"), action_group, NULL);
804 }
805
806 /**
807  * gtk_action_group_add_action_with_accel:
808  * @action_group: the action group
809  * @action: the action to add
810  * @accelerator: (allow-none): the accelerator for the action, in
811  *   the format understood by gtk_accelerator_parse(), or "" for no accelerator, or
812  *   %NULL to use the stock accelerator
813  *
814  * Adds an action object to the action group and sets up the accelerator.
815  *
816  * If @accelerator is %NULL, attempts to use the accelerator associated 
817  * with the stock_id of the action. 
818  *
819  * Accel paths are set to
820  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.
821  *
822  * Since: 2.4
823  */
824 void
825 gtk_action_group_add_action_with_accel (GtkActionGroup *action_group,
826                                         GtkAction      *action,
827                                         const gchar    *accelerator)
828 {
829   GtkActionGroupPrivate *private;
830   gchar *accel_path;
831   guint  accel_key = 0;
832   GdkModifierType accel_mods;
833   const gchar *name;
834
835   name = gtk_action_get_name (action);
836   if (!check_unique_action (action_group, name))
837     return;
838
839   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
840   accel_path = g_strconcat ("<Actions>/",
841                             private->name, "/", name, NULL);
842
843   if (accelerator)
844     {
845       if (accelerator[0] == 0) 
846         accel_key = 0;
847       else
848         {
849           gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
850           if (accel_key == 0)
851             g_warning ("Unable to parse accelerator '%s' for action '%s'",
852                        accelerator, name);
853         }
854     }
855   else 
856     {
857       gchar *stock_id;
858       GtkStockItem stock_item;
859
860       g_object_get (action, "stock-id", &stock_id, NULL);
861
862       if (stock_id && gtk_stock_lookup (stock_id, &stock_item))
863         {
864           accel_key = stock_item.keyval;
865           accel_mods = stock_item.modifier;
866         }
867
868       g_free (stock_id);
869     }
870
871   if (accel_key)
872     gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
873
874   gtk_action_set_accel_path (action, accel_path);
875   gtk_action_group_add_action (action_group, action);
876
877   g_free (accel_path);
878 }
879
880 /**
881  * gtk_action_group_remove_action:
882  * @action_group: the action group
883  * @action: an action
884  *
885  * Removes an action object from the action group.
886  *
887  * Since: 2.4
888  */
889 void
890 gtk_action_group_remove_action (GtkActionGroup *action_group,
891                                 GtkAction      *action)
892 {
893   GtkActionGroupPrivate *private;
894   const gchar *name;
895
896   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
897   g_return_if_fail (GTK_IS_ACTION (action));
898
899   name = gtk_action_get_name (action);
900   g_return_if_fail (name != NULL);
901
902   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
903
904   g_hash_table_remove (private->actions, name);
905 }
906
907 static void
908 add_single_action (gpointer key, 
909                    gpointer value, 
910                    gpointer user_data)
911 {
912   GList **list = user_data;
913
914   *list = g_list_prepend (*list, value);
915 }
916
917 /**
918  * gtk_action_group_list_actions:
919  * @action_group: the action group
920  *
921  * Lists the actions in the action group.
922  *
923  * Returns: (element-type GtkAction) (transfer container): an allocated list of the action objects in the action group
924  *
925  * Since: 2.4
926  */
927 GList *
928 gtk_action_group_list_actions (GtkActionGroup *action_group)
929 {
930   GtkActionGroupPrivate *private;
931   GList *actions = NULL;
932
933   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
934
935   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
936   
937   g_hash_table_foreach (private->actions, add_single_action, &actions);
938
939   return g_list_reverse (actions);
940 }
941
942
943 /**
944  * gtk_action_group_add_actions:
945  * @action_group: the action group
946  * @entries: an array of action descriptions
947  * @n_entries: the number of entries
948  * @user_data: data to pass to the action callbacks
949  *
950  * This is a convenience function to create a number of actions and add them 
951  * to the action group.
952  *
953  * The "activate" signals of the actions are connected to the callbacks and 
954  * their accel paths are set to 
955  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
956  * 
957  * Since: 2.4
958  */
959 void
960 gtk_action_group_add_actions (GtkActionGroup       *action_group,
961                               const GtkActionEntry *entries,
962                               guint                 n_entries,
963                               gpointer              user_data)
964 {
965   gtk_action_group_add_actions_full (action_group, 
966                                      entries, n_entries, 
967                                      user_data, NULL);
968 }
969
970 typedef struct _SharedData  SharedData;
971
972 struct _SharedData {
973   guint          ref_count;
974   gpointer       data;
975   GDestroyNotify destroy;
976 };
977
978 static void
979 shared_data_unref (gpointer data)
980 {
981   SharedData *shared_data = (SharedData *)data;
982
983   shared_data->ref_count--;
984   if (shared_data->ref_count == 0)
985     {
986       if (shared_data->destroy)
987         shared_data->destroy (shared_data->data);
988
989       g_slice_free (SharedData, shared_data);
990     }
991 }
992
993
994 /**
995  * gtk_action_group_add_actions_full:
996  * @action_group: the action group
997  * @entries: an array of action descriptions
998  * @n_entries: the number of entries
999  * @user_data: data to pass to the action callbacks
1000  * @destroy: destroy notification callback for @user_data
1001  *
1002  * This variant of gtk_action_group_add_actions() adds a #GDestroyNotify
1003  * callback for @user_data. 
1004  * 
1005  * Since: 2.4
1006  */
1007 void
1008 gtk_action_group_add_actions_full (GtkActionGroup       *action_group,
1009                                    const GtkActionEntry *entries,
1010                                    guint                 n_entries,
1011                                    gpointer              user_data,
1012                                    GDestroyNotify        destroy)
1013 {
1014
1015   /* Keep this in sync with the other 
1016    * gtk_action_group_add_..._actions_full() functions.
1017    */
1018   guint i;
1019   SharedData *shared_data;
1020
1021   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1022
1023   shared_data = g_slice_new0 (SharedData);
1024   shared_data->ref_count = 1;
1025   shared_data->data = user_data;
1026   shared_data->destroy = destroy;
1027
1028   for (i = 0; i < n_entries; i++)
1029     {
1030       GtkAction *action;
1031       const gchar *label;
1032       const gchar *tooltip;
1033
1034       if (!check_unique_action (action_group, entries[i].name))
1035         continue;
1036
1037       label = gtk_action_group_translate_string (action_group, entries[i].label);
1038       tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
1039
1040       action = gtk_action_new (entries[i].name,
1041                                label,
1042                                tooltip,
1043                                NULL);
1044
1045       if (entries[i].stock_id) 
1046         {
1047           g_object_set (action, "stock-id", entries[i].stock_id, NULL);
1048           if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), 
1049                                        entries[i].stock_id))
1050             g_object_set (action, "icon-name", entries[i].stock_id, NULL);
1051         }
1052           
1053       if (entries[i].callback)
1054         {
1055           GClosure *closure;
1056
1057           closure = g_cclosure_new (entries[i].callback, user_data, NULL);
1058           g_closure_add_finalize_notifier (closure, shared_data, 
1059                                            (GClosureNotify)shared_data_unref);
1060           shared_data->ref_count++;
1061
1062           g_signal_connect_closure (action, "activate", closure, FALSE);
1063         }
1064           
1065       gtk_action_group_add_action_with_accel (action_group, 
1066                                               action,
1067                                               entries[i].accelerator);
1068       g_object_unref (action);
1069     }
1070
1071   shared_data_unref (shared_data);
1072 }
1073
1074 /**
1075  * gtk_action_group_add_toggle_actions:
1076  * @action_group: the action group
1077  * @entries: an array of toggle action descriptions
1078  * @n_entries: the number of entries
1079  * @user_data: data to pass to the action callbacks
1080  *
1081  * This is a convenience function to create a number of toggle actions and add them 
1082  * to the action group.
1083  *
1084  * The "activate" signals of the actions are connected to the callbacks and 
1085  * their accel paths are set to 
1086  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
1087  * 
1088  * Since: 2.4
1089  */
1090 void
1091 gtk_action_group_add_toggle_actions (GtkActionGroup             *action_group,
1092                                      const GtkToggleActionEntry *entries,
1093                                      guint                       n_entries,
1094                                      gpointer                    user_data)
1095 {
1096   gtk_action_group_add_toggle_actions_full (action_group, 
1097                                             entries, n_entries, 
1098                                             user_data, NULL);
1099 }
1100
1101
1102 /**
1103  * gtk_action_group_add_toggle_actions_full:
1104  * @action_group: the action group
1105  * @entries: an array of toggle action descriptions
1106  * @n_entries: the number of entries
1107  * @user_data: data to pass to the action callbacks
1108  * @destroy: destroy notification callback for @user_data
1109  *
1110  * This variant of gtk_action_group_add_toggle_actions() adds a 
1111  * #GDestroyNotify callback for @user_data. 
1112  * 
1113  * Since: 2.4
1114  */
1115 void
1116 gtk_action_group_add_toggle_actions_full (GtkActionGroup             *action_group,
1117                                           const GtkToggleActionEntry *entries,
1118                                           guint                       n_entries,
1119                                           gpointer                    user_data,
1120                                           GDestroyNotify              destroy)
1121 {
1122   /* Keep this in sync with the other 
1123    * gtk_action_group_add_..._actions_full() functions.
1124    */
1125   guint i;
1126   SharedData *shared_data;
1127
1128   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1129
1130   shared_data = g_slice_new0 (SharedData);
1131   shared_data->ref_count = 1;
1132   shared_data->data = user_data;
1133   shared_data->destroy = destroy;
1134
1135   for (i = 0; i < n_entries; i++)
1136     {
1137       GtkToggleAction *action;
1138       const gchar *label;
1139       const gchar *tooltip;
1140
1141       if (!check_unique_action (action_group, entries[i].name))
1142         continue;
1143
1144       label = gtk_action_group_translate_string (action_group, entries[i].label);
1145       tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
1146
1147       action = gtk_toggle_action_new (entries[i].name,
1148                                       label,
1149                                       tooltip,
1150                                       NULL);
1151
1152       if (entries[i].stock_id) 
1153         {
1154           if (gtk_icon_factory_lookup_default (entries[i].stock_id))
1155             g_object_set (action, "stock-id", entries[i].stock_id, NULL);
1156           else
1157             g_object_set (action, "icon-name", entries[i].stock_id, NULL);
1158         }
1159
1160       gtk_toggle_action_set_active (action, entries[i].is_active);
1161
1162       if (entries[i].callback)
1163         {
1164           GClosure *closure;
1165
1166           closure = g_cclosure_new (entries[i].callback, user_data, NULL);
1167           g_closure_add_finalize_notifier (closure, shared_data, 
1168                                            (GClosureNotify)shared_data_unref);
1169           shared_data->ref_count++;
1170
1171           g_signal_connect_closure (action, "activate", closure, FALSE);
1172         }
1173           
1174       gtk_action_group_add_action_with_accel (action_group, 
1175                                               GTK_ACTION (action),
1176                                               entries[i].accelerator);
1177       g_object_unref (action);
1178     }
1179
1180   shared_data_unref (shared_data);
1181 }
1182
1183 /**
1184  * gtk_action_group_add_radio_actions:
1185  * @action_group: the action group
1186  * @entries: an array of radio action descriptions
1187  * @n_entries: the number of entries
1188  * @value: the value of the action to activate initially, or -1 if
1189  *   no action should be activated
1190  * @on_change: the callback to connect to the changed signal
1191  * @user_data: data to pass to the action callbacks
1192  * 
1193  * This is a convenience routine to create a group of radio actions and
1194  * add them to the action group. 
1195  *
1196  * The "changed" signal of the first radio action is connected to the 
1197  * @on_change callback and the accel paths of the actions are set to 
1198  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
1199  * 
1200  * Since: 2.4
1201  **/
1202 void            
1203 gtk_action_group_add_radio_actions (GtkActionGroup            *action_group,
1204                                     const GtkRadioActionEntry *entries,
1205                                     guint                      n_entries,
1206                                     gint                       value,
1207                                     GCallback                  on_change,
1208                                     gpointer                   user_data)
1209 {
1210   gtk_action_group_add_radio_actions_full (action_group, 
1211                                            entries, n_entries, 
1212                                            value,
1213                                            on_change, user_data, NULL);
1214 }
1215
1216 /**
1217  * gtk_action_group_add_radio_actions_full:
1218  * @action_group: the action group
1219  * @entries: an array of radio action descriptions
1220  * @n_entries: the number of entries
1221  * @value: the value of the action to activate initially, or -1 if
1222  *   no action should be activated
1223  * @on_change: the callback to connect to the changed signal
1224  * @user_data: data to pass to the action callbacks
1225  * @destroy: destroy notification callback for @user_data
1226  *
1227  * This variant of gtk_action_group_add_radio_actions() adds a 
1228  * #GDestroyNotify callback for @user_data. 
1229  * 
1230  * Since: 2.4
1231  **/
1232 void            
1233 gtk_action_group_add_radio_actions_full (GtkActionGroup            *action_group,
1234                                          const GtkRadioActionEntry *entries,
1235                                          guint                      n_entries,
1236                                          gint                       value,
1237                                          GCallback                  on_change,
1238                                          gpointer                   user_data,
1239                                          GDestroyNotify             destroy)
1240 {
1241   /* Keep this in sync with the other 
1242    * gtk_action_group_add_..._actions_full() functions.
1243    */
1244   guint i;
1245   GSList *group = NULL;
1246   GtkRadioAction *first_action = NULL;
1247
1248   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1249
1250   for (i = 0; i < n_entries; i++)
1251     {
1252       GtkRadioAction *action;
1253       const gchar *label;
1254       const gchar *tooltip; 
1255
1256       if (!check_unique_action (action_group, entries[i].name))
1257         continue;
1258
1259       label = gtk_action_group_translate_string (action_group, entries[i].label);
1260       tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
1261
1262       action = gtk_radio_action_new (entries[i].name,
1263                                      label,
1264                                      tooltip,
1265                                      NULL,
1266                                      entries[i].value);
1267
1268       if (entries[i].stock_id) 
1269         {
1270           if (gtk_icon_factory_lookup_default (entries[i].stock_id))
1271             g_object_set (action, "stock-id", entries[i].stock_id, NULL);
1272           else
1273             g_object_set (action, "icon-name", entries[i].stock_id, NULL);
1274         }
1275
1276       if (i == 0) 
1277         first_action = action;
1278
1279       gtk_radio_action_set_group (action, group);
1280       group = gtk_radio_action_get_group (action);
1281
1282       if (value == entries[i].value)
1283         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1284
1285       gtk_action_group_add_action_with_accel (action_group, 
1286                                               GTK_ACTION (action),
1287                                               entries[i].accelerator);
1288       g_object_unref (action);
1289     }
1290
1291   if (on_change && first_action)
1292     g_signal_connect_data (first_action, "changed",
1293                            on_change, user_data, 
1294                            (GClosureNotify)destroy, 0);
1295 }
1296
1297 /**
1298  * gtk_action_group_set_translate_func:
1299  * @action_group: a #GtkActionGroup
1300  * @func: a #GtkTranslateFunc
1301  * @data: data to be passed to @func and @notify
1302  * @notify: a #GDestroyNotify function to be called when @action_group is
1303  *   destroyed and when the translation function is changed again
1304  *
1305  * Sets a function to be used for translating the @label and @tooltip of 
1306  * #GtkActionGroupEntry<!-- -->s added by gtk_action_group_add_actions().
1307  *
1308  * If you're using gettext(), it is enough to set the translation domain
1309  * with gtk_action_group_set_translation_domain().
1310  *
1311  * Since: 2.4 
1312  **/
1313 void
1314 gtk_action_group_set_translate_func (GtkActionGroup   *action_group,
1315                                      GtkTranslateFunc  func,
1316                                      gpointer          data,
1317                                      GDestroyNotify    notify)
1318 {
1319   GtkActionGroupPrivate *private;
1320
1321   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1322   
1323   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
1324
1325   if (private->translate_notify)
1326     private->translate_notify (private->translate_data);
1327       
1328   private->translate_func = func;
1329   private->translate_data = data;
1330   private->translate_notify = notify;
1331 }
1332
1333 static gchar *
1334 dgettext_swapped (const gchar *msgid, 
1335                   const gchar *domainname)
1336 {
1337   /* Pass through g_dgettext if and only if msgid is nonempty. */
1338   if (msgid && *msgid) 
1339     return (gchar*) g_dgettext (domainname, msgid); 
1340   else
1341     return (gchar*) msgid;
1342 }
1343
1344 /**
1345  * gtk_action_group_set_translation_domain:
1346  * @action_group: a #GtkActionGroup
1347  * @domain: the translation domain to use for g_dgettext() calls
1348  * 
1349  * Sets the translation domain and uses g_dgettext() for translating the 
1350  * @label and @tooltip of #GtkActionEntry<!-- -->s added by 
1351  * gtk_action_group_add_actions().
1352  *
1353  * If you're not using gettext() for localization, see 
1354  * gtk_action_group_set_translate_func().
1355  *
1356  * Since: 2.4
1357  **/
1358 void 
1359 gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
1360                                          const gchar    *domain)
1361 {
1362   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
1363
1364   gtk_action_group_set_translate_func (action_group, 
1365                                        (GtkTranslateFunc)dgettext_swapped,
1366                                        g_strdup (domain),
1367                                        g_free);
1368
1369
1370
1371 /**
1372  * gtk_action_group_translate_string:
1373  * @action_group: a #GtkActionGroup
1374  * @string: a string
1375  *
1376  * Translates a string using the specified translate_func(). This
1377  * is mainly intended for language bindings.
1378  *
1379  * Returns: the translation of @string
1380  *
1381  * Since: 2.6
1382  **/
1383 G_CONST_RETURN gchar *
1384 gtk_action_group_translate_string (GtkActionGroup *action_group,
1385                                    const gchar    *string)
1386 {
1387   GtkActionGroupPrivate *private;
1388   GtkTranslateFunc translate_func;
1389   gpointer translate_data;
1390   
1391   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), string);
1392   
1393   if (string == NULL)
1394     return NULL;
1395
1396   private = GTK_ACTION_GROUP_GET_PRIVATE (action_group);
1397
1398   translate_func = private->translate_func;
1399   translate_data = private->translate_data;
1400   
1401   if (translate_func)
1402     return translate_func (string, translate_data);
1403   else
1404     return string;
1405 }
1406
1407 /* Protected for use by GtkAction */
1408 void
1409 _gtk_action_group_emit_connect_proxy  (GtkActionGroup *action_group,
1410                                        GtkAction      *action,
1411                                        GtkWidget      *proxy)
1412 {
1413   g_signal_emit (action_group, action_group_signals[CONNECT_PROXY], 0, 
1414                  action, proxy);
1415 }
1416
1417 void
1418 _gtk_action_group_emit_disconnect_proxy  (GtkActionGroup *action_group,
1419                                           GtkAction      *action,
1420                                           GtkWidget      *proxy)
1421 {
1422   g_signal_emit (action_group, action_group_signals[DISCONNECT_PROXY], 0, 
1423                  action, proxy);
1424 }
1425
1426 void
1427 _gtk_action_group_emit_pre_activate  (GtkActionGroup *action_group,
1428                                       GtkAction      *action)
1429 {
1430   g_signal_emit (action_group, action_group_signals[PRE_ACTIVATE], 0, action);
1431 }
1432
1433 void
1434 _gtk_action_group_emit_post_activate (GtkActionGroup *action_group,
1435                                       GtkAction      *action)
1436 {
1437   g_signal_emit (action_group, action_group_signals[POST_ACTIVATE], 0, action);
1438 }
1439
1440 #define __GTK_ACTION_GROUP_C__
1441 #include "gtkaliasdef.c"