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 "gtkaccelmapprivate.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 if (accel_entry_ht == NULL)
183 accel_entry_ht = g_hash_table_new (accel_entry_hash, accel_entry_equal);
187 _gtk_accel_path_is_valid (const gchar *accel_path)
191 if (!accel_path || accel_path[0] != '<' ||
192 accel_path[1] == '<' || accel_path[1] == '>' || !accel_path[1])
194 p = strchr (accel_path, '>');
195 if (!p || (p[1] != 0 && p[1] != '/'))
201 * gtk_accel_map_add_entry:
202 * @accel_path: valid accelerator path
203 * @accel_key: the accelerator key
204 * @accel_mods: the accelerator modifiers
206 * Registers a new accelerator with the global accelerator map.
207 * This function should only be called once per @accel_path
208 * with the canonical @accel_key and @accel_mods for this path.
209 * To change the accelerator during runtime programatically, use
210 * gtk_accel_map_change_entry().
212 * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
213 * pass a static string, you can save some memory by interning it first with
214 * g_intern_static_string().
217 gtk_accel_map_add_entry (const gchar *accel_path,
219 GdkModifierType accel_mods)
223 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
228 accel_mods &= gtk_accelerator_get_default_mod_mask ();
230 entry = accel_path_lookup (accel_path);
233 if (!entry->std_accel_key && !entry->std_accel_mods &&
234 (accel_key || accel_mods))
236 entry->std_accel_key = accel_key;
237 entry->std_accel_mods = accel_mods;
239 gtk_accel_map_change_entry (entry->accel_path, accel_key, accel_mods, TRUE);
244 entry = g_slice_new0 (AccelEntry);
245 entry->accel_path = g_intern_string (accel_path);
246 entry->std_accel_key = accel_key;
247 entry->std_accel_mods = accel_mods;
248 entry->accel_key = accel_key;
249 entry->accel_mods = accel_mods;
250 entry->changed = FALSE;
251 entry->lock_count = 0;
252 g_hash_table_insert (accel_entry_ht, entry, entry);
254 do_accel_map_changed (entry);
259 * gtk_accel_map_lookup_entry:
260 * @accel_path: a valid accelerator path
261 * @key: (allow-none) (out): the accelerator key to be filled in (optional)
263 * Looks up the accelerator entry for @accel_path and fills in @key.
265 * Returns: %TRUE if @accel_path is known, %FALSE otherwise
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
489 * Changes the @accel_key and @accel_mods currently associated with @accel_path.
490 * Due to conflicts with other accelerators, a change may not always be possible,
491 * @replace indicates whether other accelerators may be deleted to resolve such
492 * conflicts. A change will only occur if all conflicts could be resolved (which
493 * might not be the case if conflicting accelerators are locked). Successful
494 * changes are indicated by a %TRUE return value.
496 * Note that @accel_path string will be stored in a #GQuark. Therefore, if you
497 * pass a static string, you can save some memory by interning it first with
498 * g_intern_static_string().
500 * Returns: %TRUE if the accelerator could be changed, %FALSE otherwise
503 gtk_accel_map_change_entry (const gchar *accel_path,
505 GdkModifierType accel_mods,
508 g_return_val_if_fail (_gtk_accel_path_is_valid (accel_path), FALSE);
510 return internal_change_entry (accel_path, accel_key, accel_key ? accel_mods : 0, replace, FALSE);
514 accel_map_parse_accel_path (GScanner *scanner)
517 GdkModifierType accel_mods = 0;
520 /* parse accel path */
521 g_scanner_get_next_token (scanner);
522 if (scanner->token != G_TOKEN_STRING)
523 return G_TOKEN_STRING;
525 /* test if the next token is an accelerator */
526 g_scanner_peek_next_token (scanner);
527 if (scanner->next_token != G_TOKEN_STRING)
529 /* if not so, eat that token and error out */
530 g_scanner_get_next_token (scanner);
531 return G_TOKEN_STRING;
534 /* get the full accelerator specification */
535 path = g_strdup (scanner->value.v_string);
536 g_scanner_get_next_token (scanner);
537 accel = g_strdup (scanner->value.v_string);
539 /* ensure the entry is present */
540 gtk_accel_map_add_entry (path, 0, 0);
542 /* and propagate it */
543 gtk_accelerator_parse (accel, &accel_key, &accel_mods);
544 gtk_accel_map_change_entry (path, accel_key, accel_mods, TRUE);
549 /* check correct statement end */
550 g_scanner_get_next_token (scanner);
551 if (scanner->token != ')')
558 accel_map_parse_statement (GScanner *scanner)
560 guint expected_token;
562 g_scanner_get_next_token (scanner);
564 if (scanner->token == G_TOKEN_SYMBOL)
566 guint (*parser_func) (GScanner*);
568 parser_func = (guint (*) (GScanner *))scanner->value.v_symbol;
570 expected_token = parser_func (scanner);
573 expected_token = G_TOKEN_SYMBOL;
575 /* skip rest of statement on errrors
577 if (expected_token != G_TOKEN_NONE)
579 register guint level;
582 if (scanner->token == ')')
584 if (scanner->token == '(')
587 while (!g_scanner_eof (scanner) && level > 0)
589 g_scanner_get_next_token (scanner);
591 if (scanner->token == '(')
593 else if (scanner->token == ')')
600 * gtk_accel_map_load_scanner:
601 * @scanner: a #GScanner which has already been provided with an input file
603 * #GScanner variant of gtk_accel_map_load().
606 gtk_accel_map_load_scanner (GScanner *scanner)
608 gboolean skip_comment_single;
609 gboolean symbol_2_token;
610 gchar *cpair_comment_single;
611 gpointer saved_symbol;
613 g_return_if_fail (scanner != NULL);
615 /* configure scanner */
616 skip_comment_single = scanner->config->skip_comment_single;
617 scanner->config->skip_comment_single = TRUE;
618 cpair_comment_single = scanner->config->cpair_comment_single;
619 scanner->config->cpair_comment_single = ";\n";
620 symbol_2_token = scanner->config->symbol_2_token;
621 scanner->config->symbol_2_token = FALSE;
622 saved_symbol = g_scanner_lookup_symbol (scanner, "gtk_accel_path");
623 g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path",
624 accel_map_parse_accel_path);
626 /* outer parsing loop
628 g_scanner_peek_next_token (scanner);
629 while (scanner->next_token == '(')
631 g_scanner_get_next_token (scanner);
633 accel_map_parse_statement (scanner);
635 g_scanner_peek_next_token (scanner);
639 scanner->config->skip_comment_single = skip_comment_single;
640 scanner->config->cpair_comment_single = cpair_comment_single;
641 scanner->config->symbol_2_token = symbol_2_token;
642 g_scanner_scope_remove_symbol (scanner, 0, "gtk_accel_path");
644 g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path", saved_symbol);
648 * gtk_accel_map_load_fd:
649 * @fd: a valid readable file descriptor
651 * Filedescriptor variant of gtk_accel_map_load().
653 * Note that the file descriptor will not be closed by this function.
656 gtk_accel_map_load_fd (gint fd)
660 g_return_if_fail (fd >= 0);
662 /* create and setup scanner */
663 scanner = g_scanner_new (NULL);
664 g_scanner_input_file (scanner, fd);
666 gtk_accel_map_load_scanner (scanner);
668 g_scanner_destroy (scanner);
672 * gtk_accel_map_load:
673 * @file_name: (type filename): a file containing accelerator specifications,
674 * in the GLib file name encoding
676 * Parses a file previously saved with gtk_accel_map_save() for
677 * accelerator specifications, and propagates them accordingly.
680 gtk_accel_map_load (const gchar *file_name)
684 g_return_if_fail (file_name != NULL);
686 if (!g_file_test (file_name, G_FILE_TEST_IS_REGULAR))
689 fd = g_open (file_name, O_RDONLY, 0);
693 gtk_accel_map_load_fd (fd);
705 gssize count = write (fd, buf, to_write);
722 accel_map_print (gpointer data,
723 const gchar *accel_path,
725 GdkModifierType accel_mods,
728 GString *gstring = g_string_new (changed ? NULL : "; ");
729 gint fd = GPOINTER_TO_INT (data);
732 g_string_append (gstring, "(gtk_accel_path \"");
734 tmp = g_strescape (accel_path, NULL);
735 g_string_append (gstring, tmp);
738 g_string_append (gstring, "\" \"");
740 name = gtk_accelerator_name (accel_key, accel_mods);
741 tmp = g_strescape (name, NULL);
743 g_string_append (gstring, tmp);
746 g_string_append (gstring, "\")\n");
748 write_all (fd, gstring->str, gstring->len);
750 g_string_free (gstring, TRUE);
754 * gtk_accel_map_save_fd:
755 * @fd: a valid writable file descriptor
757 * Filedescriptor variant of gtk_accel_map_save().
759 * Note that the file descriptor will not be closed by this function.
762 gtk_accel_map_save_fd (gint fd)
766 g_return_if_fail (fd >= 0);
768 gstring = g_string_new ("; ");
769 if (g_get_prgname ())
770 g_string_append (gstring, g_get_prgname ());
771 g_string_append (gstring, " GtkAccelMap rc-file -*- scheme -*-\n");
772 g_string_append (gstring, "; this file is an automated accelerator map dump\n");
773 g_string_append (gstring, ";\n");
775 write_all (fd, gstring->str, gstring->len);
777 g_string_free (gstring, TRUE);
779 gtk_accel_map_foreach (GINT_TO_POINTER (fd), accel_map_print);
783 * gtk_accel_map_save:
784 * @file_name: (type filename): the name of the file to contain
785 * accelerator specifications, in the GLib file name encoding
787 * Saves current accelerator specifications (accelerator path, key
788 * and modifiers) to @file_name.
789 * The file is written in a format suitable to be read back in by
790 * gtk_accel_map_load().
793 gtk_accel_map_save (const gchar *file_name)
797 g_return_if_fail (file_name != NULL);
799 fd = g_open (file_name, O_CREAT | O_TRUNC | O_WRONLY, 0644);
803 gtk_accel_map_save_fd (fd);
809 * gtk_accel_map_foreach:
810 * @data: (allow-none): data to be passed into @foreach_func
811 * @foreach_func: (scope call): function to be executed for each accel
812 * map entry which is not filtered out
814 * Loops over the entries in the accelerator map whose accel path
815 * doesn't match any of the filters added with gtk_accel_map_add_filter(),
816 * and execute @foreach_func on each. The signature of @foreach_func is
817 * that of #GtkAccelMapForeach, the @changed parameter indicates whether
818 * this accelerator was changed during runtime (thus, would need
819 * saving during an accelerator map dump).
822 gtk_accel_map_foreach (gpointer data,
823 GtkAccelMapForeach foreach_func)
825 GSList *entries, *slist, *node;
827 g_return_if_fail (foreach_func != NULL);
829 entries = g_hash_table_slist_values (accel_entry_ht);
830 for (slist = entries; slist; slist = slist->next)
832 AccelEntry *entry = slist->data;
833 gboolean changed = entry->accel_key != entry->std_accel_key || entry->accel_mods != entry->std_accel_mods;
835 for (node = accel_filters; node; node = node->next)
836 if (g_pattern_match_string (node->data, entry->accel_path))
838 foreach_func (data, entry->accel_path, entry->accel_key, entry->accel_mods, changed);
842 g_slist_free (entries);
846 * gtk_accel_map_foreach_unfiltered:
847 * @data: data to be passed into @foreach_func
848 * @foreach_func: (scope call): function to be executed for each accel
851 * Loops over all entries in the accelerator map, and execute
852 * @foreach_func on each. The signature of @foreach_func is that of
853 * #GtkAccelMapForeach, the @changed parameter indicates whether
854 * this accelerator was changed during runtime (thus, would need
855 * saving during an accelerator map dump).
858 gtk_accel_map_foreach_unfiltered (gpointer data,
859 GtkAccelMapForeach foreach_func)
861 GSList *entries, *slist;
863 g_return_if_fail (foreach_func != NULL);
865 entries = g_hash_table_slist_values (accel_entry_ht);
866 for (slist = entries; slist; slist = slist->next)
868 AccelEntry *entry = slist->data;
869 gboolean changed = entry->accel_key != entry->std_accel_key || entry->accel_mods != entry->std_accel_mods;
871 foreach_func (data, entry->accel_path, entry->accel_key, entry->accel_mods, changed);
873 g_slist_free (entries);
877 * gtk_accel_map_add_filter:
878 * @filter_pattern: a pattern (see #GPatternSpec)
880 * Adds a filter to the global list of accel path filters.
882 * Accel map entries whose accel path matches one of the filters
883 * are skipped by gtk_accel_map_foreach().
885 * This function is intended for GTK+ modules that create their own
886 * menus, but don't want them to be saved into the applications accelerator
890 gtk_accel_map_add_filter (const gchar *filter_pattern)
895 g_return_if_fail (filter_pattern != NULL);
897 pspec = g_pattern_spec_new (filter_pattern);
898 for (slist = accel_filters; slist; slist = slist->next)
899 if (g_pattern_spec_equal (pspec, slist->data))
901 g_pattern_spec_free (pspec);
904 accel_filters = g_slist_prepend (accel_filters, pspec);
908 _gtk_accel_map_add_group (const gchar *accel_path,
909 GtkAccelGroup *accel_group)
913 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
914 g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
916 entry = accel_path_lookup (accel_path);
919 gtk_accel_map_add_entry (accel_path, 0, 0);
920 entry = accel_path_lookup (accel_path);
922 entry->groups = g_slist_prepend (entry->groups, accel_group);
926 _gtk_accel_map_remove_group (const gchar *accel_path,
927 GtkAccelGroup *accel_group)
931 entry = accel_path_lookup (accel_path);
932 g_return_if_fail (entry != NULL);
933 g_return_if_fail (g_slist_find (entry->groups, accel_group));
935 entry->groups = g_slist_remove (entry->groups, accel_group);
940 * gtk_accel_map_lock_path:
941 * @accel_path: a valid accelerator path
943 * Locks the given accelerator path. If the accelerator map doesn't yet contain
944 * an entry for @accel_path, a new one is created.
946 * Locking an accelerator path prevents its accelerator from being changed
947 * during runtime. A locked accelerator path can be unlocked by
948 * gtk_accel_map_unlock_path(). Refer to gtk_accel_map_change_entry()
949 * for information about runtime accelerator changes.
951 * If called more than once, @accel_path remains locked until
952 * gtk_accel_map_unlock_path() has been called an equivalent number
955 * Note that locking of individual accelerator paths is independent from
956 * locking the #GtkAccelGroup containing them. For runtime accelerator
957 * changes to be possible both the accelerator path and its #GtkAccelGroup
958 * have to be unlocked.
963 gtk_accel_map_lock_path (const gchar *accel_path)
967 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
969 entry = accel_path_lookup (accel_path);
973 gtk_accel_map_add_entry (accel_path, 0, 0);
974 entry = accel_path_lookup (accel_path);
977 entry->lock_count += 1;
981 * gtk_accel_map_unlock_path:
982 * @accel_path: a valid accelerator path
984 * Undoes the last call to gtk_accel_map_lock_path() on this @accel_path.
985 * Refer to gtk_accel_map_lock_path() for information about accelerator path locking.
990 gtk_accel_map_unlock_path (const gchar *accel_path)
994 g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
996 entry = accel_path_lookup (accel_path);
998 g_return_if_fail (entry != NULL && entry->lock_count > 0);
1000 entry->lock_count -= 1;
1003 G_DEFINE_TYPE (GtkAccelMap, gtk_accel_map, G_TYPE_OBJECT)
1006 gtk_accel_map_class_init (GtkAccelMapClass *accel_map_class)
1009 * GtkAccelMap::changed:
1010 * @object: the global accel map object
1011 * @accel_path: the path of the accelerator that changed
1012 * @accel_key: the key value for the new accelerator
1013 * @accel_mods: the modifier mask for the new accelerator
1015 * Notifies of a change in the global accelerator map.
1016 * The path is also used as the detail for the signal,
1017 * so it is possible to connect to
1018 * changed::<replaceable>accel_path</replaceable>.
1022 accel_map_signals[CHANGED] = g_signal_new (I_("changed"),
1023 G_TYPE_FROM_CLASS (accel_map_class),
1024 G_SIGNAL_DETAILED|G_SIGNAL_RUN_LAST,
1027 _gtk_marshal_VOID__STRING_UINT_FLAGS,
1029 G_TYPE_STRING, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
1033 gtk_accel_map_init (GtkAccelMap *accel_map)
1038 * gtk_accel_map_get:
1040 * Gets the singleton global #GtkAccelMap object. This object
1041 * is useful only for notification of changes to the accelerator
1042 * map via the ::changed signal; it isn't a parameter to the
1043 * other accelerator map functions.
1045 * Return value: (transfer none): the global #GtkAccelMap object
1050 gtk_accel_map_get (void)
1053 accel_map = g_object_new (GTK_TYPE_ACCEL_MAP, NULL);
1059 do_accel_map_changed (AccelEntry *entry)
1062 g_signal_emit (accel_map,
1063 accel_map_signals[CHANGED],
1064 g_quark_from_string (entry->accel_path),
1071 _gtk_accel_path_for_action (const gchar *action_name,
1072 GVariant *parameter)
1076 s = g_string_new ("<GAction>/");
1077 g_string_append (s, action_name);
1080 g_string_append_c (s, '/');
1081 g_variant_print_string (parameter, s, FALSE);
1083 return g_string_free (s, FALSE);