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