]> Pileus Git - ~andy/gtk/blob - gtk/gtkactiongroup.c
Forgot to actually commit this file
[~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.
118  *
119  * Returns: the new #GtkActionGroup
120  *
121  * Since: 2.4
122  */
123 GtkActionGroup *
124 gtk_action_group_new (const gchar *name)
125 {
126   GtkActionGroup *self;
127
128   self = g_object_new (GTK_TYPE_ACTION_GROUP, NULL);
129   self->private_data->name = g_strdup (name);
130
131   return self;
132 }
133
134 static void
135 gtk_action_group_finalize (GObject *object)
136 {
137   GtkActionGroup *self;
138
139   self = GTK_ACTION_GROUP (object);
140
141   g_free (self->private_data->name);
142   self->private_data->name = NULL;
143
144   g_hash_table_destroy (self->private_data->actions);
145   self->private_data->actions = NULL;
146
147   if (self->private_data->translate_notify)
148     self->private_data->translate_notify (self->private_data->translate_data);
149
150   if (parent_class->finalize)
151     (* parent_class->finalize) (object);
152 }
153
154 static GtkAction *
155 gtk_action_group_real_get_action (GtkActionGroup *self,
156                                   const gchar    *action_name)
157 {
158   return g_hash_table_lookup (self->private_data->actions, action_name);
159 }
160
161 /**
162  * gtk_action_group_get_name:
163  * @action_group: the action group
164  *
165  * Gets the name of the action group.
166  *
167  * Returns: the name of the action group.
168  * 
169  * Since: 2.4
170  */
171 const gchar *
172 gtk_action_group_get_name (GtkActionGroup *action_group)
173 {
174   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
175
176   return action_group->private_data->name;
177 }
178
179 /**
180  * gtk_action_group_get_action:
181  * @action_group: the action group
182  * @action_name: the name of the action
183  *
184  * Looks up an action in the action group by name.
185  *
186  * Returns: the action, or %NULL if no action by that name exists
187  *
188  * Since: 2.4
189  */
190 GtkAction *
191 gtk_action_group_get_action (GtkActionGroup *action_group,
192                              const gchar    *action_name)
193 {
194   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
195   g_return_val_if_fail (GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action != NULL, NULL);
196
197   return (* GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action)
198     (action_group, action_name);
199 }
200
201 /**
202  * gtk_action_group_add_action:
203  * @action_group: the action group
204  * @action: an action
205  *
206  * Adds an action object to the action group.
207  *
208  * Since: 2.4
209  */
210 void
211 gtk_action_group_add_action (GtkActionGroup *action_group,
212                              GtkAction      *action)
213 {
214   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
215   g_return_if_fail (GTK_IS_ACTION (action));
216   g_return_if_fail (gtk_action_get_name (action) != NULL);
217
218   g_hash_table_insert (action_group->private_data->actions, 
219                        g_strdup (gtk_action_get_name (action)),
220                        g_object_ref (action));
221 }
222
223 /**
224  * gtk_action_group_removes_action:
225  * @action_group: the action group
226  * @action: an action
227  *
228  * Removes an action object from the action group.
229  *
230  * Since: 2.4
231  */
232 void
233 gtk_action_group_remove_action (GtkActionGroup *action_group,
234                                 GtkAction      *action)
235 {
236   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
237   g_return_if_fail (GTK_IS_ACTION (action));
238   g_return_if_fail (gtk_action_get_name (action) != NULL);
239
240   /* extra protection to make sure action->name is valid */
241   g_object_ref (action);
242   g_hash_table_remove (action_group->private_data->actions, gtk_action_get_name (action));
243   g_object_unref (action);
244 }
245
246 static void
247 add_single_action (gpointer key, 
248                    gpointer value, 
249                    gpointer user_data)
250 {
251   GList **list = user_data;
252
253   *list = g_list_prepend (*list, value);
254 }
255
256 /**
257  * gtk_action_group_list_actions:
258  * @action_group: the action group
259  *
260  * Lists the actions in the action group.
261  *
262  * Returns: an allocated list of the action objects in the action group
263  * 
264  * Since: 2.4
265  */
266 GList *
267 gtk_action_group_list_actions (GtkActionGroup *action_group)
268 {
269   GList *actions = NULL;
270   g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
271   
272   g_hash_table_foreach (action_group->private_data->actions, add_single_action, &actions);
273
274   return g_list_reverse (actions);
275 }
276
277
278 /**
279  * gtk_action_group_add_actions:
280  * @action_group: the action group
281  * @entries: an array of action descriptions
282  * @n_entries: the number of entries
283  * @user_data: data to pass to the action callbacks
284  *
285  * This is a convenience routine to create a number of actions and add
286  * them to the action group.  Each member of the array describes an
287  * action to create.
288  * 
289  * Since: 2.4
290  */
291 void
292 gtk_action_group_add_actions (GtkActionGroup *action_group,
293                               GtkActionEntry *entries,
294                               guint           n_entries,
295                               gpointer        user_data)
296 {
297   gtk_action_group_add_actions_full (action_group, 
298                                      entries, n_entries, 
299                                      user_data, NULL);
300 }
301
302
303 /**
304  * gtk_action_group_add_actions_full:
305  * @action_group: the action group
306  * @entries: an array of action descriptions
307  * @n_entries: the number of entries
308  * @user_data: data to pass to the action callbacks
309  * @destroy: destroy notification callback for @user_data
310  *
311  * This variant of gtk_action_group_add_actions() adds a #GDestroyNotify
312  * callback for @user_data. 
313  * 
314  * Since: 2.4
315  */
316 void
317 gtk_action_group_add_actions_full (GtkActionGroup *action_group,
318                                    GtkActionEntry *entries,
319                                    guint           n_entries,
320                                    gpointer        user_data,
321                                    GDestroyNotify  destroy)
322 {
323   guint i;
324   GtkTranslateFunc translate_func;
325   gpointer translate_data;
326
327   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
328
329   translate_func = action_group->private_data->translate_func;
330   translate_data = action_group->private_data->translate_data;
331
332   for (i = 0; i < n_entries; i++)
333     {
334       GtkAction *action;
335       GType action_type;
336       gchar *accel_path;
337       gchar *label;
338       gchar *tooltip;
339
340       if (entries[i].is_toggle)
341         action_type = GTK_TYPE_TOGGLE_ACTION;
342       else
343         action_type = GTK_TYPE_ACTION;
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 (action_type,
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_radio_actions:
392  * @action_group: the action group
393  * @entries: an array of radio action descriptions
394  * @n_entries: the number of entries
395  * @on_change: the callback to connect to the changed signal
396  * @user_data: data to pass to the action callbacks
397  * 
398  * This is a convenience routine to create a group of radio actions and 
399  * add them to the action group.  Each member of the array describes a
400  * radio action to create.
401  * 
402  * Since: 2.4
403  * 
404  **/
405 void            
406 gtk_action_group_add_radio_actions (GtkActionGroup      *action_group,
407                                     GtkRadioActionEntry *entries,
408                                     guint                n_entries,
409                                     GCallback            on_change,
410                                     gpointer             user_data)
411 {
412   gtk_action_group_add_radio_actions_full (action_group, 
413                                            entries, n_entries, 
414                                            on_change, user_data, NULL);
415 }
416
417 void            
418 gtk_action_group_add_radio_actions_full (GtkActionGroup      *action_group,
419                                          GtkRadioActionEntry *entries,
420                                          guint                n_entries,
421                                          GCallback            on_change,
422                                          gpointer             user_data,
423                                          GDestroyNotify       destroy)
424 {
425   guint i;
426   GtkTranslateFunc translate_func;
427   gpointer translate_data;
428
429   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
430
431   translate_func = action_group->private_data->translate_func;
432   translate_data = action_group->private_data->translate_data;
433
434   for (i = 0; i < n_entries; i++)
435     {
436       GtkAction *action;
437       gchar *accel_path;
438       gchar *label;
439       gchar *tooltip; 
440
441       if (translate_func)
442         {
443           label = translate_func (entries[i].label, translate_data);
444           tooltip = translate_func (entries[i].tooltip, translate_data);
445         }
446       else
447         {
448           label = entries[i].label;
449           tooltip = entries[i].tooltip;
450         }
451
452       action = g_object_new (GTK_TYPE_RADIO_ACTION,
453                              "name", entries[i].name,
454                              "label", label,
455                              "tooltip", tooltip,
456                              "stock_id", entries[i].stock_id,
457                              "value", entries[i].value,
458                              NULL);
459       
460       if (i == 0) 
461         {
462           if (on_change)
463             g_signal_connect_data (action, "changed",
464                                    on_change, user_data, 
465                                    (GClosureNotify)destroy, 0);
466         }
467       else
468         {
469           GSList *group = gtk_radio_action_get_group (GTK_RADIO_ACTION (action));
470           gtk_radio_action_set_group (GTK_RADIO_ACTION (action), group);
471         }
472
473       /* set the accel path for the menu item */
474       accel_path = g_strconcat ("<Actions>/", action_group->private_data->name, "/",
475                                 entries[i].name, NULL);
476       if (entries[i].accelerator)
477         {
478           guint accel_key = 0;
479           GdkModifierType accel_mods;
480
481           gtk_accelerator_parse (entries[i].accelerator, &accel_key,
482                                  &accel_mods);
483           if (accel_key)
484             gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);
485         }
486
487       gtk_action_set_accel_path (action, accel_path);
488       g_free (accel_path);
489       
490       gtk_action_group_add_action (action_group, action);
491       g_object_unref (action);
492     }
493 }
494
495 /**
496  * gtk_action_group_set_translate_func:
497  * @action_group: a #GtkActionGroup
498  * @func: a #GtkTranslateFunc
499  * @data: data to be passed to @func and @notify
500  * @notify: a #GtkDestroyNotify function to be called when @action_group is 
501  *   destroyed and when the translation function is changed again
502  * 
503  * Sets a function to be used for translating the @label and @tooltip of 
504  * #GtkActionGroupEntry<!-- -->s added by gtk_action_group_add_actions().
505  *
506  * If you're using gettext(), it is enough to set the translation domain
507  * with gtk_action_group_set_translation_domain().
508  *
509  * Since: 2.4 
510  **/
511 void
512 gtk_action_group_set_translate_func (GtkActionGroup      *action_group,
513                                      GtkTranslateFunc     func,
514                                      gpointer             data,
515                                      GtkDestroyNotify     notify)
516 {
517   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
518   
519   if (action_group->private_data->translate_notify)
520     action_group->private_data->translate_notify (action_group->private_data->translate_data);
521       
522   action_group->private_data->translate_func = func;
523   action_group->private_data->translate_data = data;
524   action_group->private_data->translate_notify = notify;
525 }
526
527 static gchar *
528 dgettext_swapped (const gchar *msgid, 
529                   const gchar *domainname)
530 {
531   return dgettext (domainname, msgid);
532 }
533
534 /**
535  * gtk_action_group_set_translation_domain:
536  * @action_group: a #GtkActionGroup
537  * @domain: the translation domain to use for dgettext() calls
538  * 
539  * Sets the translation domain and uses dgettext() for translating the 
540  * @label and @tooltip of #GtkActionEntry<!-- -->s added by 
541  * gtk_action_group_add_actions().
542  *
543  * If you're not using gettext() for localization, see 
544  * gtk_action_group_set_translate_func().
545  *
546  * Since: 2.4
547  **/
548 void 
549 gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
550                                          const gchar    *domain)
551 {
552   g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
553
554   gtk_action_group_set_translate_func (action_group, 
555                                        (GtkTranslateFunc)dgettext_swapped,
556                                        g_strdup (domain),
557                                        g_free);
558