]> Pileus Git - ~andy/gtk/blob - gtk/gtkactiongroup.c
Initially activate the JUSTIFY_RIGHT group member to test the fix for
[~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
33 #include "gtkactiongroup.h"
34 #include "gtktoggleaction.h"
35 #include "gtkradioaction.h"
36 #include "gtkaccelmap.h"
37 #include "gtkintl.h"
38
39 #define GTK_ACTION_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ACTION_GROUP, GtkActionGroupPrivate))
40
41 struct _GtkActionGroupPrivate 
42 {
43   gchar           *name;
44   GHashTable      *actions;
45
46   GtkTranslateFunc translate_func;
47   gpointer         translate_data;
48   GtkDestroyNotify translate_notify;   
49 };
50
51 static void       gtk_action_group_init            (GtkActionGroup      *self);
52 static void       gtk_action_group_class_init      (GtkActionGroupClass *class);
53 static void       gtk_action_group_finalize        (GObject             *object);
54 static GtkAction *gtk_action_group_real_get_action (GtkActionGroup      *self,
55                                                     const gchar         *name);
56
57
58 GType
59 gtk_action_group_get_type (void)
60 {
61   static GType type = 0;
62
63   if (!type)
64     {
65       static const GTypeInfo type_info =
66       {
67         sizeof (GtkActionGroupClass),
68         NULL,           /* base_init */
69         NULL,           /* base_finalize */
70         (GClassInitFunc) gtk_action_group_class_init,
71         NULL,           /* class_finalize */
72         NULL,           /* class_data */
73         sizeof (GtkActionGroup),
74         0, /* n_preallocs */
75         (GInstanceInitFunc) gtk_action_group_init,
76       };
77
78       type = g_type_register_static (G_TYPE_OBJECT, "GtkActionGroup",
79                                      &type_info, 0);
80     }
81
82   return type;
83 }
84
85 static GObjectClass *parent_class = NULL;
86
87 static void
88 gtk_action_group_class_init (GtkActionGroupClass *klass)
89 {
90   GObjectClass *gobject_class;
91
92   gobject_class = G_OBJECT_CLASS (klass);
93   parent_class = g_type_class_peek_parent (klass);
94
95   gobject_class->finalize = gtk_action_group_finalize;
96   klass->get_action = gtk_action_group_real_get_action;
97
98   g_type_class_add_private (gobject_class, sizeof (GtkActionGroupPrivate));
99 }
100
101 static void
102 gtk_action_group_init (GtkActionGroup *self)
103 {
104   self->private_data = GTK_ACTION_GROUP_GET_PRIVATE (self);
105   self->private_data->name = NULL;
106   self->private_data->actions = g_hash_table_new_full (g_str_hash, g_str_equal,
107                                                        (GDestroyNotify) g_free,
108                                                        (GDestroyNotify) g_object_unref);
109   self->private_data->translate_func = NULL;
110   self->private_data->translate_data = NULL;
111   self->private_data->translate_notify = NULL;
112 }
113
114 /**
115  * gtk_action_group_new:
116  * @name: the name of the action group.
117  *
118  * Creates a new #GtkActionGroup object. The name of the action group
119  * is used when associating <link linkend="Action-Accel">keybindings</link> 
120  * with the actions.
121  *
122  * Returns: the new #GtkActionGroup
123  *
124  * Since: 2.4
125  */
126 GtkActionGroup *
127 gtk_action_group_new (const gchar *name)
128 {
129   GtkActionGroup *self;
130
131   self = g_object_new (GTK_TYPE_ACTION_GROUP, NULL);
132   self->private_data->name = g_strdup (name);
133
134   return self;
135 }
136
137 static void
138 gtk_action_group_finalize (GObject *object)
139 {
140   GtkActionGroup *self;
141
142   self = GTK_ACTION_GROUP (object);
143
144   g_free (self->private_data->name);
145   self->private_data->name = NULL;
146
147   g_hash_table_destroy (self->private_data->actions);
148   self->private_data->actions = NULL;
149
150   if (self->private_data->translate_notify)
151     self->private_data->translate_notify (self->private_data->translate_data);
152
153   if (parent_class->finalize)
154     (* parent_class->finalize) (object);
155 }
156
157 static GtkAction *
158 gtk_action_group_real_get_action (GtkActionGroup *self,
159                                   const gchar    *action_name)
160 {
161   return g_hash_table_lookup (self->private_data->actions, action_name);
162 }
163
164 /**
165  * gtk_action_group_get_name:
166  * @action_group: the action group
167  *
168  * Gets the name of the action group.
169  *
170  * Returns: the name of the action group.
171  * 
172  * Since: 2.4
173  */
174 const gchar *
175 gtk_action_group_get_name (GtkActionGroup *action_group)
176 {
177   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
178
179   return action_group->private_data->name;
180 }
181
182 /**
183  * gtk_action_group_get_action:
184  * @action_group: the action group
185  * @action_name: the name of the action
186  *
187  * Looks up an action in the action group by name.
188  *
189  * Returns: the action, or %NULL if no action by that name exists
190  *
191  * Since: 2.4
192  */
193 GtkAction *
194 gtk_action_group_get_action (GtkActionGroup *action_group,
195                              const gchar    *action_name)
196 {
197   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
198   g_return_val_if_fail (GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action != NULL, NULL);
199
200   return (* GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action)
201     (action_group, action_name);
202 }
203
204 /**
205  * gtk_action_group_add_action:
206  * @action_group: the action group
207  * @action: an action
208  *
209  * Adds an action object to the action group. 
210  *
211  * Since: 2.4
212  */
213 void
214 gtk_action_group_add_action (GtkActionGroup *action_group,
215                              GtkAction      *action)
216 {
217   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
218   g_return_if_fail (GTK_IS_ACTION (action));
219   g_return_if_fail (gtk_action_get_name (action) != NULL);
220
221   g_hash_table_insert (action_group->private_data->actions, 
222                        g_strdup (gtk_action_get_name (action)),
223                        g_object_ref (action));
224 }
225
226 /**
227  * gtk_action_group_remove_action:
228  * @action_group: the action group
229  * @action: an action
230  *
231  * Removes an action object from the action group.
232  *
233  * Since: 2.4
234  */
235 void
236 gtk_action_group_remove_action (GtkActionGroup *action_group,
237                                 GtkAction      *action)
238 {
239   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
240   g_return_if_fail (GTK_IS_ACTION (action));
241   g_return_if_fail (gtk_action_get_name (action) != NULL);
242
243   /* extra protection to make sure action->name is valid */
244   g_object_ref (action);
245   g_hash_table_remove (action_group->private_data->actions, gtk_action_get_name (action));
246   g_object_unref (action);
247 }
248
249 static void
250 add_single_action (gpointer key, 
251                    gpointer value, 
252                    gpointer user_data)
253 {
254   GList **list = user_data;
255
256   *list = g_list_prepend (*list, value);
257 }
258
259 /**
260  * gtk_action_group_list_actions:
261  * @action_group: the action group
262  *
263  * Lists the actions in the action group.
264  *
265  * Returns: an allocated list of the action objects in the action group
266  * 
267  * Since: 2.4
268  */
269 GList *
270 gtk_action_group_list_actions (GtkActionGroup *action_group)
271 {
272   GList *actions = NULL;
273   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
274   
275   g_hash_table_foreach (action_group->private_data->actions, add_single_action, &actions);
276
277   return g_list_reverse (actions);
278 }
279
280
281 /**
282  * gtk_action_group_add_actions:
283  * @action_group: the action group
284  * @entries: an array of action descriptions
285  * @n_entries: the number of entries
286  * @user_data: data to pass to the action callbacks
287  *
288  * This is a convenience function to create a number of actions and add them 
289  * to the action group.
290  *
291  * The "activate" signals of the actions are connected to the callbacks and 
292  * their accel paths are set to 
293  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
294  * 
295  * Since: 2.4
296  */
297 void
298 gtk_action_group_add_actions (GtkActionGroup *action_group,
299                               GtkActionEntry *entries,
300                               guint           n_entries,
301                               gpointer        user_data)
302 {
303   gtk_action_group_add_actions_full (action_group, 
304                                      entries, n_entries, 
305                                      user_data, NULL);
306 }
307
308
309 /**
310  * gtk_action_group_add_actions_full:
311  * @action_group: the action group
312  * @entries: an array of action descriptions
313  * @n_entries: the number of entries
314  * @user_data: data to pass to the action callbacks
315  * @destroy: destroy notification callback for @user_data
316  *
317  * This variant of gtk_action_group_add_actions() adds a #GDestroyNotify
318  * callback for @user_data. 
319  * 
320  * Since: 2.4
321  */
322 void
323 gtk_action_group_add_actions_full (GtkActionGroup *action_group,
324                                    GtkActionEntry *entries,
325                                    guint           n_entries,
326                                    gpointer        user_data,
327                                    GDestroyNotify  destroy)
328 {
329   guint i;
330   GtkTranslateFunc translate_func;
331   gpointer translate_data;
332
333   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
334
335   translate_func = action_group->private_data->translate_func;
336   translate_data = action_group->private_data->translate_data;
337
338   for (i = 0; i < n_entries; i++)
339     {
340       GtkAction *action;
341       gchar *accel_path;
342       gchar *label;
343       gchar *tooltip;
344
345       if (translate_func)
346         {
347           label = translate_func (entries[i].label, translate_data);
348           tooltip = translate_func (entries[i].tooltip, translate_data);
349         }
350       else
351         {
352           label = entries[i].label;
353           tooltip = entries[i].tooltip;
354         }
355
356       action = g_object_new (GTK_TYPE_ACTION,
357                              "name", entries[i].name,
358                              "label", label,
359                              "tooltip", tooltip,
360                              "stock_id", entries[i].stock_id,
361                              NULL);
362
363       if (entries[i].callback)
364         g_signal_connect_data (action, "activate",
365                                entries[i].callback, 
366                                user_data, (GClosureNotify)destroy, 0);
367
368       /* set the accel path for the menu item */
369       accel_path = g_strconcat ("<Actions>/", action_group->private_data->name, "/",
370                                 entries[i].name, NULL);
371       if (entries[i].accelerator)
372         {
373           guint accel_key = 0;
374           GdkModifierType accel_mods;
375
376           gtk_accelerator_parse (entries[i].accelerator, &accel_key,
377                                  &accel_mods);
378           if (accel_key)
379             gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
380         }
381
382       gtk_action_set_accel_path (action, accel_path);
383       g_free (accel_path);
384
385       gtk_action_group_add_action (action_group, action);
386       g_object_unref (action);
387     }
388 }
389
390 /**
391  * gtk_action_group_add_toggle_actions:
392  * @action_group: the action group
393  * @entries: an array of toggle action descriptions
394  * @n_entries: the number of entries
395  * @user_data: data to pass to the action callbacks
396  *
397  * This is a convenience function to create a number of toggle actions and add them 
398  * to the action group.
399  *
400  * The "activate" signals of the actions are connected to the callbacks and 
401  * their accel paths are set to 
402  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
403  * 
404  * Since: 2.4
405  */
406 void
407 gtk_action_group_add_toggle_actions (GtkActionGroup       *action_group,
408                                      GtkToggleActionEntry *entries,
409                                      guint                 n_entries,
410                                      gpointer              user_data)
411 {
412   gtk_action_group_add_toggle_actions_full (action_group, 
413                                             entries, n_entries, 
414                                             user_data, NULL);
415 }
416
417
418 /**
419  * gtk_action_group_add_toggle_actions_full:
420  * @action_group: the action group
421  * @entries: an array of toggle action descriptions
422  * @n_entries: the number of entries
423  * @user_data: data to pass to the action callbacks
424  * @destroy: destroy notification callback for @user_data
425  *
426  * This variant of gtk_action_group_add_toggle_actions() adds a 
427  * #GDestroyNotify callback for @user_data. 
428  * 
429  * Since: 2.4
430  */
431 void
432 gtk_action_group_add_toggle_actions_full (GtkActionGroup       *action_group,
433                                           GtkToggleActionEntry *entries,
434                                           guint                 n_entries,
435                                           gpointer              user_data,
436                                           GDestroyNotify        destroy)
437 {
438   guint i;
439   GtkTranslateFunc translate_func;
440   gpointer translate_data;
441
442   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
443
444   translate_func = action_group->private_data->translate_func;
445   translate_data = action_group->private_data->translate_data;
446
447   for (i = 0; i < n_entries; i++)
448     {
449       GtkAction *action;
450       gchar *accel_path;
451       gchar *label;
452       gchar *tooltip;
453
454       if (translate_func)
455         {
456           label = translate_func (entries[i].label, translate_data);
457           tooltip = translate_func (entries[i].tooltip, translate_data);
458         }
459       else
460         {
461           label = entries[i].label;
462           tooltip = entries[i].tooltip;
463         }
464
465       action = g_object_new (GTK_TYPE_TOGGLE_ACTION,
466                              "name", entries[i].name,
467                              "label", label,
468                              "tooltip", tooltip,
469                              "stock_id", entries[i].stock_id,
470                              NULL);
471
472       gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), 
473                                     entries[i].is_active);
474
475       if (entries[i].callback)
476         g_signal_connect_data (action, "activate",
477                                entries[i].callback, 
478                                user_data, (GClosureNotify)destroy, 0);
479
480       /* set the accel path for the menu item */
481       accel_path = g_strconcat ("<Actions>/", action_group->private_data->name, "/",
482                                 entries[i].name, NULL);
483       if (entries[i].accelerator)
484         {
485           guint accel_key = 0;
486           GdkModifierType accel_mods;
487
488           gtk_accelerator_parse (entries[i].accelerator, &accel_key,
489                                  &accel_mods);
490           if (accel_key)
491             gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
492         }
493
494       gtk_action_set_accel_path (action, accel_path);
495       g_free (accel_path);
496
497       gtk_action_group_add_action (action_group, action);
498       g_object_unref (action);
499     }
500 }
501
502 /**
503  * gtk_action_group_add_radio_actions:
504  * @action_group: the action group
505  * @entries: an array of radio action descriptions
506  * @n_entries: the number of entries
507  * @value: the value of the action to activate initially, or -1 if
508  *   no action should be activated
509  * @on_change: the callback to connect to the changed signal
510  * @user_data: data to pass to the action callbacks
511  * 
512  * This is a convenience routine to create a group of radio actions and
513  * add them to the action group. 
514  *
515  * The "changed" signal of the first radio action is connected to the 
516  * @on_change callback and the accel paths of the actions are set to 
517  * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
518  * 
519  * Since: 2.4
520  **/
521 void            
522 gtk_action_group_add_radio_actions (GtkActionGroup      *action_group,
523                                     GtkRadioActionEntry *entries,
524                                     guint                n_entries,
525                                     gint                 value,
526                                     GCallback            on_change,
527                                     gpointer             user_data)
528 {
529   gtk_action_group_add_radio_actions_full (action_group, 
530                                            entries, n_entries, 
531                                            value,
532                                            on_change, user_data, NULL);
533 }
534
535 /**
536  * gtk_action_group_add_radio_actions_full:
537  * @action_group: the action group
538  * @entries: an array of radio action descriptions
539  * @n_entries: the number of entries
540  * @value: the value of the action to activate initially, or -1 if
541  *   no action should be activated
542  * @on_change: the callback to connect to the changed signal
543  * @user_data: data to pass to the action callbacks
544  * @destroy: destroy notification callback for @user_data
545  *
546  * This variant of gtk_action_group_add_radio_actions() adds a 
547  * #GDestroyNotify callback for @user_data. 
548  * 
549  * Since: 2.4
550  **/
551 void            
552 gtk_action_group_add_radio_actions_full (GtkActionGroup      *action_group,
553                                          GtkRadioActionEntry *entries,
554                                          guint                n_entries,
555                                          gint                 value,
556                                          GCallback            on_change,
557                                          gpointer             user_data,
558                                          GDestroyNotify       destroy)
559 {
560   guint i;
561   GtkTranslateFunc translate_func;
562   gpointer translate_data;
563   GSList *group = NULL;
564   GtkAction *first_action;
565
566   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
567
568   translate_func = action_group->private_data->translate_func;
569   translate_data = action_group->private_data->translate_data;
570
571   for (i = 0; i < n_entries; i++)
572     {
573       GtkAction *action;
574       gchar *accel_path;
575       gchar *label;
576       gchar *tooltip; 
577
578       if (translate_func)
579         {
580           label = translate_func (entries[i].label, translate_data);
581           tooltip = translate_func (entries[i].tooltip, translate_data);
582         }
583       else
584         {
585           label = entries[i].label;
586           tooltip = entries[i].tooltip;
587         }
588
589       action = g_object_new (GTK_TYPE_RADIO_ACTION,
590                              "name", entries[i].name,
591                              "label", label,
592                              "tooltip", tooltip,
593                              "stock_id", entries[i].stock_id,
594                              "value", entries[i].value,
595                              NULL);
596
597       if (i == 0) 
598         first_action = action;
599
600       gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
601       group = gtk_radio_action_get_group (GTK_RADIO_ACTION (action));
602
603       if (value == entries[i].value)
604         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
605       
606       /* set the accel path for the menu item */
607       accel_path = g_strconcat ("<Actions>/", 
608                                 action_group->private_data->name, "/",
609                                 entries[i].name, NULL);
610       if (entries[i].accelerator)
611         {
612           guint accel_key = 0;
613           GdkModifierType accel_mods;
614
615           gtk_accelerator_parse (entries[i].accelerator, &accel_key,
616                                  &accel_mods);
617           if (accel_key)
618             gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
619         }
620
621       gtk_action_set_accel_path (action, accel_path);
622       g_free (accel_path);
623       
624       gtk_action_group_add_action (action_group, action);
625       g_object_unref (action);
626     }
627
628   if (on_change)
629     g_signal_connect_data (first_action, "changed",
630                            on_change, user_data, 
631                            (GClosureNotify)destroy, 0);
632 }
633
634 /**
635  * gtk_action_group_set_translate_func:
636  * @action_group: a #GtkActionGroup
637  * @func: a #GtkTranslateFunc
638  * @data: data to be passed to @func and @notify
639  * @notify: a #GtkDestroyNotify function to be called when @action_group is 
640  *   destroyed and when the translation function is changed again
641  * 
642  * Sets a function to be used for translating the @label and @tooltip of 
643  * #GtkActionGroupEntry<!-- -->s added by gtk_action_group_add_actions().
644  *
645  * If you're using gettext(), it is enough to set the translation domain
646  * with gtk_action_group_set_translation_domain().
647  *
648  * Since: 2.4 
649  **/
650 void
651 gtk_action_group_set_translate_func (GtkActionGroup      *action_group,
652                                      GtkTranslateFunc     func,
653                                      gpointer             data,
654                                      GtkDestroyNotify     notify)
655 {
656   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
657   
658   if (action_group->private_data->translate_notify)
659     action_group->private_data->translate_notify (action_group->private_data->translate_data);
660       
661   action_group->private_data->translate_func = func;
662   action_group->private_data->translate_data = data;
663   action_group->private_data->translate_notify = notify;
664 }
665
666 static gchar *
667 dgettext_swapped (const gchar *msgid, 
668                   const gchar *domainname)
669 {
670   return dgettext (domainname, msgid);
671 }
672
673 /**
674  * gtk_action_group_set_translation_domain:
675  * @action_group: a #GtkActionGroup
676  * @domain: the translation domain to use for dgettext() calls
677  * 
678  * Sets the translation domain and uses dgettext() for translating the 
679  * @label and @tooltip of #GtkActionEntry<!-- -->s added by 
680  * gtk_action_group_add_actions().
681  *
682  * If you're not using gettext() for localization, see 
683  * gtk_action_group_set_translate_func().
684  *
685  * Since: 2.4
686  **/
687 void 
688 gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
689                                          const gchar    *domain)
690 {
691   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
692
693   gtk_action_group_set_translate_func (action_group, 
694                                        (GtkTranslateFunc)dgettext_swapped,
695                                        g_strdup (domain),
696                                        g_free);
697