1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1998, 2001 Tim Janik
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.
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.
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.
22 #include "gtkaccelmap.h"
24 #include "gtkmarshalers.h"
25 #include "gtkwindowprivate.h"
28 #include <glib/gstdio.h>
43 * @Short_description: Loadable keyboard accelerator specifications
44 * @Title: Accelerator Maps
45 * @See_also: #GtkAccelGroup, #GtkAccelKey, #GtkUIManager, gtk_widget_set_accel_path(), gtk_menu_item_set_accel_path(), #GtkSettings:gtk-can-change-accels
47 * Accelerator maps are used to define runtime configurable accelerators.
48 * Functions for manipulating them are are usually used by higher level
49 * convenience mechanisms like #GtkUIManager and are thus considered
50 * "low-level". You'll want to use them if you're manually creating menus that
51 * should have user-configurable accelerators.
53 * Accelerator is uniquely defined by:
56 * <listitem><para>accelerator path</para></listitem>
57 * <listitem><para>accelerator key</para></listitem>
58 * <listitem><para>accelerator modifiers</para></listitem>
61 * The accelerator path must consist of
62 * "<WINDOWTYPE>/Category1/Category2/.../Action", where WINDOWTYPE
63 * should be a unique application-specific identifier that corresponds to the
64 * kind of window the accelerator is being used in, e.g. "Gimp-Image",
65 * "Abiword-Document" or "Gnumeric-Settings".
66 * The "Category1/.../Action" portion is most appropriately chosen by the action
67 * the accelerator triggers, i.e. for accelerators on menu items, choose the
68 * item's menu path, e.g. "File/Save As", "Image/View/Zoom" or
69 * "Edit/Select All". So a full valid accelerator path may look like:
70 * "<Gimp-Toolbox>/File/Dialogs/Tool Options...".
72 * All accelerators are stored inside one global #GtkAccelMap that can be
73 * obtained using gtk_accel_map_get(). See <link
74 * linkend="monitoring-changes">Monitoring changes</link> for additional
77 * <refsect2 id="manipulating-accelerators">
78 * <title>Manipulating accelerators</title>
80 * New accelerators can be added using gtk_accel_map_add_entry(). To search for
81 * specific accelerator, use gtk_accel_map_lookup_entry(). Modifications of
82 * existing accelerators should be done using gtk_accel_map_change_entry().
84 * In order to avoid having some accelerators changed, they can be locked using
85 * gtk_accel_map_lock_path(). Unlocking is done using
86 * gtk_accel_map_unlock_path().
89 * <refsect2 id="saving-and-loading">
90 * <title>Saving and loading accelerator maps</title>
92 * Accelerator maps can be saved to and loaded from some external resource. For
93 * simple saving and loading from file, gtk_accel_map_save() and
94 * gtk_accel_map_load() are provided. Saving and loading can also be done by
95 * providing file descriptor to gtk_accel_map_save_fd() and
96 * gtk_accel_map_load_fd().
99 * <refsect2 id="monitoring-changes">
100 * <title>Monitoring changes</title>
102 * #GtkAccelMap object is only useful for monitoring changes of accelerators. By
103 * connecting to #GtkAccelMap::changed signal, one can monitor changes of all
104 * accelerators. It is also possible to monitor only single accelerator path by
105 * using it as a detail of the #GtkAccelMap::changed signal.
111 /* --- structures --- */
114 GObject parent_instance;
117 struct _GtkAccelMapClass
119 GObjectClass parent_class;
123 const gchar *accel_path;
127 guint std_accel_mods;
129 guint lock_count : 15;
133 /* --- signals --- */
139 /* --- variables --- */
141 static GHashTable *accel_entry_ht = NULL; /* accel_path -> AccelEntry */
142 static GSList *accel_filters = NULL;
143 static gulong accel_map_signals[LAST_SIGNAL] = { 0, };
144 static GtkAccelMap *accel_map;
146 /* --- prototypes --- */
147 static void do_accel_map_changed (AccelEntry *entry);
149 /* --- functions --- */
151 accel_entry_hash (gconstpointer key)
153 const AccelEntry *entry = key;
155 return g_str_hash (entry->accel_path);
159 accel_entry_equal (gconstpointer key1,
162 const AccelEntry *entry1 = key1;
163 const AccelEntry *entry2 = key2;
165 return g_str_equal (entry1->accel_path, entry2->accel_path);
168 static inline AccelEntry*
169 accel_path_lookup (const gchar *accel_path)
173 ekey.accel_path = accel_path;
175 /* safety NULL check for return_if_fail()s */
176 return accel_path ? g_hash_table_lookup (accel_entry_ht, &ekey) : NULL;
180 _gtk_accel_map_init (void)
182 g_assert (accel_entry_ht == NULL);
184 accel_entry_ht = g_hash_table_new (accel_entry_hash, accel_entry_equal);
188 _gtk_accel_path_is_valid (const gchar *accel_path)
192 if (!accel_path || accel_path[0] != '<' ||
193 accel_path[1] == '<' || accel_path[1] == '>' || !accel_path[1])
195 p = strchr (accel_path, '>');
196 if (!p || (p[1] != 0 && p[1] != '/'))
202 * gtk_accel_map_add_entry:
203 * @accel_path: valid accelerator path
204 * @accel_key: the accelerator key
205 * @accel_mods: the accelerator modifiers
207 * Registers a new accelerator with the global accelerator map.
208 * This function should only be called once per @accel_path
209 * with the canonical @accel_key and @accel_mods for this path.
210 * To change the accelerator during runtime programatically, use
211 * gtk_accel_map_change_entry().
213 * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
214 * pass a static string, you can save some memory by interning it first with
215 * g_intern_static_string().
218 gtk_accel_map_add_entry (const gchar *accel_path,
220 GdkModifierType accel_mods)
224 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
229 accel_mods &= gtk_accelerator_get_default_mod_mask ();
231 entry = accel_path_lookup (accel_path);
234 if (!entry->std_accel_key && !entry->std_accel_mods &&
235 (accel_key || accel_mods))
237 entry->std_accel_key = accel_key;
238 entry->std_accel_mods = accel_mods;
240 gtk_accel_map_change_entry (entry->accel_path, accel_key, accel_mods, TRUE);
245 entry = g_slice_new0 (AccelEntry);
246 entry->accel_path = g_intern_string (accel_path);
247 entry->std_accel_key = accel_key;
248 entry->std_accel_mods = accel_mods;
249 entry->accel_key = accel_key;
250 entry->accel_mods = accel_mods;
251 entry->changed = FALSE;
252 entry->lock_count = 0;
253 g_hash_table_insert (accel_entry_ht, entry, entry);
255 do_accel_map_changed (entry);
260 * gtk_accel_map_lookup_entry:
261 * @accel_path: a valid accelerator path
262 * @key: (allow-none) (out): the accelerator key to be filled in (optional)
263 * @returns: %TRUE if @accel_path is known, %FALSE otherwise
265 * Looks up the accelerator entry for @accel_path and fills in @key.
268 gtk_accel_map_lookup_entry (const gchar *accel_path,
273 g_return_val_if_fail (_gtk_accel_path_is_valid (accel_path), FALSE);
275 entry = accel_path_lookup (accel_path);
278 key->accel_key = entry->accel_key;
279 key->accel_mods = entry->accel_mods;
280 key->accel_flags = 0;
283 return entry ? TRUE : FALSE;
287 hash2slist_foreach (gpointer key,
291 GSList **slist_p = user_data;
293 *slist_p = g_slist_prepend (*slist_p, value);
297 g_hash_table_slist_values (GHashTable *hash_table)
299 GSList *slist = NULL;
301 g_return_val_if_fail (hash_table != NULL, NULL);
303 g_hash_table_foreach (hash_table, hash2slist_foreach, &slist);
308 /* if simulate==TRUE, return whether accel_path can be changed to
309 * accel_key && accel_mods. otherwise, return whether accel_path
310 * was actually changed.
313 internal_change_entry (const gchar *accel_path,
315 GdkModifierType accel_mods,
319 GSList *node, *slist, *win_list, *group_list, *replace_list = NULL;
320 GHashTable *group_hm, *window_hm;
321 gboolean change_accel, removable, can_change = TRUE, seen_accel = FALSE;
323 AccelEntry *entry = accel_path_lookup (accel_path);
325 /* not much todo if there's no entry yet */
330 gtk_accel_map_add_entry (accel_path, 0, 0);
331 entry = accel_path_lookup (accel_path);
332 entry->accel_key = accel_key;
333 entry->accel_mods = accel_mods;
334 entry->changed = TRUE;
336 do_accel_map_changed (entry);
341 /* if there's nothing to change, not much todo either */
342 if (entry->accel_key == accel_key && entry->accel_mods == accel_mods)
345 entry->changed = TRUE;
346 return simulate ? TRUE : FALSE;
349 /* The no-change case has already been handled, so
350 * simulate doesn't make a difference here.
352 if (entry->lock_count > 0)
355 /* nobody's interested, easy going */
360 entry->accel_key = accel_key;
361 entry->accel_mods = accel_mods;
362 entry->changed = TRUE;
364 do_accel_map_changed (entry);
369 /* 1) fetch all accel groups affected by this entry */
370 entry_quark = g_quark_try_string (entry->accel_path);
371 group_hm = g_hash_table_new (NULL, NULL);
372 window_hm = g_hash_table_new (NULL, NULL);
373 for (slist = entry->groups; slist; slist = slist->next)
374 g_hash_table_insert (group_hm, slist->data, slist->data);
376 /* 2) collect acceleratables affected */
377 group_list = g_hash_table_slist_values (group_hm);
378 for (slist = group_list; slist; slist = slist->next)
380 GtkAccelGroup *group = slist->data;
382 for (node = _gtk_accel_group_get_accelerables (group); node; node = node->next)
383 g_hash_table_insert (window_hm, node->data, node->data);
385 g_slist_free (group_list);
387 /* 3) include all accel groups used by acceleratables */
388 win_list = g_hash_table_slist_values (window_hm);
389 g_hash_table_destroy (window_hm);
390 for (slist = win_list; slist; slist = slist->next)
391 for (node = gtk_accel_groups_from_object (slist->data); node; node = node->next)
392 g_hash_table_insert (group_hm, node->data, node->data);
393 group_list = g_hash_table_slist_values (group_hm);
394 g_hash_table_destroy (group_hm);
396 /* 4) walk the acceleratables and figure whether they occupy accel_key&accel_mods */
398 for (slist = win_list; slist; slist = slist->next)
399 if (GTK_IS_WINDOW (slist->data)) /* bad kludge in lack of a GtkAcceleratable */
400 if (_gtk_window_query_nonaccels (slist->data, accel_key, accel_mods))
405 removable = !seen_accel;
407 /* 5) walk all accel groups and search for locks */
409 for (slist = group_list; slist; slist = slist->next)
411 GtkAccelGroup *group = slist->data;
412 GtkAccelGroupEntry *ag_entry;
416 ag_entry = entry->accel_key ? gtk_accel_group_query (group, entry->accel_key, entry->accel_mods, &n) : NULL;
417 for (i = 0; i < n; i++)
418 if (ag_entry[i].accel_path_quark == entry_quark)
420 can_change = !(ag_entry[i].key.accel_flags & GTK_ACCEL_LOCKED);
422 goto break_loop_step5;
426 ag_entry = accel_key ? gtk_accel_group_query (group, accel_key, accel_mods, &n) : NULL;
427 for (i = 0; i < n; i++)
430 removable = !gtk_accel_group_get_is_locked (group) && !(ag_entry[i].key.accel_flags & GTK_ACCEL_LOCKED);
432 goto break_loop_step5;
433 if (ag_entry[i].accel_path_quark)
434 replace_list = g_slist_prepend (replace_list, GUINT_TO_POINTER (ag_entry[i].accel_path_quark));
439 /* 6) check whether we can remove existing accelerators */
440 if (removable && can_change)
441 for (slist = replace_list; slist; slist = slist->next)
442 if (!internal_change_entry (g_quark_to_string (GPOINTER_TO_UINT (slist->data)), 0, 0, FALSE, TRUE))
448 /* 7) check conditions and proceed if possible */
449 change_accel = can_change && (!seen_accel || (removable && replace));
451 if (change_accel && !simulate)
453 /* ref accel groups */
454 for (slist = group_list; slist; slist = slist->next)
455 g_object_ref (slist->data);
457 /* 8) remove existing accelerators */
458 for (slist = replace_list; slist; slist = slist->next)
459 internal_change_entry (g_quark_to_string (GPOINTER_TO_UINT (slist->data)), 0, 0, FALSE, FALSE);
461 /* 9) install new accelerator */
462 entry->accel_key = accel_key;
463 entry->accel_mods = accel_mods;
464 entry->changed = TRUE;
466 for (slist = group_list; slist; slist = slist->next)
467 _gtk_accel_group_reconnect (slist->data, g_quark_from_string (entry->accel_path));
469 /* unref accel groups */
470 for (slist = group_list; slist; slist = slist->next)
471 g_object_unref (slist->data);
473 do_accel_map_changed (entry);
475 g_slist_free (replace_list);
476 g_slist_free (group_list);
477 g_slist_free (win_list);
483 * gtk_accel_map_change_entry:
484 * @accel_path: a valid accelerator path
485 * @accel_key: the new accelerator key
486 * @accel_mods: the new accelerator modifiers
487 * @replace: %TRUE if other accelerators may be deleted upon conflicts
488 * @returns: %TRUE if the accelerator could be changed, %FALSE otherwise
490 * Changes the @accel_key and @accel_mods currently associated with @accel_path.
491 * Due to conflicts with other accelerators, a change may not always be possible,
492 * @replace indicates whether other accelerators may be deleted to resolve such
493 * conflicts. A change will only occur if all conflicts could be resolved (which
494 * might not be the case if conflicting accelerators are locked). Successful
495 * changes are indicated by a %TRUE return value.
497 * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
498 * pass a static string, you can save some memory by interning it first with
499 * g_intern_static_string().
502 gtk_accel_map_change_entry (const gchar *accel_path,
504 GdkModifierType accel_mods,
507 g_return_val_if_fail (_gtk_accel_path_is_valid (accel_path), FALSE);
509 return internal_change_entry (accel_path, accel_key, accel_key ? accel_mods : 0, replace, FALSE);
513 accel_map_parse_accel_path (GScanner *scanner)
516 GdkModifierType accel_mods = 0;
519 /* parse accel path */
520 g_scanner_get_next_token (scanner);
521 if (scanner->token != G_TOKEN_STRING)
522 return G_TOKEN_STRING;
524 /* test if the next token is an accelerator */
525 g_scanner_peek_next_token (scanner);
526 if (scanner->next_token != G_TOKEN_STRING)
528 /* if not so, eat that token and error out */
529 g_scanner_get_next_token (scanner);
530 return G_TOKEN_STRING;
533 /* get the full accelerator specification */
534 path = g_strdup (scanner->value.v_string);
535 g_scanner_get_next_token (scanner);
536 accel = g_strdup (scanner->value.v_string);
538 /* ensure the entry is present */
539 gtk_accel_map_add_entry (path, 0, 0);
541 /* and propagate it */
542 gtk_accelerator_parse (accel, &accel_key, &accel_mods);
543 gtk_accel_map_change_entry (path, accel_key, accel_mods, TRUE);
548 /* check correct statement end */
549 g_scanner_get_next_token (scanner);
550 if (scanner->token != ')')
557 accel_map_parse_statement (GScanner *scanner)
559 guint expected_token;
561 g_scanner_get_next_token (scanner);
563 if (scanner->token == G_TOKEN_SYMBOL)
565 guint (*parser_func) (GScanner*);
567 parser_func = (guint (*) (GScanner *))scanner->value.v_symbol;
569 expected_token = parser_func (scanner);
572 expected_token = G_TOKEN_SYMBOL;
574 /* skip rest of statement on errrors
576 if (expected_token != G_TOKEN_NONE)
578 register guint level;
581 if (scanner->token == ')')
583 if (scanner->token == '(')
586 while (!g_scanner_eof (scanner) && level > 0)
588 g_scanner_get_next_token (scanner);
590 if (scanner->token == '(')
592 else if (scanner->token == ')')
599 * gtk_accel_map_load_scanner:
600 * @scanner: a #GScanner which has already been provided with an input file
602 * #GScanner variant of gtk_accel_map_load().
605 gtk_accel_map_load_scanner (GScanner *scanner)
607 gboolean skip_comment_single;
608 gboolean symbol_2_token;
609 gchar *cpair_comment_single;
610 gpointer saved_symbol;
612 g_return_if_fail (scanner != NULL);
614 /* configure scanner */
615 skip_comment_single = scanner->config->skip_comment_single;
616 scanner->config->skip_comment_single = TRUE;
617 cpair_comment_single = scanner->config->cpair_comment_single;
618 scanner->config->cpair_comment_single = ";\n";
619 symbol_2_token = scanner->config->symbol_2_token;
620 scanner->config->symbol_2_token = FALSE;
621 saved_symbol = g_scanner_lookup_symbol (scanner, "gtk_accel_path");
622 g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path",
623 accel_map_parse_accel_path);
625 /* outer parsing loop
627 g_scanner_peek_next_token (scanner);
628 while (scanner->next_token == '(')
630 g_scanner_get_next_token (scanner);
632 accel_map_parse_statement (scanner);
634 g_scanner_peek_next_token (scanner);
638 scanner->config->skip_comment_single = skip_comment_single;
639 scanner->config->cpair_comment_single = cpair_comment_single;
640 scanner->config->symbol_2_token = symbol_2_token;
641 g_scanner_scope_remove_symbol (scanner, 0, "gtk_accel_path");
643 g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path", saved_symbol);
647 * gtk_accel_map_load_fd:
648 * @fd: a valid readable file descriptor
650 * Filedescriptor variant of gtk_accel_map_load().
652 * Note that the file descriptor will not be closed by this function.
655 gtk_accel_map_load_fd (gint fd)
659 g_return_if_fail (fd >= 0);
661 /* create and setup scanner */
662 scanner = g_scanner_new (NULL);
663 g_scanner_input_file (scanner, fd);
665 gtk_accel_map_load_scanner (scanner);
667 g_scanner_destroy (scanner);
671 * gtk_accel_map_load:
672 * @file_name: (type filename): a file containing accelerator specifications,
673 * in the GLib file name encoding
675 * Parses a file previously saved with gtk_accel_map_save() for
676 * accelerator specifications, and propagates them accordingly.
679 gtk_accel_map_load (const gchar *file_name)
683 g_return_if_fail (file_name != NULL);
685 if (!g_file_test (file_name, G_FILE_TEST_IS_REGULAR))
688 fd = g_open (file_name, O_RDONLY, 0);
692 gtk_accel_map_load_fd (fd);
704 gssize count = write (fd, buf, to_write);
721 accel_map_print (gpointer data,
722 const gchar *accel_path,
724 GdkModifierType accel_mods,
727 GString *gstring = g_string_new (changed ? NULL : "; ");
728 gint fd = GPOINTER_TO_INT (data);
731 g_string_append (gstring, "(gtk_accel_path \"");
733 tmp = g_strescape (accel_path, NULL);
734 g_string_append (gstring, tmp);
737 g_string_append (gstring, "\" \"");
739 name = gtk_accelerator_name (accel_key, accel_mods);
740 tmp = g_strescape (name, NULL);
742 g_string_append (gstring, tmp);
745 g_string_append (gstring, "\")\n");
747 write_all (fd, gstring->str, gstring->len);
749 g_string_free (gstring, TRUE);
753 * gtk_accel_map_save_fd:
754 * @fd: a valid writable file descriptor
756 * Filedescriptor variant of gtk_accel_map_save().
758 * Note that the file descriptor will not be closed by this function.
761 gtk_accel_map_save_fd (gint fd)
765 g_return_if_fail (fd >= 0);
767 gstring = g_string_new ("; ");
768 if (g_get_prgname ())
769 g_string_append (gstring, g_get_prgname ());
770 g_string_append (gstring, " GtkAccelMap rc-file -*- scheme -*-\n");
771 g_string_append (gstring, "; this file is an automated accelerator map dump\n");
772 g_string_append (gstring, ";\n");
774 write_all (fd, gstring->str, gstring->len);
776 g_string_free (gstring, TRUE);
778 gtk_accel_map_foreach (GINT_TO_POINTER (fd), accel_map_print);
782 * gtk_accel_map_save:
783 * @file_name: (type filename): the name of the file to contain
784 * accelerator specifications, in the GLib file name encoding
786 * Saves current accelerator specifications (accelerator path, key
787 * and modifiers) to @file_name.
788 * The file is written in a format suitable to be read back in by
789 * gtk_accel_map_load().
792 gtk_accel_map_save (const gchar *file_name)
796 g_return_if_fail (file_name != NULL);
798 fd = g_open (file_name, O_CREAT | O_TRUNC | O_WRONLY, 0644);
802 gtk_accel_map_save_fd (fd);
808 * gtk_accel_map_foreach:
809 * @data: data to be passed into @foreach_func
810 * @foreach_func: (scope call): function to be executed for each accel
811 * map entry which is not filtered out
813 * Loops over the entries in the accelerator map whose accel path
814 * doesn't match any of the filters added with gtk_accel_map_add_filter(),
815 * and execute @foreach_func on each. The signature of @foreach_func is
816 * that of #GtkAccelMapForeach, the @changed parameter indicates whether
817 * this accelerator was changed during runtime (thus, would need
818 * saving during an accelerator map dump).
821 gtk_accel_map_foreach (gpointer data,
822 GtkAccelMapForeach foreach_func)
824 GSList *entries, *slist, *node;
826 g_return_if_fail (foreach_func != NULL);
828 entries = g_hash_table_slist_values (accel_entry_ht);
829 for (slist = entries; slist; slist = slist->next)
831 AccelEntry *entry = slist->data;
832 gboolean changed = entry->accel_key != entry->std_accel_key || entry->accel_mods != entry->std_accel_mods;
834 for (node = accel_filters; node; node = node->next)
835 if (g_pattern_match_string (node->data, entry->accel_path))
837 foreach_func (data, entry->accel_path, entry->accel_key, entry->accel_mods, changed);
841 g_slist_free (entries);
845 * gtk_accel_map_foreach_unfiltered:
846 * @data: data to be passed into @foreach_func
847 * @foreach_func: (scope call): function to be executed for each accel
850 * Loops over all entries in the accelerator map, and execute
851 * @foreach_func on each. The signature of @foreach_func is that of
852 * #GtkAccelMapForeach, the @changed parameter indicates whether
853 * this accelerator was changed during runtime (thus, would need
854 * saving during an accelerator map dump).
857 gtk_accel_map_foreach_unfiltered (gpointer data,
858 GtkAccelMapForeach foreach_func)
860 GSList *entries, *slist;
862 g_return_if_fail (foreach_func != NULL);
864 entries = g_hash_table_slist_values (accel_entry_ht);
865 for (slist = entries; slist; slist = slist->next)
867 AccelEntry *entry = slist->data;
868 gboolean changed = entry->accel_key != entry->std_accel_key || entry->accel_mods != entry->std_accel_mods;
870 foreach_func (data, entry->accel_path, entry->accel_key, entry->accel_mods, changed);
872 g_slist_free (entries);
876 * gtk_accel_map_add_filter:
877 * @filter_pattern: a pattern (see #GPatternSpec)
879 * Adds a filter to the global list of accel path filters.
881 * Accel map entries whose accel path matches one of the filters
882 * are skipped by gtk_accel_map_foreach().
884 * This function is intended for GTK+ modules that create their own
885 * menus, but don't want them to be saved into the applications accelerator
889 gtk_accel_map_add_filter (const gchar *filter_pattern)
894 g_return_if_fail (filter_pattern != NULL);
896 pspec = g_pattern_spec_new (filter_pattern);
897 for (slist = accel_filters; slist; slist = slist->next)
898 if (g_pattern_spec_equal (pspec, slist->data))
900 g_pattern_spec_free (pspec);
903 accel_filters = g_slist_prepend (accel_filters, pspec);
907 _gtk_accel_map_add_group (const gchar *accel_path,
908 GtkAccelGroup *accel_group)
912 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
913 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
915 entry = accel_path_lookup (accel_path);
918 gtk_accel_map_add_entry (accel_path, 0, 0);
919 entry = accel_path_lookup (accel_path);
921 entry->groups = g_slist_prepend (entry->groups, accel_group);
925 _gtk_accel_map_remove_group (const gchar *accel_path,
926 GtkAccelGroup *accel_group)
930 entry = accel_path_lookup (accel_path);
931 g_return_if_fail (entry != NULL);
932 g_return_if_fail (g_slist_find (entry->groups, accel_group));
934 entry->groups = g_slist_remove (entry->groups, accel_group);
939 * gtk_accel_map_lock_path:
940 * @accel_path: a valid accelerator path
942 * Locks the given accelerator path. If the accelerator map doesn't yet contain
943 * an entry for @accel_path, a new one is created.
945 * Locking an accelerator path prevents its accelerator from being changed
946 * during runtime. A locked accelerator path can be unlocked by
947 * gtk_accel_map_unlock_path(). Refer to gtk_accel_map_change_entry()
948 * for information about runtime accelerator changes.
950 * If called more than once, @accel_path remains locked until
951 * gtk_accel_map_unlock_path() has been called an equivalent number
954 * Note that locking of individual accelerator paths is independent from
955 * locking the #GtkAccelGroup containing them. For runtime accelerator
956 * changes to be possible both the accelerator path and its #GtkAccelGroup
957 * have to be unlocked.
962 gtk_accel_map_lock_path (const gchar *accel_path)
966 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
968 entry = accel_path_lookup (accel_path);
972 gtk_accel_map_add_entry (accel_path, 0, 0);
973 entry = accel_path_lookup (accel_path);
976 entry->lock_count += 1;
980 * gtk_accel_map_unlock_path:
981 * @accel_path: a valid accelerator path
983 * Undoes the last call to gtk_accel_map_lock_path() on this @accel_path.
984 * Refer to gtk_accel_map_lock_path() for information about accelerator path locking.
989 gtk_accel_map_unlock_path (const gchar *accel_path)
993 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
995 entry = accel_path_lookup (accel_path);
997 g_return_if_fail (entry != NULL && entry->lock_count > 0);
999 entry->lock_count -= 1;
1002 G_DEFINE_TYPE (GtkAccelMap, gtk_accel_map, G_TYPE_OBJECT)
1005 gtk_accel_map_class_init (GtkAccelMapClass *accel_map_class)
1008 * GtkAccelMap::changed:
1009 * @object: the global accel map object
1010 * @accel_path: the path of the accelerator that changed
1011 * @accel_key: the key value for the new accelerator
1012 * @accel_mods: the modifier mask for the new accelerator
1014 * Notifies of a change in the global accelerator map.
1015 * The path is also used as the detail for the signal,
1016 * so it is possible to connect to
1017 * changed::<replaceable>accel_path</replaceable>.
1021 accel_map_signals[CHANGED] = g_signal_new (I_("changed"),
1022 G_TYPE_FROM_CLASS (accel_map_class),
1023 G_SIGNAL_DETAILED|G_SIGNAL_RUN_LAST,
1026 _gtk_marshal_VOID__STRING_UINT_FLAGS,
1028 G_TYPE_STRING, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
1032 gtk_accel_map_init (GtkAccelMap *accel_map)
1037 * gtk_accel_map_get:
1039 * Gets the singleton global #GtkAccelMap object. This object
1040 * is useful only for notification of changes to the accelerator
1041 * map via the ::changed signal; it isn't a parameter to the
1042 * other accelerator map functions.
1044 * Return value: (transfer none): the global #GtkAccelMap object
1049 gtk_accel_map_get (void)
1052 accel_map = g_object_new (GTK_TYPE_ACCEL_MAP, NULL);
1058 do_accel_map_changed (AccelEntry *entry)
1061 g_signal_emit (accel_map,
1062 accel_map_signals[CHANGED],
1063 g_quark_from_string (entry->accel_path),