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