]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
Fix a memory leak in GtkAccelGroup
[~andy/gtk] / gtk / gtkaccelgroup.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1998, 2001 Tim Janik
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "config.h"
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "gtkaccelgroup.h"
32 #include "gtkaccellabel.h" /* For _gtk_accel_label_class_get_accelerator_label */
33 #include "gtkaccelmap.h"
34 #include "gtkintl.h"
35 #include "gtkmain.h"            /* For _gtk_boolean_handled_accumulator */
36 #include "gdk/gdkkeysyms.h"
37 #include "gtkmarshalers.h"
38 #include "gtkalias.h"
39
40
41 /* --- prototypes --- */
42 static void gtk_accel_group_finalize     (GObject    *object);
43 static void gtk_accel_group_get_property (GObject    *object,
44                                           guint       param_id,
45                                           GValue     *value,
46                                           GParamSpec *pspec);
47 static void accel_closure_invalidate     (gpointer    data,
48                                           GClosure   *closure);
49
50
51 /* --- variables --- */
52 static guint  signal_accel_activate      = 0;
53 static guint  signal_accel_changed       = 0;
54 static guint  quark_acceleratable_groups = 0;
55 static guint  default_accel_mod_mask     = (GDK_SHIFT_MASK   |
56                                             GDK_CONTROL_MASK |
57                                             GDK_MOD1_MASK    |
58                                             GDK_SUPER_MASK   |
59                                             GDK_HYPER_MASK   |
60                                             GDK_META_MASK);
61
62
63 enum {
64   PROP_0,
65   PROP_IS_LOCKED,
66   PROP_MODIFIER_MASK,
67 };
68
69 G_DEFINE_TYPE (GtkAccelGroup, gtk_accel_group, G_TYPE_OBJECT)
70
71 /* --- functions --- */
72 static void
73 gtk_accel_group_class_init (GtkAccelGroupClass *class)
74 {
75   GObjectClass *object_class = G_OBJECT_CLASS (class);
76
77   quark_acceleratable_groups = g_quark_from_static_string ("gtk-acceleratable-accel-groups");
78
79   object_class->finalize = gtk_accel_group_finalize;
80   object_class->get_property = gtk_accel_group_get_property;
81
82   class->accel_changed = NULL;
83
84   g_object_class_install_property (object_class,
85                                    PROP_IS_LOCKED,
86                                    g_param_spec_boolean ("is-locked",
87                                                          "Is locked",
88                                                          "Is the accel group locked",
89                                                          FALSE,
90                                                          G_PARAM_READABLE));
91
92   g_object_class_install_property (object_class,
93                                    PROP_MODIFIER_MASK,
94                                    g_param_spec_flags ("modifier-mask",
95                                                        "Modifier Mask",
96                                                        "Modifier Mask",
97                                                        GDK_TYPE_MODIFIER_TYPE,
98                                                        default_accel_mod_mask,
99                                                        G_PARAM_READABLE));
100
101   /**
102    * GtkAccelGroup::accel-activate:
103    * @accel_group: the #GtkAccelGroup which received the signal
104    * @acceleratable: the object on which the accelerator was activated
105    * @keyval: the accelerator keyval
106    * @modifier: the modifier combination of the accelerator
107    *
108    * The accel-activate signal is an implementation detail of
109    * #GtkAccelGroup and not meant to be used by applications.
110    * 
111    * Returns: %TRUE if the accelerator was activated
112    */
113   signal_accel_activate =
114     g_signal_new (I_("accel-activate"),
115                   G_OBJECT_CLASS_TYPE (class),
116                   G_SIGNAL_DETAILED,
117                   0,
118                   _gtk_boolean_handled_accumulator, NULL,
119                   _gtk_marshal_BOOLEAN__OBJECT_UINT_FLAGS,
120                   G_TYPE_BOOLEAN, 3,
121                   G_TYPE_OBJECT,
122                   G_TYPE_UINT,
123                   GDK_TYPE_MODIFIER_TYPE);
124   /**
125    * GtkAccelGroup::accel-changed:
126    * @accel_group: the #GtkAccelGroup which received the signal
127    * @keyval: the accelerator keyval
128    * @modifier: the modifier combination of the accelerator
129    * @accel_closure: the #GClosure of the accelerator
130    *
131    * The accel-changed signal is emitted when a #GtkAccelGroupEntry
132    * is added to or removed from the accel group. 
133    *
134    * Widgets like #GtkAccelLabel which display an associated 
135    * accelerator should connect to this signal, and rebuild 
136    * their visual representation if the @accel_closure is theirs.
137    */
138   signal_accel_changed =
139     g_signal_new (I_("accel-changed"),
140                   G_OBJECT_CLASS_TYPE (class),
141                   G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
142                   G_STRUCT_OFFSET (GtkAccelGroupClass, accel_changed),
143                   NULL, NULL,
144                   _gtk_marshal_VOID__UINT_FLAGS_BOXED,
145                   G_TYPE_NONE, 3,
146                   G_TYPE_UINT,
147                   GDK_TYPE_MODIFIER_TYPE,
148                   G_TYPE_CLOSURE);
149 }
150
151 static void
152 gtk_accel_group_finalize (GObject *object)
153 {
154   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
155   guint i;
156   
157   for (i = 0; i < accel_group->n_accels; i++)
158     {
159       GtkAccelGroupEntry *entry = &accel_group->priv_accels[i];
160
161       if (entry->accel_path_quark)
162         {
163           const gchar *accel_path = g_quark_to_string (entry->accel_path_quark);
164
165           _gtk_accel_map_remove_group (accel_path, accel_group);
166         }
167       g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
168
169       /* remove quick_accel_add() refcount */
170       g_closure_unref (entry->closure);
171     }
172
173   g_free (accel_group->priv_accels);
174
175   G_OBJECT_CLASS (gtk_accel_group_parent_class)->finalize (object);
176 }
177
178 static void
179 gtk_accel_group_get_property (GObject    *object,
180                               guint       param_id,
181                               GValue     *value,
182                               GParamSpec *pspec)
183 {
184   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (object);
185
186   switch (param_id)
187     {
188     case PROP_IS_LOCKED:
189       g_value_set_boolean (value, accel_group->lock_count > 0);
190       break;
191     case PROP_MODIFIER_MASK:
192       g_value_set_flags (value, accel_group->modifier_mask);
193       break;
194     default:
195       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
196       break;
197     }
198 }
199
200 static void
201 gtk_accel_group_init (GtkAccelGroup *accel_group)
202 {
203   accel_group->lock_count = 0;
204   accel_group->modifier_mask = gtk_accelerator_get_default_mod_mask ();
205   accel_group->acceleratables = NULL;
206   accel_group->n_accels = 0;
207   accel_group->priv_accels = NULL;
208 }
209
210 /**
211  * gtk_accel_group_new:
212  * @returns: a new #GtkAccelGroup object
213  * 
214  * Creates a new #GtkAccelGroup. 
215  */
216 GtkAccelGroup*
217 gtk_accel_group_new (void)
218 {
219   return g_object_new (GTK_TYPE_ACCEL_GROUP, NULL);
220 }
221
222 /**
223  * gtk_accel_group_get_is_locked:
224  * @accel_group: a #GtkAccelGroup
225  *
226  * Locks are added and removed using gtk_accel_group_lock() and
227  * gtk_accel_group_unlock().
228  *
229  * Returns: %TRUE if there are 1 or more locks on the @accel_group,
230  * %FALSE otherwise.
231  *
232  * Since: 2.14
233  */
234 gboolean
235 gtk_accel_group_get_is_locked (GtkAccelGroup *accel_group)
236 {
237   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
238
239   return accel_group->lock_count > 0;
240 }
241
242 /**
243  * gtk_accel_group_get_modifier_mask:
244  * @accel_group: a #GtkAccelGroup
245  *
246  * Gets a #GdkModifierType representing the mask for this
247  * @accel_group. For example, #GDK_CONTROL_MASK, #GDK_SHIFT_MASK, etc.
248  *
249  * Returns: the modifier mask for this accel group.
250  *
251  * Since: 2.14
252  */
253 GdkModifierType
254 gtk_accel_group_get_modifier_mask (GtkAccelGroup *accel_group)
255 {
256   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), 0);
257
258   return accel_group->modifier_mask;
259 }
260
261 static void
262 accel_group_weak_ref_detach (GSList  *free_list,
263                              GObject *stale_object)
264 {
265   GSList *slist;
266   
267   for (slist = free_list; slist; slist = slist->next)
268     {
269       GtkAccelGroup *accel_group;
270       
271       accel_group = slist->data;
272       accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, stale_object);
273       g_object_unref (accel_group);
274     }
275   g_slist_free (free_list);
276   g_object_set_qdata (stale_object, quark_acceleratable_groups, NULL);
277 }
278
279 void
280 _gtk_accel_group_attach (GtkAccelGroup *accel_group,
281                          GObject       *object)
282 {
283   GSList *slist;
284   
285   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
286   g_return_if_fail (G_IS_OBJECT (object));
287   g_return_if_fail (g_slist_find (accel_group->acceleratables, object) == NULL);
288   
289   g_object_ref (accel_group);
290   accel_group->acceleratables = g_slist_prepend (accel_group->acceleratables, object);
291   slist = g_object_get_qdata (object, quark_acceleratable_groups);
292   if (slist)
293     g_object_weak_unref (object,
294                          (GWeakNotify) accel_group_weak_ref_detach,
295                          slist);
296   slist = g_slist_prepend (slist, accel_group);
297   g_object_set_qdata (object, quark_acceleratable_groups, slist);
298   g_object_weak_ref (object,
299                      (GWeakNotify) accel_group_weak_ref_detach,
300                      slist);
301 }
302
303 void
304 _gtk_accel_group_detach (GtkAccelGroup *accel_group,
305                          GObject       *object)
306 {
307   GSList *slist;
308   
309   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
310   g_return_if_fail (G_IS_OBJECT (object));
311   g_return_if_fail (g_slist_find (accel_group->acceleratables, object) != NULL);
312   
313   accel_group->acceleratables = g_slist_remove (accel_group->acceleratables, object);
314   slist = g_object_get_qdata (object, quark_acceleratable_groups);
315   g_object_weak_unref (object,
316                        (GWeakNotify) accel_group_weak_ref_detach,
317                        slist);
318   slist = g_slist_remove (slist, accel_group);
319   g_object_set_qdata (object, quark_acceleratable_groups, slist);
320   if (slist)
321     g_object_weak_ref (object,
322                        (GWeakNotify) accel_group_weak_ref_detach,
323                        slist);
324   g_object_unref (accel_group);
325 }
326
327 /**
328  * gtk_accel_groups_from_object:
329  * @object:        a #GObject, usually a #GtkWindow 
330  * @returns: a list of all accel groups which are attached to @object
331  *
332  * Gets a list of all accel groups which are attached to @object.
333  */
334 GSList*
335 gtk_accel_groups_from_object (GObject *object)
336 {
337   g_return_val_if_fail (G_IS_OBJECT (object), NULL);
338   
339   return g_object_get_qdata (object, quark_acceleratable_groups);
340 }
341
342 /**
343  * gtk_accel_group_find:
344  * @accel_group: a #GtkAccelGroup
345  * @find_func: a function to filter the entries of @accel_group with
346  * @data: data to pass to @find_func
347  * @returns: the key of the first entry passing @find_func. The key is 
348  * owned by GTK+ and must not be freed.
349  *
350  * Finds the first entry in an accelerator group for which 
351  * @find_func returns %TRUE and returns its #GtkAccelKey.
352  *
353  */
354 GtkAccelKey*
355 gtk_accel_group_find (GtkAccelGroup        *accel_group,
356                       GtkAccelGroupFindFunc find_func,
357                       gpointer              data)
358 {
359   GtkAccelKey *key = NULL;
360   guint i;
361
362   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
363   g_return_val_if_fail (find_func != NULL, NULL);
364
365   g_object_ref (accel_group);
366   for (i = 0; i < accel_group->n_accels; i++)
367     if (find_func (&accel_group->priv_accels[i].key,
368                    accel_group->priv_accels[i].closure,
369                    data))
370       {
371         key = &accel_group->priv_accels[i].key;
372         break;
373       }
374   g_object_unref (accel_group);
375
376   return key;
377 }
378
379 /**
380  * gtk_accel_group_lock:
381  * @accel_group: a #GtkAccelGroup
382  * 
383  * Locks the given accelerator group.
384  *
385  * Locking an acelerator group prevents the accelerators contained
386  * within it to be changed during runtime. Refer to
387  * gtk_accel_map_change_entry() about runtime accelerator changes.
388  *
389  * If called more than once, @accel_group remains locked until
390  * gtk_accel_group_unlock() has been called an equivalent number
391  * of times.
392  */
393 void
394 gtk_accel_group_lock (GtkAccelGroup *accel_group)
395 {
396   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
397   
398   accel_group->lock_count += 1;
399
400   if (accel_group->lock_count == 1) {
401     /* State change from unlocked to locked */
402     g_object_notify (G_OBJECT (accel_group), "is-locked");
403   }
404 }
405
406 /**
407  * gtk_accel_group_unlock:
408  * @accel_group: a #GtkAccelGroup
409  * 
410  * Undoes the last call to gtk_accel_group_lock() on this @accel_group.
411  */
412 void
413 gtk_accel_group_unlock (GtkAccelGroup *accel_group)
414 {
415   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
416   g_return_if_fail (accel_group->lock_count > 0);
417
418   accel_group->lock_count -= 1;
419
420   if (accel_group->lock_count < 1) {
421     /* State change from locked to unlocked */
422     g_object_notify (G_OBJECT (accel_group), "is-locked");
423   }
424 }
425
426 static void
427 accel_closure_invalidate (gpointer  data,
428                           GClosure *closure)
429 {
430   GtkAccelGroup *accel_group = GTK_ACCEL_GROUP (data);
431
432   gtk_accel_group_disconnect (accel_group, closure);
433 }
434
435 static int
436 bsearch_compare_accels (const void *d1,
437                         const void *d2)
438 {
439   const GtkAccelGroupEntry *entry1 = d1;
440   const GtkAccelGroupEntry *entry2 = d2;
441
442   if (entry1->key.accel_key == entry2->key.accel_key)
443     return entry1->key.accel_mods < entry2->key.accel_mods ? -1 : entry1->key.accel_mods > entry2->key.accel_mods;
444   else
445     return entry1->key.accel_key < entry2->key.accel_key ? -1 : 1;
446 }
447
448 static void
449 quick_accel_add (GtkAccelGroup  *accel_group,
450                  guint           accel_key,
451                  GdkModifierType accel_mods,
452                  GtkAccelFlags   accel_flags,
453                  GClosure       *closure,
454                  GQuark          path_quark)
455 {
456   guint pos, i = accel_group->n_accels++;
457   GtkAccelGroupEntry key;
458
459   /* find position */
460   key.key.accel_key = accel_key;
461   key.key.accel_mods = accel_mods;
462   for (pos = 0; pos < i; pos++)
463     if (bsearch_compare_accels (&key, accel_group->priv_accels + pos) < 0)
464       break;
465
466   /* insert at position, ref closure */
467   accel_group->priv_accels = g_renew (GtkAccelGroupEntry, accel_group->priv_accels, accel_group->n_accels);
468   g_memmove (accel_group->priv_accels + pos + 1, accel_group->priv_accels + pos,
469              (i - pos) * sizeof (accel_group->priv_accels[0]));
470   accel_group->priv_accels[pos].key.accel_key = accel_key;
471   accel_group->priv_accels[pos].key.accel_mods = accel_mods;
472   accel_group->priv_accels[pos].key.accel_flags = accel_flags;
473   accel_group->priv_accels[pos].closure = g_closure_ref (closure);
474   accel_group->priv_accels[pos].accel_path_quark = path_quark;
475   g_closure_sink (closure);
476   
477   /* handle closure invalidation and reverse lookups */
478   g_closure_add_invalidate_notifier (closure, accel_group, accel_closure_invalidate);
479
480   /* get accel path notification */
481   if (path_quark)
482     _gtk_accel_map_add_group (g_quark_to_string (path_quark), accel_group);
483
484   /* connect and notify changed */
485   if (accel_key)
486     {
487       gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
488       GQuark accel_quark = g_quark_from_string (accel_name);
489
490       g_free (accel_name);
491       
492       /* setup handler */
493       g_signal_connect_closure_by_id (accel_group, signal_accel_activate, accel_quark, closure, FALSE);
494       
495       /* and notify */
496       g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
497     }
498 }
499
500 static void
501 quick_accel_remove (GtkAccelGroup      *accel_group,
502                     guint               pos)
503 {
504   GQuark accel_quark = 0;
505   GtkAccelGroupEntry *entry = accel_group->priv_accels + pos;
506   guint accel_key = entry->key.accel_key;
507   GdkModifierType accel_mods = entry->key.accel_mods;
508   GClosure *closure = entry->closure;
509
510   /* quark for notification */
511   if (accel_key)
512     {
513       gchar *accel_name = gtk_accelerator_name (accel_key, accel_mods);
514
515       accel_quark = g_quark_from_string (accel_name);
516       g_free (accel_name);
517     }
518
519   /* clean up closure invalidate notification and disconnect */
520   g_closure_remove_invalidate_notifier (entry->closure, accel_group, accel_closure_invalidate);
521   if (accel_quark)
522     g_signal_handlers_disconnect_matched (accel_group,
523                                           G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_CLOSURE,
524                                           signal_accel_activate, accel_quark,
525                                           closure, NULL, NULL);
526   /* clean up accel path notification */
527   if (entry->accel_path_quark)
528     _gtk_accel_map_remove_group (g_quark_to_string (entry->accel_path_quark), accel_group);
529
530   /* physically remove */
531   accel_group->n_accels -= 1;
532   g_memmove (entry, entry + 1,
533              (accel_group->n_accels - pos) * sizeof (accel_group->priv_accels[0]));
534
535   /* and notify */
536   if (accel_quark)
537     g_signal_emit (accel_group, signal_accel_changed, accel_quark, accel_key, accel_mods, closure);
538
539   /* remove quick_accel_add() refcount */
540   g_closure_unref (closure);
541 }
542
543 static GtkAccelGroupEntry*
544 quick_accel_find (GtkAccelGroup  *accel_group,
545                   guint           accel_key,
546                   GdkModifierType accel_mods,
547                   guint          *count_p)
548 {
549   GtkAccelGroupEntry *entry;
550   GtkAccelGroupEntry key;
551
552   *count_p = 0;
553
554   if (!accel_group->n_accels)
555     return NULL;
556
557   key.key.accel_key = accel_key;
558   key.key.accel_mods = accel_mods;
559   entry = bsearch (&key, accel_group->priv_accels, accel_group->n_accels,
560                    sizeof (accel_group->priv_accels[0]), bsearch_compare_accels);
561   
562   if (!entry)
563     return NULL;
564
565   /* step back to the first member */
566   for (; entry > accel_group->priv_accels; entry--)
567     if (entry[-1].key.accel_key != accel_key ||
568         entry[-1].key.accel_mods != accel_mods)
569       break;
570   /* count equal members */
571   for (; entry + *count_p < accel_group->priv_accels + accel_group->n_accels; (*count_p)++)
572     if (entry[*count_p].key.accel_key != accel_key ||
573         entry[*count_p].key.accel_mods != accel_mods)
574       break;
575   return entry;
576 }
577
578 /**
579  * gtk_accel_group_connect:
580  * @accel_group:      the accelerator group to install an accelerator in
581  * @accel_key:        key value of the accelerator
582  * @accel_mods:       modifier combination of the accelerator
583  * @accel_flags:      a flag mask to configure this accelerator
584  * @closure:          closure to be executed upon accelerator activation
585  *
586  * Installs an accelerator in this group. When @accel_group is being activated
587  * in response to a call to gtk_accel_groups_activate(), @closure will be
588  * invoked if the @accel_key and @accel_mods from gtk_accel_groups_activate()
589  * match those of this connection.
590  *
591  * The signature used for the @closure is that of #GtkAccelGroupActivate.
592  * 
593  * Note that, due to implementation details, a single closure can only be
594  * connected to one accelerator group.
595  */
596 void
597 gtk_accel_group_connect (GtkAccelGroup  *accel_group,
598                          guint           accel_key,
599                          GdkModifierType accel_mods,
600                          GtkAccelFlags   accel_flags,
601                          GClosure       *closure)
602 {
603   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
604   g_return_if_fail (closure != NULL);
605   g_return_if_fail (accel_key > 0);
606   g_return_if_fail (gtk_accel_group_from_accel_closure (closure) == NULL);
607
608   g_object_ref (accel_group);
609   if (!closure->is_invalid)
610     quick_accel_add (accel_group,
611                      gdk_keyval_to_lower (accel_key),
612                      accel_mods, accel_flags, closure, 0);
613   g_object_unref (accel_group);
614 }
615
616 /**
617  * gtk_accel_group_connect_by_path:
618  * @accel_group:      the accelerator group to install an accelerator in
619  * @accel_path:       path used for determining key and modifiers.
620  * @closure:          closure to be executed upon accelerator activation
621  *
622  * Installs an accelerator in this group, using an accelerator path to look
623  * up the appropriate key and modifiers (see gtk_accel_map_add_entry()).
624  * When @accel_group is being activated in response to a call to
625  * gtk_accel_groups_activate(), @closure will be invoked if the @accel_key and
626  * @accel_mods from gtk_accel_groups_activate() match the key and modifiers
627  * for the path.
628  *
629  * The signature used for the @closure is that of #GtkAccelGroupActivate.
630  * 
631  * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
632  * pass a static string, you can save some memory by interning it first with 
633  * g_intern_static_string().
634  */
635 void
636 gtk_accel_group_connect_by_path (GtkAccelGroup  *accel_group,
637                                  const gchar    *accel_path,
638                                  GClosure       *closure)
639 {
640   guint accel_key = 0;
641   GdkModifierType accel_mods = 0;
642   GtkAccelKey key;
643
644   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
645   g_return_if_fail (closure != NULL);
646   g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
647
648   if (closure->is_invalid)
649     return;
650
651   g_object_ref (accel_group);
652
653   if (gtk_accel_map_lookup_entry (accel_path, &key))
654     {
655       accel_key = gdk_keyval_to_lower (key.accel_key);
656       accel_mods = key.accel_mods;
657     }
658
659   quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure,
660                    g_quark_from_string (accel_path));
661
662   g_object_unref (accel_group);
663 }
664
665 /**
666  * gtk_accel_group_disconnect:
667  * @accel_group: the accelerator group to remove an accelerator from
668  * @closure:     the closure to remove from this accelerator group
669  * @returns:     %TRUE if the closure was found and got disconnected
670  *
671  * Removes an accelerator previously installed through
672  * gtk_accel_group_connect().
673  */
674 gboolean
675 gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
676                             GClosure      *closure)
677 {
678   guint i;
679
680   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
681
682   for (i = 0; i < accel_group->n_accels; i++)
683     if (accel_group->priv_accels[i].closure == closure)
684       {
685         g_object_ref (accel_group);
686         quick_accel_remove (accel_group, i);
687         g_object_unref (accel_group);
688         return TRUE;
689       }
690   return FALSE;
691 }
692
693 /**
694  * gtk_accel_group_disconnect_key:
695  * @accel_group:      the accelerator group to install an accelerator in
696  * @accel_key:        key value of the accelerator
697  * @accel_mods:       modifier combination of the accelerator
698  * @returns:          %TRUE if there was an accelerator which could be 
699  *                    removed, %FALSE otherwise
700  *
701  * Removes an accelerator previously installed through
702  * gtk_accel_group_connect().
703  */
704 gboolean
705 gtk_accel_group_disconnect_key (GtkAccelGroup  *accel_group,
706                                 guint           accel_key,
707                                 GdkModifierType accel_mods)
708 {
709   GtkAccelGroupEntry *entries;
710   GSList *slist, *clist = NULL;
711   gboolean removed_one = FALSE;
712   guint n;
713
714   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
715
716   g_object_ref (accel_group);
717   
718   accel_key = gdk_keyval_to_lower (accel_key);
719   entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);
720   while (n--)
721     {
722       GClosure *closure = g_closure_ref (entries[n].closure);
723
724       clist = g_slist_prepend (clist, closure);
725     }
726
727   for (slist = clist; slist; slist = slist->next)
728     {
729       GClosure *closure = slist->data;
730
731       removed_one |= gtk_accel_group_disconnect (accel_group, closure);
732       g_closure_unref (closure);
733     }
734   g_slist_free (clist);
735
736   g_object_unref (accel_group);
737
738   return removed_one;
739 }
740
741 void
742 _gtk_accel_group_reconnect (GtkAccelGroup *accel_group,
743                             GQuark         accel_path_quark)
744 {
745   GSList *slist, *clist = NULL;
746   guint i;
747
748   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
749
750   g_object_ref (accel_group);
751
752   for (i = 0; i < accel_group->n_accels; i++)
753     if (accel_group->priv_accels[i].accel_path_quark == accel_path_quark)
754       {
755         GClosure *closure = g_closure_ref (accel_group->priv_accels[i].closure);
756
757         clist = g_slist_prepend (clist, closure);
758       }
759
760   for (slist = clist; slist; slist = slist->next)
761     {
762       GClosure *closure = slist->data;
763
764       gtk_accel_group_disconnect (accel_group, closure);
765       gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure);
766       g_closure_unref (closure);
767     }
768   g_slist_free (clist);
769
770   g_object_unref (accel_group);
771 }
772
773 /**
774  * gtk_accel_group_query:
775  * @accel_group:      the accelerator group to query
776  * @accel_key:        key value of the accelerator
777  * @accel_mods:       modifier combination of the accelerator
778  * @n_entries:        location to return the number of entries found, or %NULL
779  * @returns:          an array of @n_entries #GtkAccelGroupEntry elements, or %NULL. The array is owned by GTK+ and must not be freed. 
780  *
781  * Queries an accelerator group for all entries matching @accel_key and 
782  * @accel_mods.
783  */
784 GtkAccelGroupEntry*
785 gtk_accel_group_query (GtkAccelGroup  *accel_group,
786                        guint           accel_key,
787                        GdkModifierType accel_mods,
788                        guint          *n_entries)
789 {
790   GtkAccelGroupEntry *entries;
791   guint n;
792
793   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
794
795   entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n);
796
797   if (n_entries)
798     *n_entries = entries ? n : 0;
799
800   return entries;
801 }
802
803 /**
804  * gtk_accel_group_from_accel_closure:
805  * @closure: a #GClosure
806  * @returns: the #GtkAccelGroup to which @closure is connected, or %NULL.
807  *
808  * Finds the #GtkAccelGroup to which @closure is connected; 
809  * see gtk_accel_group_connect().
810  */
811 GtkAccelGroup*
812 gtk_accel_group_from_accel_closure (GClosure *closure)
813 {
814   guint i;
815
816   g_return_val_if_fail (closure != NULL, NULL);
817
818   /* a few remarks on what we do here. in general, we need a way to reverse lookup
819    * accel_groups from closures that are being used in accel groups. this could
820    * be done e.g via a hashtable. it is however cheaper (memory wise) to just
821    * use the invalidation notifier on the closure itself (which we need to install
822    * anyway), that contains the accel group as data which, besides needing to peek
823    * a bit at closure internals, works just as good.
824    */
825   for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
826     if (closure->notifiers[i].notify == accel_closure_invalidate)
827       return closure->notifiers[i].data;
828
829   return NULL;
830 }
831
832 /**
833  * gtk_accel_group_activate:
834  * @accel_group:   a #GtkAccelGroup
835  * @accel_quark:   the quark for the accelerator name
836  * @acceleratable: the #GObject, usually a #GtkWindow, on which
837  *                 to activate the accelerator.
838  * @accel_key:     accelerator keyval from a key event
839  * @accel_mods:    keyboard state mask from a key event
840  * 
841  * Finds the first accelerator in @accel_group 
842  * that matches @accel_key and @accel_mods, and
843  * activates it.
844  *
845  * Returns: %TRUE if an accelerator was activated and handled this keypress
846  */
847 gboolean
848 gtk_accel_group_activate (GtkAccelGroup   *accel_group,
849                           GQuark           accel_quark,
850                           GObject         *acceleratable,
851                           guint            accel_key,
852                           GdkModifierType  accel_mods)
853 {
854   gboolean was_handled;
855
856   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
857   g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
858   
859   was_handled = FALSE;
860   g_signal_emit (accel_group, signal_accel_activate, accel_quark,
861                  acceleratable, accel_key, accel_mods, &was_handled);
862
863   return was_handled;
864 }
865
866 /**
867  * gtk_accel_groups_activate:
868  * @object:        the #GObject, usually a #GtkWindow, on which
869  *                 to activate the accelerator.
870  * @accel_key:     accelerator keyval from a key event
871  * @accel_mods:    keyboard state mask from a key event
872  * 
873  * Finds the first accelerator in any #GtkAccelGroup attached
874  * to @object that matches @accel_key and @accel_mods, and
875  * activates that accelerator.
876  *
877  * Returns: %TRUE if an accelerator was activated and handled this keypress
878  */
879 gboolean
880 gtk_accel_groups_activate (GObject        *object,
881                            guint           accel_key,
882                            GdkModifierType accel_mods)
883 {
884   g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
885   
886   if (gtk_accelerator_valid (accel_key, accel_mods))
887     {
888       gchar *accel_name;
889       GQuark accel_quark;
890       GSList *slist;
891
892       accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
893       accel_quark = g_quark_from_string (accel_name);
894       g_free (accel_name);
895       
896       for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
897         if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
898           return TRUE;
899     }
900   
901   return FALSE;
902 }
903
904 /**
905  * gtk_accelerator_valid:
906  * @keyval:    a GDK keyval
907  * @modifiers: modifier mask
908  * @returns:   %TRUE if the accelerator is valid
909  * 
910  * Determines whether a given keyval and modifier mask constitute
911  * a valid keyboard accelerator. For example, the #GDK_a keyval
912  * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
913  * But, you can't, for instance, use the #GDK_Control_L keyval
914  * as an accelerator.
915  */
916 gboolean
917 gtk_accelerator_valid (guint              keyval,
918                        GdkModifierType    modifiers)
919 {
920   static const guint invalid_accelerator_vals[] = {
921     GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
922     GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
923     GDK_Alt_L, GDK_Alt_R, GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
924     GDK_ISO_Level3_Shift, GDK_ISO_Next_Group, GDK_ISO_Prev_Group,
925     GDK_ISO_First_Group, GDK_ISO_Last_Group,
926     GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
927     GDK_Scroll_Lock, GDK_Sys_Req, 
928     GDK_Tab, GDK_ISO_Left_Tab, GDK_KP_Tab,
929     GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
930     GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
931     GDK_Terminate_Server, GDK_AudibleBell_Enable,
932     0
933   };
934   static const guint invalid_unmodified_vals[] = {
935     GDK_Up, GDK_Down, GDK_Left, GDK_Right,
936     GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right,
937     0
938   };
939   const guint *ac_val;
940
941   modifiers &= GDK_MODIFIER_MASK;
942     
943   if (keyval <= 0xFF)
944     return keyval >= 0x20;
945
946   ac_val = invalid_accelerator_vals;
947   while (*ac_val)
948     {
949       if (keyval == *ac_val++)
950         return FALSE;
951     }
952
953   if (!modifiers)
954     {
955       ac_val = invalid_unmodified_vals;
956       while (*ac_val)
957         {
958           if (keyval == *ac_val++)
959             return FALSE;
960         }
961     }
962   
963   return TRUE;
964 }
965
966 static inline gboolean
967 is_alt (const gchar *string)
968 {
969   return ((string[0] == '<') &&
970           (string[1] == 'a' || string[1] == 'A') &&
971           (string[2] == 'l' || string[2] == 'L') &&
972           (string[3] == 't' || string[3] == 'T') &&
973           (string[4] == '>'));
974 }
975
976 static inline gboolean
977 is_ctl (const gchar *string)
978 {
979   return ((string[0] == '<') &&
980           (string[1] == 'c' || string[1] == 'C') &&
981           (string[2] == 't' || string[2] == 'T') &&
982           (string[3] == 'l' || string[3] == 'L') &&
983           (string[4] == '>'));
984 }
985
986 static inline gboolean
987 is_modx (const gchar *string)
988 {
989   return ((string[0] == '<') &&
990           (string[1] == 'm' || string[1] == 'M') &&
991           (string[2] == 'o' || string[2] == 'O') &&
992           (string[3] == 'd' || string[3] == 'D') &&
993           (string[4] >= '1' && string[4] <= '5') &&
994           (string[5] == '>'));
995 }
996
997 static inline gboolean
998 is_ctrl (const gchar *string)
999 {
1000   return ((string[0] == '<') &&
1001           (string[1] == 'c' || string[1] == 'C') &&
1002           (string[2] == 't' || string[2] == 'T') &&
1003           (string[3] == 'r' || string[3] == 'R') &&
1004           (string[4] == 'l' || string[4] == 'L') &&
1005           (string[5] == '>'));
1006 }
1007
1008 static inline gboolean
1009 is_shft (const gchar *string)
1010 {
1011   return ((string[0] == '<') &&
1012           (string[1] == 's' || string[1] == 'S') &&
1013           (string[2] == 'h' || string[2] == 'H') &&
1014           (string[3] == 'f' || string[3] == 'F') &&
1015           (string[4] == 't' || string[4] == 'T') &&
1016           (string[5] == '>'));
1017 }
1018
1019 static inline gboolean
1020 is_shift (const gchar *string)
1021 {
1022   return ((string[0] == '<') &&
1023           (string[1] == 's' || string[1] == 'S') &&
1024           (string[2] == 'h' || string[2] == 'H') &&
1025           (string[3] == 'i' || string[3] == 'I') &&
1026           (string[4] == 'f' || string[4] == 'F') &&
1027           (string[5] == 't' || string[5] == 'T') &&
1028           (string[6] == '>'));
1029 }
1030
1031 static inline gboolean
1032 is_control (const gchar *string)
1033 {
1034   return ((string[0] == '<') &&
1035           (string[1] == 'c' || string[1] == 'C') &&
1036           (string[2] == 'o' || string[2] == 'O') &&
1037           (string[3] == 'n' || string[3] == 'N') &&
1038           (string[4] == 't' || string[4] == 'T') &&
1039           (string[5] == 'r' || string[5] == 'R') &&
1040           (string[6] == 'o' || string[6] == 'O') &&
1041           (string[7] == 'l' || string[7] == 'L') &&
1042           (string[8] == '>'));
1043 }
1044
1045 static inline gboolean
1046 is_release (const gchar *string)
1047 {
1048   return ((string[0] == '<') &&
1049           (string[1] == 'r' || string[1] == 'R') &&
1050           (string[2] == 'e' || string[2] == 'E') &&
1051           (string[3] == 'l' || string[3] == 'L') &&
1052           (string[4] == 'e' || string[4] == 'E') &&
1053           (string[5] == 'a' || string[5] == 'A') &&
1054           (string[6] == 's' || string[6] == 'S') &&
1055           (string[7] == 'e' || string[7] == 'E') &&
1056           (string[8] == '>'));
1057 }
1058
1059 static inline gboolean
1060 is_meta (const gchar *string)
1061 {
1062   return ((string[0] == '<') &&
1063           (string[1] == 'm' || string[1] == 'M') &&
1064           (string[2] == 'e' || string[2] == 'E') &&
1065           (string[3] == 't' || string[3] == 'T') &&
1066           (string[4] == 'a' || string[4] == 'A') &&
1067           (string[5] == '>'));
1068 }
1069
1070 static inline gboolean
1071 is_super (const gchar *string)
1072 {
1073   return ((string[0] == '<') &&
1074           (string[1] == 's' || string[1] == 'S') &&
1075           (string[2] == 'u' || string[2] == 'U') &&
1076           (string[3] == 'p' || string[3] == 'P') &&
1077           (string[4] == 'e' || string[4] == 'E') &&
1078           (string[5] == 'r' || string[5] == 'R') &&
1079           (string[6] == '>'));
1080 }
1081
1082 static inline gboolean
1083 is_hyper (const gchar *string)
1084 {
1085   return ((string[0] == '<') &&
1086           (string[1] == 'h' || string[1] == 'H') &&
1087           (string[2] == 'y' || string[2] == 'Y') &&
1088           (string[3] == 'p' || string[3] == 'P') &&
1089           (string[4] == 'e' || string[4] == 'E') &&
1090           (string[5] == 'r' || string[5] == 'R') &&
1091           (string[6] == '>'));
1092 }
1093
1094 /**
1095  * gtk_accelerator_parse:
1096  * @accelerator:      string representing an accelerator
1097  * @accelerator_key:  return location for accelerator keyval
1098  * @accelerator_mods: return location for accelerator modifier mask
1099  *
1100  * Parses a string representing an accelerator. The
1101  * format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1" or
1102  * "&lt;Release&gt;z" (the last one is for key release).
1103  * The parser is fairly liberal and allows lower or upper case,
1104  * and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
1105  *
1106  * If the parse fails, @accelerator_key and @accelerator_mods will
1107  * be set to 0 (zero).
1108  */
1109 void
1110 gtk_accelerator_parse (const gchar     *accelerator,
1111                        guint           *accelerator_key,
1112                        GdkModifierType *accelerator_mods)
1113 {
1114   guint keyval;
1115   GdkModifierType mods;
1116   gint len;
1117   
1118   if (accelerator_key)
1119     *accelerator_key = 0;
1120   if (accelerator_mods)
1121     *accelerator_mods = 0;
1122   g_return_if_fail (accelerator != NULL);
1123   
1124   keyval = 0;
1125   mods = 0;
1126   len = strlen (accelerator);
1127   while (len)
1128     {
1129       if (*accelerator == '<')
1130         {
1131           if (len >= 9 && is_release (accelerator))
1132             {
1133               accelerator += 9;
1134               len -= 9;
1135               mods |= GDK_RELEASE_MASK;
1136             }
1137           else if (len >= 9 && is_control (accelerator))
1138             {
1139               accelerator += 9;
1140               len -= 9;
1141               mods |= GDK_CONTROL_MASK;
1142             }
1143           else if (len >= 7 && is_shift (accelerator))
1144             {
1145               accelerator += 7;
1146               len -= 7;
1147               mods |= GDK_SHIFT_MASK;
1148             }
1149           else if (len >= 6 && is_shft (accelerator))
1150             {
1151               accelerator += 6;
1152               len -= 6;
1153               mods |= GDK_SHIFT_MASK;
1154             }
1155           else if (len >= 6 && is_ctrl (accelerator))
1156             {
1157               accelerator += 6;
1158               len -= 6;
1159               mods |= GDK_CONTROL_MASK;
1160             }
1161           else if (len >= 6 && is_modx (accelerator))
1162             {
1163               static const guint mod_vals[] = {
1164                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
1165                 GDK_MOD4_MASK, GDK_MOD5_MASK
1166               };
1167
1168               len -= 6;
1169               accelerator += 4;
1170               mods |= mod_vals[*accelerator - '1'];
1171               accelerator += 2;
1172             }
1173           else if (len >= 5 && is_ctl (accelerator))
1174             {
1175               accelerator += 5;
1176               len -= 5;
1177               mods |= GDK_CONTROL_MASK;
1178             }
1179           else if (len >= 5 && is_alt (accelerator))
1180             {
1181               accelerator += 5;
1182               len -= 5;
1183               mods |= GDK_MOD1_MASK;
1184             }
1185           else if (len >= 6 && is_meta (accelerator))
1186             {
1187               accelerator += 6;
1188               len -= 6;
1189               mods |= GDK_META_MASK;
1190             }
1191           else if (len >= 7 && is_hyper (accelerator))
1192             {
1193               accelerator += 7;
1194               len -= 7;
1195               mods |= GDK_HYPER_MASK;
1196             }
1197           else if (len >= 7 && is_super (accelerator))
1198             {
1199               accelerator += 7;
1200               len -= 7;
1201               mods |= GDK_SUPER_MASK;
1202             }
1203           else
1204             {
1205               gchar last_ch;
1206               
1207               last_ch = *accelerator;
1208               while (last_ch && last_ch != '>')
1209                 {
1210                   last_ch = *accelerator;
1211                   accelerator += 1;
1212                   len -= 1;
1213                 }
1214             }
1215         }
1216       else
1217         {
1218           keyval = gdk_keyval_from_name (accelerator);
1219           accelerator += len;
1220           len -= len;
1221         }
1222     }
1223   
1224   if (accelerator_key)
1225     *accelerator_key = gdk_keyval_to_lower (keyval);
1226   if (accelerator_mods)
1227     *accelerator_mods = mods;
1228 }
1229
1230 /**
1231  * gtk_accelerator_name:
1232  * @accelerator_key:  accelerator keyval
1233  * @accelerator_mods: accelerator modifier mask
1234  * 
1235  * Converts an accelerator keyval and modifier mask
1236  * into a string parseable by gtk_accelerator_parse().
1237  * For example, if you pass in #GDK_q and #GDK_CONTROL_MASK,
1238  * this function returns "&lt;Control&gt;q". 
1239  *
1240  * If you need to display accelerators in the user interface,
1241  * see gtk_accelerator_get_label().
1242  *
1243  * Returns: a newly-allocated accelerator name
1244  */
1245 gchar*
1246 gtk_accelerator_name (guint           accelerator_key,
1247                       GdkModifierType accelerator_mods)
1248 {
1249   static const gchar text_release[] = "<Release>";
1250   static const gchar text_shift[] = "<Shift>";
1251   static const gchar text_control[] = "<Control>";
1252   static const gchar text_mod1[] = "<Alt>";
1253   static const gchar text_mod2[] = "<Mod2>";
1254   static const gchar text_mod3[] = "<Mod3>";
1255   static const gchar text_mod4[] = "<Mod4>";
1256   static const gchar text_mod5[] = "<Mod5>";
1257   static const gchar text_meta[] = "<Meta>";
1258   static const gchar text_super[] = "<Super>";
1259   static const gchar text_hyper[] = "<Hyper>";
1260   guint l;
1261   gchar *keyval_name;
1262   gchar *accelerator;
1263
1264   accelerator_mods &= GDK_MODIFIER_MASK;
1265
1266   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
1267   if (!keyval_name)
1268     keyval_name = "";
1269
1270   l = 0;
1271   if (accelerator_mods & GDK_RELEASE_MASK)
1272     l += sizeof (text_release) - 1;
1273   if (accelerator_mods & GDK_SHIFT_MASK)
1274     l += sizeof (text_shift) - 1;
1275   if (accelerator_mods & GDK_CONTROL_MASK)
1276     l += sizeof (text_control) - 1;
1277   if (accelerator_mods & GDK_MOD1_MASK)
1278     l += sizeof (text_mod1) - 1;
1279   if (accelerator_mods & GDK_MOD2_MASK)
1280     l += sizeof (text_mod2) - 1;
1281   if (accelerator_mods & GDK_MOD3_MASK)
1282     l += sizeof (text_mod3) - 1;
1283   if (accelerator_mods & GDK_MOD4_MASK)
1284     l += sizeof (text_mod4) - 1;
1285   if (accelerator_mods & GDK_MOD5_MASK)
1286     l += sizeof (text_mod5) - 1;
1287   l += strlen (keyval_name);
1288   if (accelerator_mods & GDK_META_MASK)
1289     l += sizeof (text_meta) - 1;
1290   if (accelerator_mods & GDK_HYPER_MASK)
1291     l += sizeof (text_hyper) - 1;
1292   if (accelerator_mods & GDK_SUPER_MASK)
1293     l += sizeof (text_super) - 1;
1294
1295   accelerator = g_new (gchar, l + 1);
1296
1297   l = 0;
1298   accelerator[l] = 0;
1299   if (accelerator_mods & GDK_RELEASE_MASK)
1300     {
1301       strcpy (accelerator + l, text_release);
1302       l += sizeof (text_release) - 1;
1303     }
1304   if (accelerator_mods & GDK_SHIFT_MASK)
1305     {
1306       strcpy (accelerator + l, text_shift);
1307       l += sizeof (text_shift) - 1;
1308     }
1309   if (accelerator_mods & GDK_CONTROL_MASK)
1310     {
1311       strcpy (accelerator + l, text_control);
1312       l += sizeof (text_control) - 1;
1313     }
1314   if (accelerator_mods & GDK_MOD1_MASK)
1315     {
1316       strcpy (accelerator + l, text_mod1);
1317       l += sizeof (text_mod1) - 1;
1318     }
1319   if (accelerator_mods & GDK_MOD2_MASK)
1320     {
1321       strcpy (accelerator + l, text_mod2);
1322       l += sizeof (text_mod2) - 1;
1323     }
1324   if (accelerator_mods & GDK_MOD3_MASK)
1325     {
1326       strcpy (accelerator + l, text_mod3);
1327       l += sizeof (text_mod3) - 1;
1328     }
1329   if (accelerator_mods & GDK_MOD4_MASK)
1330     {
1331       strcpy (accelerator + l, text_mod4);
1332       l += sizeof (text_mod4) - 1;
1333     }
1334   if (accelerator_mods & GDK_MOD5_MASK)
1335     {
1336       strcpy (accelerator + l, text_mod5);
1337       l += sizeof (text_mod5) - 1;
1338     }
1339   if (accelerator_mods & GDK_META_MASK)
1340     {
1341       strcpy (accelerator + l, text_meta);
1342       l += sizeof (text_meta) - 1;
1343     }
1344   if (accelerator_mods & GDK_HYPER_MASK)
1345     {
1346       strcpy (accelerator + l, text_hyper);
1347       l += sizeof (text_hyper) - 1;
1348     }
1349   if (accelerator_mods & GDK_SUPER_MASK)
1350     {
1351       strcpy (accelerator + l, text_super);
1352       l += sizeof (text_super) - 1;
1353     }
1354   strcpy (accelerator + l, keyval_name);
1355
1356   return accelerator;
1357 }
1358
1359 /**
1360  * gtk_accelerator_get_label:
1361  * @accelerator_key:  accelerator keyval
1362  * @accelerator_mods: accelerator modifier mask
1363  * 
1364  * Converts an accelerator keyval and modifier mask into a string 
1365  * which can be used to represent the accelerator to the user. 
1366  *
1367  * Returns: a newly-allocated string representing the accelerator.
1368  *
1369  * Since: 2.6
1370  */
1371 gchar*
1372 gtk_accelerator_get_label (guint           accelerator_key,
1373                            GdkModifierType accelerator_mods)
1374 {
1375   GtkAccelLabelClass *klass;
1376   gchar *label;
1377
1378   klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
1379   label = _gtk_accel_label_class_get_accelerator_label (klass, 
1380                                                         accelerator_key, 
1381                                                         accelerator_mods);
1382   g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
1383
1384   return label;
1385 }  
1386
1387 /**
1388  * gtk_accelerator_set_default_mod_mask:
1389  * @default_mod_mask: accelerator modifier mask
1390  *
1391  * Sets the modifiers that will be considered significant for keyboard
1392  * accelerators. The default mod mask is #GDK_CONTROL_MASK |
1393  * #GDK_SHIFT_MASK | #GDK_MOD1_MASK | #GDK_SUPER_MASK | 
1394  * #GDK_HYPER_MASK | #GDK_META_MASK, that is, Control, Shift, Alt, 
1395  * Super, Hyper and Meta. Other modifiers will by default be ignored 
1396  * by #GtkAccelGroup.
1397  * You must include at least the three modifiers Control, Shift
1398  * and Alt in any value you pass to this function.
1399  *
1400  * The default mod mask should be changed on application startup,
1401  * before using any accelerator groups.
1402  */
1403 void
1404 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1405 {
1406   default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) |
1407     (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
1408 }
1409
1410 /**
1411  * gtk_accelerator_get_default_mod_mask:
1412  * @returns: the default accelerator modifier mask
1413  *
1414  * Gets the value set by gtk_accelerator_set_default_mod_mask().
1415  */
1416 guint
1417 gtk_accelerator_get_default_mod_mask (void)
1418 {
1419   return default_accel_mod_mask;
1420 }
1421
1422 #define __GTK_ACCEL_GROUP_C__
1423 #include "gtkaliasdef.c"