]> Pileus Git - ~andy/gtk/blob - gtk/gtkaccelmap.c
Fix problem with wrong depth being used. (#89941, Jacob Berkman.) Remove
[~andy/gtk] / gtk / gtkaccelmap.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 #include "config.h"
21
22 #include "gtkaccelmap.h"
23
24 #include "gtkwindow.h"  /* in lack of GtkAcceleratable */
25
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #ifdef G_OS_WIN32
33 #include <io.h>
34 #endif
35 #include <errno.h>
36
37
38 /* --- structures --- */
39 typedef struct {
40   const gchar *accel_path;
41   guint        accel_key;
42   guint        accel_mods;
43   guint        std_accel_key;
44   guint        std_accel_mods;
45   guint        changed : 1;
46   GSList      *groups;
47 } AccelEntry;
48
49
50 /* --- variables --- */
51 static GHashTable *accel_entry_ht = NULL;       /* accel_path -> AccelEntry */
52 static GSList     *accel_filters = NULL;
53
54
55 /* --- functions --- */
56 static guint
57 accel_entry_hash (gconstpointer key)
58 {
59   const AccelEntry *entry = key;
60
61   return g_str_hash (entry->accel_path);
62 }
63
64 static gboolean
65 accel_entry_equal (gconstpointer key1,
66                    gconstpointer key2)
67 {
68   const AccelEntry *entry1 = key1;
69   const AccelEntry *entry2 = key2;
70
71   return g_str_equal (entry1->accel_path, entry2->accel_path);
72 }
73
74 static inline AccelEntry*
75 accel_path_lookup (const gchar *accel_path)
76 {
77   AccelEntry ekey;
78
79   ekey.accel_path = accel_path;
80
81   /* safety NULL check for return_if_fail()s */
82   return accel_path ? g_hash_table_lookup (accel_entry_ht, &ekey) : NULL;
83 }
84
85 void
86 _gtk_accel_map_init (void)
87 {
88   g_assert (accel_entry_ht == NULL);
89
90   accel_entry_ht = g_hash_table_new (accel_entry_hash, accel_entry_equal);
91 }
92
93 gboolean
94 _gtk_accel_path_is_valid (const gchar *accel_path)
95 {
96   gchar *p;
97
98   if (!accel_path || accel_path[0] != '<' ||
99       accel_path[1] == '<' || accel_path[1] == '>' || !accel_path[1])
100     return FALSE;
101   p = strchr (accel_path, '>');
102   if (!p || p[1] != '/')
103     return FALSE;
104   return TRUE;
105 }
106
107 /**
108  * gtk_accel_map_add_entry:
109  * @accel_path: valid accelerator path
110  * @accel_key:  the accelerator key
111  * @accel_mods: the accelerator modifiers
112  *
113  * Registers a new accelerator with the global accelerator map.
114  * This function should only be called once per @accel_path
115  * with the canonical @accel_key and @accel_mods for this path.
116  * To change the accelerator during runtime programatically, use
117  * gtk_accel_map_change_entry().
118  * The accelerator path must consist of "&lt;WINDOWTYPE&gt;/Category1/Category2/.../Action",
119  * where &lt;WINDOWTYPE&gt; should be a unique application-specific identifier, that
120  * corresponds to the kind of window the accelerator is being used in, e.g. "Gimp-Image",
121  * "Abiword-Document" or "Gnumeric-Settings".
122  * The Category1/.../Action portion is most appropriately chosen by the action the
123  * accelerator triggers, i.e. for accelerators on menu items, choose the item's menu path,
124  * e.g. "File/Save As", "Image/View/Zoom" or "Edit/Select All".
125  * So a full valid accelerator path may look like:
126  * "&lt;Gimp-Toolbox&gt;/File/Dialogs/Tool Options...".
127  */
128 void
129 gtk_accel_map_add_entry (const gchar    *accel_path,
130                          guint           accel_key,
131                          GdkModifierType accel_mods)
132 {
133   AccelEntry *entry;
134
135   g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
136
137   if (!accel_key)
138     accel_mods = 0;
139   else
140     accel_mods &= gtk_accelerator_get_default_mod_mask ();
141
142   entry = accel_path_lookup (accel_path);
143   if (entry)
144     {
145       if (!entry->std_accel_key && !entry->std_accel_mods &&
146           (accel_key || accel_mods))
147         {
148           entry->std_accel_key = accel_key;
149           entry->std_accel_mods = accel_mods;
150           if (!entry->changed)
151             gtk_accel_map_change_entry (entry->accel_path, accel_key, accel_mods, TRUE);
152         }
153     }
154   else
155     {
156       entry = g_new0 (AccelEntry, 1);
157       entry->accel_path = g_quark_to_string (g_quark_from_string (accel_path));
158       entry->std_accel_key = accel_key;
159       entry->std_accel_mods = accel_mods;
160       entry->accel_key = accel_key;
161       entry->accel_mods = accel_mods;
162       entry->changed = FALSE;
163       g_hash_table_insert (accel_entry_ht, entry, entry);
164     }
165 }
166
167 /**
168  * gtk_accel_map_lookup_entry:
169  * @accel_path:  a valid accelerator path
170  * @key:         the accelerator key to be filled in (optional)
171  * @returns:     %TRUE if @accel_path is known, %FALSE otherwise
172  *
173  * Looks up the accelerator entry for @accel_path and fills in @key.
174  */
175 gboolean
176 gtk_accel_map_lookup_entry (const gchar *accel_path,
177                             GtkAccelKey *key)
178 {
179   AccelEntry *entry;
180
181   g_return_val_if_fail (_gtk_accel_path_is_valid (accel_path), FALSE);
182
183   entry = accel_path_lookup (accel_path);
184   if (entry && key)
185     {
186       key->accel_key = entry->accel_key;
187       key->accel_mods = entry->accel_mods;
188       key->accel_flags = 0;
189     }
190
191   return entry ? TRUE : FALSE;
192 }
193
194 static void
195 hash2slist_foreach (gpointer  key,
196                     gpointer  value,
197                     gpointer  user_data)
198 {
199   GSList **slist_p = user_data;
200
201   *slist_p = g_slist_prepend (*slist_p, value);
202 }
203
204 static GSList*
205 g_hash_table_slist_values (GHashTable *hash_table)
206 {
207   GSList *slist = NULL;
208
209   g_return_val_if_fail (hash_table != NULL, NULL);
210
211   g_hash_table_foreach (hash_table, hash2slist_foreach, &slist);
212
213   return slist;
214 }
215
216 /* if simulate==TRUE, return whether accel_path can be changed to
217  * accel_key && accel_mods. otherwise, return whether accel_path
218  * was actually changed.
219  */
220 static gboolean
221 internal_change_entry (const gchar    *accel_path,
222                        guint           accel_key,
223                        GdkModifierType accel_mods,
224                        gboolean        replace,
225                        gboolean        simulate)
226 {
227   GSList *node, *slist, *win_list, *group_list, *replace_list = NULL;
228   GHashTable *group_hm, *window_hm;
229   gboolean change_accel, removable, can_change = TRUE, seen_accel = FALSE;
230   GQuark entry_quark;
231   AccelEntry *entry = accel_path_lookup (accel_path);
232
233   /* not much todo if there's no entry yet */
234   if (!entry)
235     {
236       if (!simulate)
237         {
238           gtk_accel_map_add_entry (accel_path, 0, 0);
239           entry = accel_path_lookup (accel_path);
240           entry->accel_key = accel_key;
241           entry->accel_mods = accel_mods;
242           entry->changed = TRUE;
243         }
244       return TRUE;
245     }
246
247   /* if there's nothing to change, not much todo either */
248   if (entry->accel_key == accel_key && entry->accel_mods == accel_mods)
249     {
250       if (!simulate)
251         entry->changed = TRUE;
252       return simulate ? TRUE : FALSE;
253     }
254
255   /* nobody's interested, easy going */
256   if (!entry->groups)
257     {
258       if (!simulate)
259         {
260           entry->accel_key = accel_key;
261           entry->accel_mods = accel_mods;
262           entry->changed = TRUE;
263         }
264       return TRUE;
265     }
266
267   /* 1) fetch all accel groups affected by this entry */
268   entry_quark = g_quark_try_string (entry->accel_path);
269   group_hm = g_hash_table_new (NULL, NULL);
270   window_hm = g_hash_table_new (NULL, NULL);
271   for (slist = entry->groups; slist; slist = slist->next)
272     g_hash_table_insert (group_hm, slist->data, slist->data);
273
274   /* 2) collect acceleratables affected */
275   group_list = g_hash_table_slist_values (group_hm);
276   for (slist = group_list; slist; slist = slist->next)
277     {
278       GtkAccelGroup *group = slist->data;
279
280       for (node = group->acceleratables; node; node = node->next)
281         g_hash_table_insert (window_hm, node->data, node->data);
282     }
283   g_slist_free (group_list);
284
285   /* 3) include all accel groups used by acceleratables */
286   win_list = g_hash_table_slist_values (window_hm);
287   g_hash_table_destroy (window_hm);
288   for (slist = win_list; slist; slist = slist->next)
289     for (node = gtk_accel_groups_from_object (slist->data); node; node = node->next)
290       g_hash_table_insert (group_hm, node->data, node->data);
291   group_list = g_hash_table_slist_values (group_hm);
292   g_hash_table_destroy (group_hm);
293   
294   /* 4) walk the acceleratables and figure whether they occupy accel_key&accel_mods */
295   if (accel_key)
296     for (slist = win_list; slist; slist = slist->next)
297       if (GTK_IS_WINDOW (slist->data))  /* bad kludge in lack of a GtkAcceleratable */
298         if (_gtk_window_query_nonaccels (slist->data, accel_key, accel_mods))
299           {
300             seen_accel = TRUE;
301             break;
302           }
303   removable = !seen_accel;
304   
305   /* 5) walk all accel groups and search for locks */
306   if (removable)
307     for (slist = group_list; slist; slist = slist->next)
308       {
309         GtkAccelGroup *group = slist->data;
310         GtkAccelGroupEntry *ag_entry;
311         guint i, n;
312         
313         n = 0;
314         ag_entry = entry->accel_key ? gtk_accel_group_query (group, entry->accel_key, entry->accel_mods, &n) : NULL;
315         for (i = 0; i < n; i++)
316           if (ag_entry[i].accel_path_quark == entry_quark)
317             {
318               can_change = !(ag_entry[i].key.accel_flags & GTK_ACCEL_LOCKED);
319               if (!can_change)
320                 goto break_loop_step5;
321             }
322         
323         n = 0;
324         ag_entry = accel_key ? gtk_accel_group_query (group, accel_key, accel_mods, &n) : NULL;
325         for (i = 0; i < n; i++)
326           {
327             seen_accel = TRUE;
328             removable = !group->lock_count && !(ag_entry[i].key.accel_flags & GTK_ACCEL_LOCKED);
329             if (!removable)
330               goto break_loop_step5;
331             if (ag_entry[i].accel_path_quark)
332               replace_list = g_slist_prepend (replace_list, GUINT_TO_POINTER (ag_entry->accel_path_quark));
333           }
334       }
335  break_loop_step5:
336   
337   /* 6) check whether we can remove existing accelerators */
338   if (removable && can_change)
339     for (slist = replace_list; slist; slist = slist->next)
340       if (!internal_change_entry (g_quark_to_string (GPOINTER_TO_UINT (slist->data)), 0, 0, FALSE, TRUE))
341         {
342           removable = FALSE;
343           break;
344         }
345   
346   /* 7) check conditions and proceed if possible */
347   change_accel = can_change && (!seen_accel || (removable && replace));
348   
349   if (change_accel && !simulate)
350     {
351       guint old_accel_key, old_accel_mods;
352       
353       /* ref accel groups */
354       for (slist = group_list; slist; slist = slist->next)
355         g_object_ref (slist->data);
356
357       /* 8) remove existing accelerators */
358       for (slist = replace_list; slist; slist = slist->next)
359         internal_change_entry (g_quark_to_string (GPOINTER_TO_UINT (slist->data)), 0, 0, FALSE, FALSE);
360
361       /* 9) install new accelerator */
362       old_accel_key = entry->accel_key;
363       old_accel_mods = entry->accel_mods;
364       entry->accel_key = accel_key;
365       entry->accel_mods = accel_mods;
366       entry->changed = TRUE;
367       for (slist = group_list; slist; slist = slist->next)
368         _gtk_accel_group_reconnect (slist->data, g_quark_from_string (entry->accel_path));
369
370       /* unref accel groups */
371       for (slist = group_list; slist; slist = slist->next)
372         g_object_unref (slist->data);
373     }
374   g_slist_free (replace_list);
375   g_slist_free (group_list);
376   g_slist_free (win_list);
377
378   return change_accel;
379 }
380
381 /**
382  * gtk_accel_map_change_entry:
383  * @accel_path:  a valid accelerator path
384  * @accel_key:   the new accelerator key
385  * @accel_mods:  the new accelerator modifiers
386  * @replace:     %TRUE if other accelerators may be deleted upon conflicts
387  * @returns:     %TRUE if the accelerator could be changed, %FALSE otherwise
388  *
389  * Changes the @accel_key and @accel_mods currently associated with @accel_path.
390  * Due to conflicts with other accelerators, a change may not always be possible,
391  * @replace indicates whether other accelerators may be deleted to resolve such
392  * conflicts. A change will only occur if all conflicts could be resolved (which
393  * might not be the case if conflicting accelerators are locked). Successful
394  * changes are indicated by a %TRUE return value.
395  */
396 gboolean
397 gtk_accel_map_change_entry (const gchar    *accel_path,
398                             guint           accel_key,
399                             GdkModifierType accel_mods,
400                             gboolean        replace)
401 {
402   g_return_val_if_fail (_gtk_accel_path_is_valid (accel_path), FALSE);
403
404   return internal_change_entry (accel_path, accel_key, accel_key ? accel_mods : 0, replace, FALSE);
405 }
406
407 static guint
408 accel_map_parse_accel_path (GScanner *scanner)
409 {
410   guint accel_key = 0;
411   GdkModifierType accel_mods = 0;
412   gchar *path, *accel;
413   
414   /* parse accel path */
415   g_scanner_get_next_token (scanner);
416   if (scanner->token != G_TOKEN_STRING)
417     return G_TOKEN_STRING;
418
419   /* test if the next token is an accelerator */
420   g_scanner_peek_next_token (scanner);
421   if (scanner->next_token != G_TOKEN_STRING)
422     {
423       /* if not so, eat that token and error out */
424       g_scanner_get_next_token (scanner);
425       return G_TOKEN_STRING;
426     }
427
428   /* get the full accelerator specification */
429   path = g_strdup (scanner->value.v_string);
430   g_scanner_get_next_token (scanner);
431   accel = g_strdup (scanner->value.v_string);
432
433   /* ensure the entry is present */
434   gtk_accel_map_add_entry (path, 0, 0);
435
436   /* and propagate it */
437   gtk_accelerator_parse (accel, &accel_key, &accel_mods);
438   gtk_accel_map_change_entry (path, accel_key, accel_mods, TRUE);
439
440   g_free (accel);
441   g_free (path);
442
443   /* check correct statement end */
444   g_scanner_get_next_token (scanner);
445   if (scanner->token != ')')
446     return ')';
447   else
448     return G_TOKEN_NONE;
449 }
450
451 static void
452 accel_map_parse_statement (GScanner *scanner)
453 {
454   guint expected_token;
455
456   g_scanner_get_next_token (scanner);
457
458   if (scanner->token == G_TOKEN_SYMBOL)
459     {
460       guint (*parser_func) (GScanner*);
461
462       parser_func = (guint (*) (GScanner *))scanner->value.v_symbol;
463
464       expected_token = parser_func (scanner);
465     }
466   else
467     expected_token = G_TOKEN_SYMBOL;
468
469   /* skip rest of statement on errrors
470    */
471   if (expected_token != G_TOKEN_NONE)
472     {
473       register guint level;
474
475       level = 1;
476       if (scanner->token == ')')
477         level--;
478       if (scanner->token == '(')
479         level++;
480
481       while (!g_scanner_eof (scanner) && level > 0)
482         {
483           g_scanner_get_next_token (scanner);
484
485           if (scanner->token == '(')
486             level++;
487           else if (scanner->token == ')')
488             level--;
489         }
490     }
491 }
492
493 /**
494  * gtk_accel_map_load_scanner:
495  * @scanner: a #GScanner which has already been provided with an input file
496  *
497  * #GScanner variant of gtk_accel_map_load().
498  */
499 void
500 gtk_accel_map_load_scanner (GScanner *scanner)
501 {
502   gboolean skip_comment_single;
503   gboolean symbol_2_token;
504   gchar *cpair_comment_single;
505   gpointer saved_symbol;
506   
507   g_return_if_fail (scanner != 0);
508
509   /* configure scanner */
510   skip_comment_single = scanner->config->skip_comment_single;
511   scanner->config->skip_comment_single = TRUE;
512   cpair_comment_single = scanner->config->cpair_comment_single;
513   scanner->config->cpair_comment_single = ";\n";
514   symbol_2_token = scanner->config->symbol_2_token;
515   scanner->config->symbol_2_token = FALSE;
516   saved_symbol = g_scanner_lookup_symbol (scanner, "gtk_accel_path");
517   g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path", 
518                               (gpointer) accel_map_parse_accel_path);
519
520   /* outer parsing loop
521    */
522   g_scanner_peek_next_token (scanner);
523   while (scanner->next_token == '(')
524     {
525       g_scanner_get_next_token (scanner);
526
527       accel_map_parse_statement (scanner);
528
529       g_scanner_peek_next_token (scanner);
530     }
531
532   /* restore config */
533   scanner->config->skip_comment_single = skip_comment_single;
534   scanner->config->cpair_comment_single = cpair_comment_single;
535   scanner->config->symbol_2_token = symbol_2_token;
536   g_scanner_scope_remove_symbol (scanner, 0, "gtk_accel_path");
537   if (saved_symbol)
538     g_scanner_scope_add_symbol (scanner, 0, "gtk_accel_path", saved_symbol);
539 }
540
541 /**
542  * gtk_accel_map_load_fd:
543  * @fd: a valid readable file descriptor
544  *
545  * Filedescriptor variant of gtk_accel_map_load().
546  *
547  * Note that the file descriptor will not be closed by this function.
548  */
549 void
550 gtk_accel_map_load_fd (gint fd)
551 {
552   GScanner *scanner;
553
554   g_return_if_fail (fd >= 0);
555
556   /* create and setup scanner */
557   scanner = g_scanner_new (NULL);
558   g_scanner_input_file (scanner, fd);
559
560   gtk_accel_map_load_scanner (scanner);
561
562   g_scanner_destroy (scanner);
563 }
564
565 /**
566  * gtk_accel_map_load:
567  * @file_name: a file containing accelerator specifications
568  *
569  * Parses a file previously saved with gtk_accel_map_save() for
570  * accelerator specifications, and propagates them accordingly.
571  */
572 void
573 gtk_accel_map_load (const gchar *file_name)
574 {
575   gint fd;
576
577   g_return_if_fail (file_name != NULL);
578
579   if (!g_file_test (file_name, G_FILE_TEST_IS_REGULAR))
580     return;
581
582   fd = open (file_name, O_RDONLY);
583   if (fd < 0)
584     return;
585
586   gtk_accel_map_load_fd (fd);
587
588   close (fd);
589 }
590
591 static gboolean
592 write_all (gint   fd,
593            gchar *buf,
594            gsize  to_write)
595 {
596   while (to_write > 0)
597     {
598       gssize count = write (fd, buf, to_write);
599       if (count < 0)
600         {
601           if (errno != EINTR)
602             return FALSE;
603         }
604       else
605         {
606           to_write -= count;
607           buf += count;
608         }
609     }
610
611   return TRUE;
612 }
613
614 static void
615 accel_map_print (gpointer        data,
616                  const gchar    *accel_path,
617                  guint           accel_key,
618                  GdkModifierType accel_mods,
619                  gboolean        changed)
620 {
621   GString *gstring = g_string_new (changed ? NULL : "; ");
622   gint fd = GPOINTER_TO_INT (data);
623   gchar *tmp, *name;
624
625   g_string_append (gstring, "(gtk_accel_path \"");
626
627   tmp = g_strescape (accel_path, NULL);
628   g_string_append (gstring, tmp);
629   g_free (tmp);
630
631   g_string_append (gstring, "\" \"");
632
633   name = gtk_accelerator_name (accel_key, accel_mods);
634   tmp = g_strescape (name, NULL);
635   g_free (name);
636   g_string_append (gstring, tmp);
637   g_free (tmp);
638
639   g_string_append (gstring, "\")\n");
640
641   write_all (fd, gstring->str, gstring->len);
642
643   g_string_free (gstring, TRUE);
644 }
645
646 /**
647  * gtk_accel_map_save_fd:
648  * @fd: a valid writable file descriptor
649  *
650  * Filedescriptor variant of gtk_accel_map_save().
651  *
652  * Note that the file descriptor will not be closed by this function.
653  */
654 void
655 gtk_accel_map_save_fd (gint fd)
656 {
657   GString *gstring;
658
659   g_return_if_fail (fd >= 0);
660
661   gstring = g_string_new ("; ");
662   if (g_get_prgname ())
663     g_string_append (gstring, g_get_prgname ());
664   g_string_append (gstring, " GtkAccelMap rc-file         -*- scheme -*-\n");
665   g_string_append (gstring, "; this file is an automated accelerator map dump\n");
666   g_string_append (gstring, ";\n");
667
668   write_all (fd, gstring->str, gstring->len);
669   
670   g_string_free (gstring, TRUE);
671
672   gtk_accel_map_foreach (GINT_TO_POINTER (fd), accel_map_print);
673 }
674
675 /**
676  * gtk_accel_map_save:
677  * @file_name: the file to contain accelerator specifications
678  *
679  * Saves current accelerator specifications (accelerator path, key
680  * and modifiers) to @file_name.
681  * The file is written in a format suitable to be read back in by
682  * gtk_accel_map_load().
683  */
684 void
685 gtk_accel_map_save (const gchar *file_name)
686 {
687   gint fd;
688
689   g_return_if_fail (file_name != NULL);
690
691   fd = open (file_name, O_CREAT | O_TRUNC | O_WRONLY, 0644);
692   if (fd < 0)
693     return;
694
695   gtk_accel_map_save_fd (fd);
696
697   close (fd);
698 }
699
700 /**
701  * gtk_accel_map_foreach:
702  * @data:         data to be passed into @foreach_func
703  * @foreach_func: function to be executed for each accel map entry which
704  *                is not filtered out
705  *
706  * Loops over the entries in the accelerator map whose accel path 
707  * doesn't match any of the filters added with gtk_accel_map_add_filter(), 
708  * and execute @foreach_func on each. The signature of @foreach_func is 
709  * that of #GtkAccelMapForeach, the @changed parameter indicates whether
710  * this accelerator was changed during runtime (thus, would need
711  * saving during an accelerator map dump).
712  */
713 void
714 gtk_accel_map_foreach (gpointer           data,
715                        GtkAccelMapForeach foreach_func)
716 {
717   GSList *entries, *slist, *node;
718
719   g_return_if_fail (foreach_func != NULL);
720
721   entries = g_hash_table_slist_values (accel_entry_ht);
722   for (slist = entries; slist; slist = slist->next)
723     {
724       AccelEntry *entry = slist->data;
725       gboolean changed = entry->accel_key != entry->std_accel_key || entry->accel_mods != entry->std_accel_mods;
726
727       for (node = accel_filters; node; node = node->next)
728         if (g_pattern_match_string (node->data, entry->accel_path))
729           goto skip_accel;
730       foreach_func (data, entry->accel_path, entry->accel_key, entry->accel_mods, changed);
731     skip_accel:
732       /* noop */;
733     }
734   g_slist_free (entries);
735 }
736
737 /**
738  * gtk_accel_map_foreach_unfiltered:
739  * @data:         data to be passed into @foreach_func
740  * @foreach_func: function to be executed for each accel map entry
741  *
742  * Loops over all entries in the accelerator map, and execute
743  * @foreach_func on each. The signature of @foreach_func is that of
744  * #GtkAccelMapForeach, the @changed parameter indicates whether
745  * this accelerator was changed during runtime (thus, would need
746  * saving during an accelerator map dump).
747  */
748 void
749 gtk_accel_map_foreach_unfiltered (gpointer           data,
750                                   GtkAccelMapForeach foreach_func)
751 {
752   GSList *entries, *slist;
753
754   g_return_if_fail (foreach_func != NULL);
755
756   entries = g_hash_table_slist_values (accel_entry_ht);
757   for (slist = entries; slist; slist = slist->next)
758     {
759       AccelEntry *entry = slist->data;
760       gboolean changed = entry->accel_key != entry->std_accel_key || entry->accel_mods != entry->std_accel_mods;
761
762       foreach_func (data, entry->accel_path, entry->accel_key, entry->accel_mods, changed);
763     }
764   g_slist_free (entries);
765 }
766
767 /**
768  * gtk_accel_map_add_filter:
769  * @filter_pattern: a pattern (see #GPatternSpec)
770  *
771  * Adds a filter to the global list of accel path filters.
772  *
773  * Accel map entries whose accel path matches one of the filters
774  * are skipped by gtk_accel_map_foreach().
775  *
776  * This function is intended for GTK+ modules that create their own
777  * menus, but don't want them to be saved into the applications accelerator
778  * map dump.
779  */
780 void
781 gtk_accel_map_add_filter (const gchar *filter_pattern)
782 {
783   GPatternSpec *pspec;
784   GSList *slist;
785
786   g_return_if_fail (filter_pattern != NULL);
787
788   pspec = g_pattern_spec_new (filter_pattern);
789   for (slist = accel_filters; slist; slist = slist->next)
790     if (g_pattern_spec_equal (pspec, slist->data))
791       {
792         g_pattern_spec_free (pspec);
793         return;
794       }
795   accel_filters = g_slist_prepend (accel_filters, pspec);
796 }
797
798 void
799 _gtk_accel_map_add_group (const gchar   *accel_path,
800                           GtkAccelGroup *accel_group)
801 {
802   AccelEntry *entry;
803
804   g_return_if_fail (_gtk_accel_path_is_valid (accel_path));
805   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
806
807   entry = accel_path_lookup (accel_path);
808   if (!entry)
809     {
810       gtk_accel_map_add_entry (accel_path, 0, 0);
811       entry = accel_path_lookup (accel_path);
812     }
813   entry->groups = g_slist_prepend (entry->groups, accel_group);
814 }
815
816 void
817 _gtk_accel_map_remove_group (const gchar   *accel_path,
818                              GtkAccelGroup *accel_group)
819 {
820   AccelEntry *entry;
821
822   entry = accel_path_lookup (accel_path);
823   g_return_if_fail (entry != NULL);
824   g_return_if_fail (g_slist_find (entry->groups, accel_group));
825
826   entry->groups = g_slist_remove (entry->groups, accel_group);
827 }