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