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