]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelgroup.c
Bug 535608 – do not string-copy accel paths in the menu code
[~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                     guint               pos)
401 {
402   GQuark accel_quark = 0;
403   GtkAccelGroupEntry *entry = accel_group->priv_accels + pos;
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  * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
530  * pass a static string, you can save some memory by interning it first with 
531  * g_intern_static_string().
532  */
533 void
534 gtk_accel_group_connect_by_path (GtkAccelGroup  *accel_group,
535                                  const gchar    *accel_path,
536                                  GClosure       *closure)
537 {
538   guint accel_key = 0;
539   GdkModifierType accel_mods = 0;
540   GtkAccelKey key;
541
542   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
543   g_return_if_fail (closure != NULL);
544   g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
545
546   if (closure->is_invalid)
547     return;
548
549   g_object_ref (accel_group);
550
551   if (gtk_accel_map_lookup_entry (accel_path, &key))
552     {
553       accel_key = gdk_keyval_to_lower (key.accel_key);
554       accel_mods = key.accel_mods;
555     }
556
557   quick_accel_add (accel_group, accel_key, accel_mods, GTK_ACCEL_VISIBLE, closure,
558                    g_quark_from_string (accel_path));
559
560   g_object_unref (accel_group);
561 }
562
563 /**
564  * gtk_accel_group_disconnect:
565  * @accel_group: the accelerator group to remove an accelerator from
566  * @closure:     the closure to remove from this accelerator group
567  * @returns:     %TRUE if the closure was found and got disconnected
568  *
569  * Removes an accelerator previously installed through
570  * gtk_accel_group_connect().
571  */
572 gboolean
573 gtk_accel_group_disconnect (GtkAccelGroup *accel_group,
574                             GClosure      *closure)
575 {
576   guint i;
577
578   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
579
580   for (i = 0; i < accel_group->n_accels; i++)
581     if (accel_group->priv_accels[i].closure == closure)
582       {
583         g_object_ref (accel_group);
584         quick_accel_remove (accel_group, i);
585         g_object_unref (accel_group);
586         return TRUE;
587       }
588   return FALSE;
589 }
590
591 /**
592  * gtk_accel_group_disconnect_key:
593  * @accel_group:      the accelerator group to install an accelerator in
594  * @accel_key:        key value of the accelerator
595  * @accel_mods:       modifier combination of the accelerator
596  * @returns:          %TRUE if there was an accelerator which could be 
597  *                    removed, %FALSE otherwise
598  *
599  * Removes an accelerator previously installed through
600  * gtk_accel_group_connect().
601  */
602 gboolean
603 gtk_accel_group_disconnect_key (GtkAccelGroup  *accel_group,
604                                 guint           accel_key,
605                                 GdkModifierType accel_mods)
606 {
607   GtkAccelGroupEntry *entries;
608   GSList *slist, *clist = NULL;
609   gboolean removed_one = FALSE;
610   guint n;
611
612   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
613
614   g_object_ref (accel_group);
615   
616   accel_key = gdk_keyval_to_lower (accel_key);
617   entries = quick_accel_find (accel_group, accel_key, accel_mods, &n);
618   while (n--)
619     {
620       GClosure *closure = g_closure_ref (entries[n].closure);
621
622       clist = g_slist_prepend (clist, closure);
623     }
624
625   for (slist = clist; slist; slist = slist->next)
626     {
627       GClosure *closure = slist->data;
628
629       removed_one |= gtk_accel_group_disconnect (accel_group, closure);
630       g_closure_unref (closure);
631     }
632   g_slist_free (clist);
633
634   g_object_unref (accel_group);
635
636   return removed_one;
637 }
638
639 void
640 _gtk_accel_group_reconnect (GtkAccelGroup *accel_group,
641                             GQuark         accel_path_quark)
642 {
643   GSList *slist, *clist = NULL;
644   guint i;
645
646   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
647
648   g_object_ref (accel_group);
649
650   for (i = 0; i < accel_group->n_accels; i++)
651     if (accel_group->priv_accels[i].accel_path_quark == accel_path_quark)
652       {
653         GClosure *closure = g_closure_ref (accel_group->priv_accels[i].closure);
654
655         clist = g_slist_prepend (clist, closure);
656       }
657
658   for (slist = clist; slist; slist = slist->next)
659     {
660       GClosure *closure = slist->data;
661
662       gtk_accel_group_disconnect (accel_group, closure);
663       gtk_accel_group_connect_by_path (accel_group, g_quark_to_string (accel_path_quark), closure);
664       g_closure_unref (closure);
665     }
666   g_slist_free (clist);
667
668   g_object_unref (accel_group);
669 }
670
671 /**
672  * gtk_accel_group_query:
673  * @accel_group:      the accelerator group to query
674  * @accel_key:        key value of the accelerator
675  * @accel_mods:       modifier combination of the accelerator
676  * @n_entries:        location to return the number of entries found, or %NULL
677  * @returns:          an array of @n_entries #GtkAccelGroupEntry elements, or %NULL. The array is owned by GTK+ and must not be freed. 
678  *
679  * Queries an accelerator group for all entries matching @accel_key and 
680  * @accel_mods.
681  */
682 GtkAccelGroupEntry*
683 gtk_accel_group_query (GtkAccelGroup  *accel_group,
684                        guint           accel_key,
685                        GdkModifierType accel_mods,
686                        guint          *n_entries)
687 {
688   GtkAccelGroupEntry *entries;
689   guint n;
690
691   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), NULL);
692
693   entries = quick_accel_find (accel_group, gdk_keyval_to_lower (accel_key), accel_mods, &n);
694
695   if (n_entries)
696     *n_entries = entries ? n : 0;
697
698   return entries;
699 }
700
701 /**
702  * gtk_accel_group_from_accel_closure:
703  * @closure: a #GClosure
704  * @returns: the #GtkAccelGroup to which @closure is connected, or %NULL.
705  *
706  * Finds the #GtkAccelGroup to which @closure is connected; 
707  * see gtk_accel_group_connect().
708  */
709 GtkAccelGroup*
710 gtk_accel_group_from_accel_closure (GClosure *closure)
711 {
712   guint i;
713
714   g_return_val_if_fail (closure != NULL, NULL);
715
716   /* a few remarks on what we do here. in general, we need a way to reverse lookup
717    * accel_groups from closures that are being used in accel groups. this could
718    * be done e.g via a hashtable. it is however cheaper (memory wise) to just
719    * use the invalidation notifier on the closure itself (which we need to install
720    * anyway), that contains the accel group as data which, besides needing to peek
721    * a bit at closure internals, works just as good.
722    */
723   for (i = 0; i < G_CLOSURE_N_NOTIFIERS (closure); i++)
724     if (closure->notifiers[i].notify == accel_closure_invalidate)
725       return closure->notifiers[i].data;
726
727   return NULL;
728 }
729
730 gboolean
731 gtk_accel_group_activate (GtkAccelGroup   *accel_group,
732                           GQuark           accel_quark,
733                           GObject         *acceleratable,
734                           guint            accel_key,
735                           GdkModifierType  accel_mods)
736 {
737   gboolean was_handled;
738
739   g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE);
740   g_return_val_if_fail (G_IS_OBJECT (acceleratable), FALSE);
741   
742   was_handled = FALSE;
743   g_signal_emit (accel_group, signal_accel_activate, accel_quark,
744                  acceleratable, accel_key, accel_mods, &was_handled);
745
746   return was_handled;
747 }
748
749 /**
750  * gtk_accel_groups_activate:
751  * @object:        the #GObject, usually a #GtkWindow, on which
752  *                 to activate the accelerator.
753  * @accel_key:     accelerator keyval from a key event
754  * @accel_mods:    keyboard state mask from a key event
755  * @returns:       %TRUE if the accelerator was handled, %FALSE otherwise
756  * 
757  * Finds the first accelerator in any #GtkAccelGroup attached
758  * to @object that matches @accel_key and @accel_mods, and
759  * activates that accelerator.
760  * If an accelerator was activated and handled this keypress, %TRUE
761  * is returned.
762  */
763 gboolean
764 gtk_accel_groups_activate (GObject        *object,
765                            guint           accel_key,
766                            GdkModifierType accel_mods)
767 {
768   g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
769   
770   if (gtk_accelerator_valid (accel_key, accel_mods))
771     {
772       gchar *accel_name;
773       GQuark accel_quark;
774       GSList *slist;
775
776       accel_name = gtk_accelerator_name (accel_key, (accel_mods & gtk_accelerator_get_default_mod_mask ()));
777       accel_quark = g_quark_from_string (accel_name);
778       g_free (accel_name);
779       
780       for (slist = gtk_accel_groups_from_object (object); slist; slist = slist->next)
781         if (gtk_accel_group_activate (slist->data, accel_quark, object, accel_key, accel_mods))
782           return TRUE;
783     }
784   
785   return FALSE;
786 }
787
788 /**
789  * gtk_accelerator_valid:
790  * @keyval:    a GDK keyval
791  * @modifiers: modifier mask
792  * @returns:   %TRUE if the accelerator is valid
793  * 
794  * Determines whether a given keyval and modifier mask constitute
795  * a valid keyboard accelerator. For example, the #GDK_a keyval
796  * plus #GDK_CONTROL_MASK is valid - this is a "Ctrl+a" accelerator.
797  * But, you can't, for instance, use the #GDK_Control_L keyval
798  * as an accelerator.
799  */
800 gboolean
801 gtk_accelerator_valid (guint              keyval,
802                        GdkModifierType    modifiers)
803 {
804   static const guint invalid_accelerator_vals[] = {
805     GDK_Shift_L, GDK_Shift_R, GDK_Shift_Lock, GDK_Caps_Lock, GDK_ISO_Lock,
806     GDK_Control_L, GDK_Control_R, GDK_Meta_L, GDK_Meta_R,
807     GDK_Alt_L, GDK_Alt_R, GDK_Super_L, GDK_Super_R, GDK_Hyper_L, GDK_Hyper_R,
808     GDK_ISO_Level3_Shift, GDK_ISO_Next_Group, GDK_ISO_Prev_Group,
809     GDK_ISO_First_Group, GDK_ISO_Last_Group,
810     GDK_Mode_switch, GDK_Num_Lock, GDK_Multi_key,
811     GDK_Scroll_Lock, GDK_Sys_Req, 
812     GDK_Tab, GDK_ISO_Left_Tab, GDK_KP_Tab,
813     GDK_First_Virtual_Screen, GDK_Prev_Virtual_Screen,
814     GDK_Next_Virtual_Screen, GDK_Last_Virtual_Screen,
815     GDK_Terminate_Server, GDK_AudibleBell_Enable,
816     0
817   };
818   static const guint invalid_unmodified_vals[] = {
819     GDK_Up, GDK_Down, GDK_Left, GDK_Right,
820     GDK_KP_Up, GDK_KP_Down, GDK_KP_Left, GDK_KP_Right,
821     0
822   };
823   const guint *ac_val;
824
825   modifiers &= GDK_MODIFIER_MASK;
826     
827   if (keyval <= 0xFF)
828     return keyval >= 0x20;
829
830   ac_val = invalid_accelerator_vals;
831   while (*ac_val)
832     {
833       if (keyval == *ac_val++)
834         return FALSE;
835     }
836
837   if (!modifiers)
838     {
839       ac_val = invalid_unmodified_vals;
840       while (*ac_val)
841         {
842           if (keyval == *ac_val++)
843             return FALSE;
844         }
845     }
846   
847   return TRUE;
848 }
849
850 static inline gboolean
851 is_alt (const gchar *string)
852 {
853   return ((string[0] == '<') &&
854           (string[1] == 'a' || string[1] == 'A') &&
855           (string[2] == 'l' || string[2] == 'L') &&
856           (string[3] == 't' || string[3] == 'T') &&
857           (string[4] == '>'));
858 }
859
860 static inline gboolean
861 is_ctl (const gchar *string)
862 {
863   return ((string[0] == '<') &&
864           (string[1] == 'c' || string[1] == 'C') &&
865           (string[2] == 't' || string[2] == 'T') &&
866           (string[3] == 'l' || string[3] == 'L') &&
867           (string[4] == '>'));
868 }
869
870 static inline gboolean
871 is_modx (const gchar *string)
872 {
873   return ((string[0] == '<') &&
874           (string[1] == 'm' || string[1] == 'M') &&
875           (string[2] == 'o' || string[2] == 'O') &&
876           (string[3] == 'd' || string[3] == 'D') &&
877           (string[4] >= '1' && string[4] <= '5') &&
878           (string[5] == '>'));
879 }
880
881 static inline gboolean
882 is_ctrl (const gchar *string)
883 {
884   return ((string[0] == '<') &&
885           (string[1] == 'c' || string[1] == 'C') &&
886           (string[2] == 't' || string[2] == 'T') &&
887           (string[3] == 'r' || string[3] == 'R') &&
888           (string[4] == 'l' || string[4] == 'L') &&
889           (string[5] == '>'));
890 }
891
892 static inline gboolean
893 is_shft (const gchar *string)
894 {
895   return ((string[0] == '<') &&
896           (string[1] == 's' || string[1] == 'S') &&
897           (string[2] == 'h' || string[2] == 'H') &&
898           (string[3] == 'f' || string[3] == 'F') &&
899           (string[4] == 't' || string[4] == 'T') &&
900           (string[5] == '>'));
901 }
902
903 static inline gboolean
904 is_shift (const gchar *string)
905 {
906   return ((string[0] == '<') &&
907           (string[1] == 's' || string[1] == 'S') &&
908           (string[2] == 'h' || string[2] == 'H') &&
909           (string[3] == 'i' || string[3] == 'I') &&
910           (string[4] == 'f' || string[4] == 'F') &&
911           (string[5] == 't' || string[5] == 'T') &&
912           (string[6] == '>'));
913 }
914
915 static inline gboolean
916 is_control (const gchar *string)
917 {
918   return ((string[0] == '<') &&
919           (string[1] == 'c' || string[1] == 'C') &&
920           (string[2] == 'o' || string[2] == 'O') &&
921           (string[3] == 'n' || string[3] == 'N') &&
922           (string[4] == 't' || string[4] == 'T') &&
923           (string[5] == 'r' || string[5] == 'R') &&
924           (string[6] == 'o' || string[6] == 'O') &&
925           (string[7] == 'l' || string[7] == 'L') &&
926           (string[8] == '>'));
927 }
928
929 static inline gboolean
930 is_release (const gchar *string)
931 {
932   return ((string[0] == '<') &&
933           (string[1] == 'r' || string[1] == 'R') &&
934           (string[2] == 'e' || string[2] == 'E') &&
935           (string[3] == 'l' || string[3] == 'L') &&
936           (string[4] == 'e' || string[4] == 'E') &&
937           (string[5] == 'a' || string[5] == 'A') &&
938           (string[6] == 's' || string[6] == 'S') &&
939           (string[7] == 'e' || string[7] == 'E') &&
940           (string[8] == '>'));
941 }
942
943 static inline gboolean
944 is_meta (const gchar *string)
945 {
946   return ((string[0] == '<') &&
947           (string[1] == 'm' || string[1] == 'M') &&
948           (string[2] == 'e' || string[2] == 'E') &&
949           (string[3] == 't' || string[3] == 'T') &&
950           (string[4] == 'a' || string[4] == 'A') &&
951           (string[5] == '>'));
952 }
953
954 static inline gboolean
955 is_super (const gchar *string)
956 {
957   return ((string[0] == '<') &&
958           (string[1] == 's' || string[1] == 'S') &&
959           (string[2] == 'u' || string[2] == 'U') &&
960           (string[3] == 'p' || string[3] == 'P') &&
961           (string[4] == 'e' || string[4] == 'E') &&
962           (string[5] == 'r' || string[5] == 'R') &&
963           (string[6] == '>'));
964 }
965
966 static inline gboolean
967 is_hyper (const gchar *string)
968 {
969   return ((string[0] == '<') &&
970           (string[1] == 'h' || string[1] == 'H') &&
971           (string[2] == 'y' || string[2] == 'Y') &&
972           (string[3] == 'p' || string[3] == 'P') &&
973           (string[4] == 'e' || string[4] == 'E') &&
974           (string[5] == 'r' || string[5] == 'R') &&
975           (string[6] == '>'));
976 }
977
978 /**
979  * gtk_accelerator_parse:
980  * @accelerator:      string representing an accelerator
981  * @accelerator_key:  return location for accelerator keyval
982  * @accelerator_mods: return location for accelerator modifier mask
983  *
984  * Parses a string representing an accelerator. The
985  * format looks like "&lt;Control&gt;a" or "&lt;Shift&gt;&lt;Alt&gt;F1" or
986  * "&lt;Release&gt;z" (the last one is for key release).
987  * The parser is fairly liberal and allows lower or upper case,
988  * and also abbreviations such as "&lt;Ctl&gt;" and "&lt;Ctrl&gt;".
989  *
990  * If the parse fails, @accelerator_key and @accelerator_mods will
991  * be set to 0 (zero).
992  */
993 void
994 gtk_accelerator_parse (const gchar     *accelerator,
995                        guint           *accelerator_key,
996                        GdkModifierType *accelerator_mods)
997 {
998   guint keyval;
999   GdkModifierType mods;
1000   gint len;
1001   
1002   if (accelerator_key)
1003     *accelerator_key = 0;
1004   if (accelerator_mods)
1005     *accelerator_mods = 0;
1006   g_return_if_fail (accelerator != NULL);
1007   
1008   keyval = 0;
1009   mods = 0;
1010   len = strlen (accelerator);
1011   while (len)
1012     {
1013       if (*accelerator == '<')
1014         {
1015           if (len >= 9 && is_release (accelerator))
1016             {
1017               accelerator += 9;
1018               len -= 9;
1019               mods |= GDK_RELEASE_MASK;
1020             }
1021           else if (len >= 9 && is_control (accelerator))
1022             {
1023               accelerator += 9;
1024               len -= 9;
1025               mods |= GDK_CONTROL_MASK;
1026             }
1027           else if (len >= 7 && is_shift (accelerator))
1028             {
1029               accelerator += 7;
1030               len -= 7;
1031               mods |= GDK_SHIFT_MASK;
1032             }
1033           else if (len >= 6 && is_shft (accelerator))
1034             {
1035               accelerator += 6;
1036               len -= 6;
1037               mods |= GDK_SHIFT_MASK;
1038             }
1039           else if (len >= 6 && is_ctrl (accelerator))
1040             {
1041               accelerator += 6;
1042               len -= 6;
1043               mods |= GDK_CONTROL_MASK;
1044             }
1045           else if (len >= 6 && is_modx (accelerator))
1046             {
1047               static const guint mod_vals[] = {
1048                 GDK_MOD1_MASK, GDK_MOD2_MASK, GDK_MOD3_MASK,
1049                 GDK_MOD4_MASK, GDK_MOD5_MASK
1050               };
1051
1052               len -= 6;
1053               accelerator += 4;
1054               mods |= mod_vals[*accelerator - '1'];
1055               accelerator += 2;
1056             }
1057           else if (len >= 5 && is_ctl (accelerator))
1058             {
1059               accelerator += 5;
1060               len -= 5;
1061               mods |= GDK_CONTROL_MASK;
1062             }
1063           else if (len >= 5 && is_alt (accelerator))
1064             {
1065               accelerator += 5;
1066               len -= 5;
1067               mods |= GDK_MOD1_MASK;
1068             }
1069           else if (len >= 6 && is_meta (accelerator))
1070             {
1071               accelerator += 6;
1072               len -= 6;
1073               mods |= GDK_META_MASK;
1074             }
1075           else if (len >= 7 && is_hyper (accelerator))
1076             {
1077               accelerator += 7;
1078               len -= 7;
1079               mods |= GDK_HYPER_MASK;
1080             }
1081           else if (len >= 7 && is_super (accelerator))
1082             {
1083               accelerator += 7;
1084               len -= 7;
1085               mods |= GDK_SUPER_MASK;
1086             }
1087           else
1088             {
1089               gchar last_ch;
1090               
1091               last_ch = *accelerator;
1092               while (last_ch && last_ch != '>')
1093                 {
1094                   last_ch = *accelerator;
1095                   accelerator += 1;
1096                   len -= 1;
1097                 }
1098             }
1099         }
1100       else
1101         {
1102           keyval = gdk_keyval_from_name (accelerator);
1103           accelerator += len;
1104           len -= len;
1105         }
1106     }
1107   
1108   if (accelerator_key)
1109     *accelerator_key = gdk_keyval_to_lower (keyval);
1110   if (accelerator_mods)
1111     *accelerator_mods = mods;
1112 }
1113
1114 /**
1115  * gtk_accelerator_name:
1116  * @accelerator_key:  accelerator keyval
1117  * @accelerator_mods: accelerator modifier mask
1118  * 
1119  * Converts an accelerator keyval and modifier mask
1120  * into a string parseable by gtk_accelerator_parse().
1121  * For example, if you pass in #GDK_q and #GDK_CONTROL_MASK,
1122  * this function returns "&lt;Control&gt;q". 
1123  *
1124  * If you need to display accelerators in the user interface,
1125  * see gtk_accelerator_get_label().
1126  *
1127  * Returns: a newly-allocated accelerator name
1128  */
1129 gchar*
1130 gtk_accelerator_name (guint           accelerator_key,
1131                       GdkModifierType accelerator_mods)
1132 {
1133   static const gchar text_release[] = "<Release>";
1134   static const gchar text_shift[] = "<Shift>";
1135   static const gchar text_control[] = "<Control>";
1136   static const gchar text_mod1[] = "<Alt>";
1137   static const gchar text_mod2[] = "<Mod2>";
1138   static const gchar text_mod3[] = "<Mod3>";
1139   static const gchar text_mod4[] = "<Mod4>";
1140   static const gchar text_mod5[] = "<Mod5>";
1141   static const gchar text_meta[] = "<Meta>";
1142   static const gchar text_super[] = "<Super>";
1143   static const gchar text_hyper[] = "<Hyper>";
1144   guint l;
1145   gchar *keyval_name;
1146   gchar *accelerator;
1147
1148   accelerator_mods &= GDK_MODIFIER_MASK;
1149
1150   keyval_name = gdk_keyval_name (gdk_keyval_to_lower (accelerator_key));
1151   if (!keyval_name)
1152     keyval_name = "";
1153
1154   l = 0;
1155   if (accelerator_mods & GDK_RELEASE_MASK)
1156     l += sizeof (text_release) - 1;
1157   if (accelerator_mods & GDK_SHIFT_MASK)
1158     l += sizeof (text_shift) - 1;
1159   if (accelerator_mods & GDK_CONTROL_MASK)
1160     l += sizeof (text_control) - 1;
1161   if (accelerator_mods & GDK_MOD1_MASK)
1162     l += sizeof (text_mod1) - 1;
1163   if (accelerator_mods & GDK_MOD2_MASK)
1164     l += sizeof (text_mod2) - 1;
1165   if (accelerator_mods & GDK_MOD3_MASK)
1166     l += sizeof (text_mod3) - 1;
1167   if (accelerator_mods & GDK_MOD4_MASK)
1168     l += sizeof (text_mod4) - 1;
1169   if (accelerator_mods & GDK_MOD5_MASK)
1170     l += sizeof (text_mod5) - 1;
1171   l += strlen (keyval_name);
1172   if (accelerator_mods & GDK_META_MASK)
1173     l += sizeof (text_meta) - 1;
1174   if (accelerator_mods & GDK_HYPER_MASK)
1175     l += sizeof (text_hyper) - 1;
1176   if (accelerator_mods & GDK_SUPER_MASK)
1177     l += sizeof (text_super) - 1;
1178
1179   accelerator = g_new (gchar, l + 1);
1180
1181   l = 0;
1182   accelerator[l] = 0;
1183   if (accelerator_mods & GDK_RELEASE_MASK)
1184     {
1185       strcpy (accelerator + l, text_release);
1186       l += sizeof (text_release) - 1;
1187     }
1188   if (accelerator_mods & GDK_SHIFT_MASK)
1189     {
1190       strcpy (accelerator + l, text_shift);
1191       l += sizeof (text_shift) - 1;
1192     }
1193   if (accelerator_mods & GDK_CONTROL_MASK)
1194     {
1195       strcpy (accelerator + l, text_control);
1196       l += sizeof (text_control) - 1;
1197     }
1198   if (accelerator_mods & GDK_MOD1_MASK)
1199     {
1200       strcpy (accelerator + l, text_mod1);
1201       l += sizeof (text_mod1) - 1;
1202     }
1203   if (accelerator_mods & GDK_MOD2_MASK)
1204     {
1205       strcpy (accelerator + l, text_mod2);
1206       l += sizeof (text_mod2) - 1;
1207     }
1208   if (accelerator_mods & GDK_MOD3_MASK)
1209     {
1210       strcpy (accelerator + l, text_mod3);
1211       l += sizeof (text_mod3) - 1;
1212     }
1213   if (accelerator_mods & GDK_MOD4_MASK)
1214     {
1215       strcpy (accelerator + l, text_mod4);
1216       l += sizeof (text_mod4) - 1;
1217     }
1218   if (accelerator_mods & GDK_MOD5_MASK)
1219     {
1220       strcpy (accelerator + l, text_mod5);
1221       l += sizeof (text_mod5) - 1;
1222     }
1223   if (accelerator_mods & GDK_META_MASK)
1224     {
1225       strcpy (accelerator + l, text_meta);
1226       l += sizeof (text_meta) - 1;
1227     }
1228   if (accelerator_mods & GDK_HYPER_MASK)
1229     {
1230       strcpy (accelerator + l, text_hyper);
1231       l += sizeof (text_hyper) - 1;
1232     }
1233   if (accelerator_mods & GDK_SUPER_MASK)
1234     {
1235       strcpy (accelerator + l, text_super);
1236       l += sizeof (text_super) - 1;
1237     }
1238   strcpy (accelerator + l, keyval_name);
1239
1240   return accelerator;
1241 }
1242
1243 /**
1244  * gtk_accelerator_get_label:
1245  * @accelerator_key:  accelerator keyval
1246  * @accelerator_mods: accelerator modifier mask
1247  * 
1248  * Converts an accelerator keyval and modifier mask into a string 
1249  * which can be used to represent the accelerator to the user. 
1250  *
1251  * Returns: a newly-allocated string representing the accelerator.
1252  *
1253  * Since: 2.6
1254  */
1255 gchar*
1256 gtk_accelerator_get_label (guint           accelerator_key,
1257                            GdkModifierType accelerator_mods)
1258 {
1259   GtkAccelLabelClass *klass;
1260   gchar *label;
1261
1262   klass = g_type_class_ref (GTK_TYPE_ACCEL_LABEL);
1263   label = _gtk_accel_label_class_get_accelerator_label (klass, 
1264                                                         accelerator_key, 
1265                                                         accelerator_mods);
1266   g_type_class_unref (klass); /* klass is kept alive since gtk uses static types */
1267
1268   return label;
1269 }  
1270
1271 /**
1272  * gtk_accelerator_set_default_mod_mask:
1273  * @default_mod_mask: accelerator modifier mask
1274  *
1275  * Sets the modifiers that will be considered significant for keyboard
1276  * accelerators. The default mod mask is #GDK_CONTROL_MASK |
1277  * #GDK_SHIFT_MASK | #GDK_MOD1_MASK | #GDK_SUPER_MASK | 
1278  * #GDK_HYPER_MASK | #GDK_META_MASK, that is, Control, Shift, Alt, 
1279  * Super, Hyper and Meta. Other modifiers will by default be ignored 
1280  * by #GtkAccelGroup.
1281  * You must include at least the three modifiers Control, Shift
1282  * and Alt in any value you pass to this function.
1283  *
1284  * The default mod mask should be changed on application startup,
1285  * before using any accelerator groups.
1286  */
1287 void
1288 gtk_accelerator_set_default_mod_mask (GdkModifierType default_mod_mask)
1289 {
1290   default_accel_mod_mask = (default_mod_mask & GDK_MODIFIER_MASK) |
1291     (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
1292 }
1293
1294 /**
1295  * gtk_accelerator_get_default_mod_mask:
1296  * @returns: the default accelerator modifier mask
1297  *
1298  * Gets the value set by gtk_accelerator_set_default_mod_mask().
1299  */
1300 guint
1301 gtk_accelerator_get_default_mod_mask (void)
1302 {
1303   return default_accel_mod_mask;
1304 }
1305
1306 #define __GTK_ACCEL_GROUP_C__
1307 #include "gtkaliasdef.c"