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