]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - misc/mediasource/extra/netradiant-src/libs/gtkutil/accelerator.cpp
Rename the compiled fteqcc to fteqcc-win32 (as that's what it is)
[voretournament/voretournament.git] / misc / mediasource / extra / netradiant-src / libs / gtkutil / accelerator.cpp
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "accelerator.h"
23
24 #include "debugging/debugging.h"
25
26 #include <map>
27 #include <set>
28 #include <gtk/gtkwindow.h>
29 #include <gtk/gtkaccelgroup.h>
30
31 #include "generic/callback.h"
32 #include "generic/bitfield.h"
33 #include "string/string.h"
34
35 #include "pointer.h"
36 #include "closure.h"
37
38 #include <gdk/gdkkeysyms.h>
39
40
41
42 const char* global_keys_find(unsigned int key)
43 {
44   const char *s;
45   if(key == 0)
46     return "";
47   s = gdk_keyval_name(key);
48   if(!s)
49         return "";
50   return s;
51 }
52
53 unsigned int global_keys_find(const char* name)
54 {
55   guint k;
56   if(!name || !*name)
57     return 0;
58   k = gdk_keyval_from_name(name);
59   if(k == GDK_VoidSymbol)
60         return 0;
61   return k;
62 }
63
64 void accelerator_write(const Accelerator& accelerator, TextOutputStream& ostream)
65 {
66 #if 0
67   if(accelerator.modifiers & GDK_SHIFT_MASK)
68   {
69     ostream << "Shift + ";
70   }
71   if(accelerator.modifiers & GDK_MOD1_MASK)
72   {
73     ostream << "Alt + ";
74   }
75   if(accelerator.modifiers & GDK_CONTROL_MASK)
76   {
77     ostream << "Control + ";
78   }
79
80   const char* keyName = global_keys_find(accelerator.key);
81   if(!string_empty(keyName))
82   {
83     ostream << keyName;
84   }
85   else
86   {
87     ostream << static_cast<char>(accelerator.key);
88   }
89 #endif
90   ostream << gtk_accelerator_get_label(accelerator.key, accelerator.modifiers);
91 }
92
93 typedef std::map<Accelerator, Callback> AcceleratorMap;
94 typedef std::set<Accelerator> AcceleratorSet;
95
96 bool accelerator_map_insert(AcceleratorMap& acceleratorMap, Accelerator accelerator, const Callback& callback)
97 {
98   if(accelerator.key != 0)
99   {
100     return acceleratorMap.insert(AcceleratorMap::value_type(accelerator, callback)).second;
101   }
102   return true;
103 }
104
105 bool accelerator_map_erase(AcceleratorMap& acceleratorMap, Accelerator accelerator)
106 {
107   if(accelerator.key != 0)
108   {
109     AcceleratorMap::iterator i = acceleratorMap.find(accelerator);
110     if(i == acceleratorMap.end())
111     {
112       return false;
113     }
114     acceleratorMap.erase(i);
115   }
116   return true;
117 }
118
119 Accelerator accelerator_for_event_key(guint keyval, guint state)
120 {
121   keyval = gdk_keyval_to_upper(keyval);
122   if(keyval == GDK_ISO_Left_Tab)
123     keyval = GDK_Tab;
124   return Accelerator(keyval, (GdkModifierType)(state & gtk_accelerator_get_default_mod_mask()));
125 }
126
127 bool AcceleratorMap_activate(const AcceleratorMap& acceleratorMap, const Accelerator& accelerator)
128 {
129   AcceleratorMap::const_iterator i = acceleratorMap.find(accelerator);
130   if(i != acceleratorMap.end())
131   {
132     (*i).second();
133     return true;
134   }
135
136   return false;
137 }
138
139 static gboolean accelerator_key_event(GtkWindow* window, GdkEventKey* event, AcceleratorMap* acceleratorMap)
140 {
141   return AcceleratorMap_activate(*acceleratorMap, accelerator_for_event_key(event->keyval, event->state));
142 }
143
144
145 AcceleratorMap g_special_accelerators;
146
147
148 namespace MouseButton
149 {
150   enum 
151   {
152     Left = 1 << 0,
153     Right = 1 << 1,
154     Middle = 1 << 2,
155   };
156 }
157
158 typedef unsigned int ButtonMask;
159
160 void print_buttons(ButtonMask mask)
161 {
162   globalOutputStream() << "button state: ";
163   if((mask & MouseButton::Left) != 0)
164   {
165     globalOutputStream() << "Left ";
166   }
167   if((mask & MouseButton::Right) != 0)
168   {
169     globalOutputStream() << "Right ";
170   }
171   if((mask & MouseButton::Middle) != 0)
172   {
173     globalOutputStream() << "Middle ";
174   }
175   globalOutputStream() << "\n";
176 }
177
178 ButtonMask ButtonMask_for_event_button(guint button)
179 {
180   switch(button)
181   {
182   case 1:
183     return MouseButton::Left;
184   case 2:
185     return MouseButton::Middle;
186   case 3:
187     return MouseButton::Right;
188   }
189   return 0;
190 }
191
192 bool window_has_accel(GtkWindow* toplevel)
193 {
194   return g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) != 0;
195 }
196
197 namespace
198 {
199   bool g_accel_enabled = true;
200 }
201
202 bool global_accel_enabled()
203 {
204   return g_accel_enabled;
205 }
206
207
208 GClosure* accel_group_add_accelerator(GtkAccelGroup* group, Accelerator accelerator, const Callback& callback);
209 void accel_group_remove_accelerator(GtkAccelGroup* group, Accelerator accelerator);
210
211 AcceleratorMap g_queuedAcceleratorsAdd;
212 AcceleratorSet g_queuedAcceleratorsRemove;
213
214 void globalQueuedAccelerators_add(Accelerator accelerator, const Callback& callback)
215 {
216   if(!g_queuedAcceleratorsAdd.insert(AcceleratorMap::value_type(accelerator, callback)).second)
217   {
218     globalErrorStream() << "globalQueuedAccelerators_add: accelerator already queued: " << accelerator << "\n";
219   }
220 }
221
222 void globalQueuedAccelerators_remove(Accelerator accelerator)
223 {
224   if(g_queuedAcceleratorsAdd.erase(accelerator) == 0)
225   {
226     if(!g_queuedAcceleratorsRemove.insert(accelerator).second)
227     {
228       globalErrorStream() << "globalQueuedAccelerators_remove: accelerator already queued: " << accelerator << "\n";
229     }
230   }
231 }
232
233 void globalQueuedAccelerators_commit()
234 {
235   for(AcceleratorSet::const_iterator i = g_queuedAcceleratorsRemove.begin(); i != g_queuedAcceleratorsRemove.end(); ++i)
236   {
237     //globalOutputStream() << "removing: " << (*i).first << "\n";
238     accel_group_remove_accelerator(global_accel, *i);
239   }
240   g_queuedAcceleratorsRemove.clear();
241   for(AcceleratorMap::const_iterator i = g_queuedAcceleratorsAdd.begin(); i != g_queuedAcceleratorsAdd.end(); ++i)
242   {
243     //globalOutputStream() << "adding: " << (*i).first << "\n";
244     accel_group_add_accelerator(global_accel, (*i).first, (*i).second);
245   }
246   g_queuedAcceleratorsAdd.clear();
247 }
248
249 void accel_group_test(GtkWindow* toplevel, GtkAccelGroup* accel)
250 {
251   guint n_entries;
252   gtk_accel_group_query(accel, '4', (GdkModifierType)0, &n_entries);
253   globalOutputStream() << "grid4: " << n_entries << "\n";
254   globalOutputStream() << "toplevel accelgroups: " << g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) << "\n";
255 }
256
257 typedef std::set<GtkWindow*> WindowSet;
258 WindowSet g_accel_windows;
259
260 bool Buttons_press(ButtonMask& buttons, guint button, guint state)
261 {
262   if(buttons == 0 && bitfield_enable(buttons, ButtonMask_for_event_button(button)) != 0)
263   {
264     ASSERT_MESSAGE(g_accel_enabled, "Buttons_press: accelerators not enabled");
265     g_accel_enabled = false;
266     for(WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i)
267     {
268       GtkWindow* toplevel = *i;
269       ASSERT_MESSAGE(window_has_accel(toplevel), "ERROR");
270       ASSERT_MESSAGE(GTK_WIDGET_TOPLEVEL(toplevel), "disabling accel for non-toplevel window");
271       gtk_window_remove_accel_group(toplevel,  global_accel);
272 #if 0
273       globalOutputStream() << reinterpret_cast<unsigned int>(toplevel) << ": disabled global accelerators\n";
274 #endif
275 #if 0
276       accel_group_test(toplevel, global_accel);
277 #endif
278     }
279   }
280   buttons = bitfield_enable(buttons, ButtonMask_for_event_button(button));
281 #if 0
282   globalOutputStream() << "Buttons_press: ";
283   print_buttons(buttons);
284 #endif
285   return false;
286 }
287
288 bool Buttons_release(ButtonMask& buttons, guint button, guint state)
289 {
290   if(buttons != 0 && bitfield_disable(buttons, ButtonMask_for_event_button(button)) == 0)
291   {
292     ASSERT_MESSAGE(!g_accel_enabled, "Buttons_release: accelerators are enabled");
293     g_accel_enabled = true;
294     for(WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i)
295     {
296       GtkWindow* toplevel = *i;
297       ASSERT_MESSAGE(!window_has_accel(toplevel), "ERROR");
298       ASSERT_MESSAGE(GTK_WIDGET_TOPLEVEL(toplevel), "enabling accel for non-toplevel window");
299       gtk_window_add_accel_group(toplevel, global_accel);
300 #if 0
301       globalOutputStream() << reinterpret_cast<unsigned int>(toplevel) << ": enabled global accelerators\n";
302 #endif
303 #if 0
304       accel_group_test(toplevel, global_accel);
305 #endif
306     }
307     globalQueuedAccelerators_commit();
308   }
309   buttons = bitfield_disable(buttons, ButtonMask_for_event_button(button));
310 #if 0
311   globalOutputStream() << "Buttons_release: ";
312   print_buttons(buttons);
313 #endif
314   return false;
315 }
316
317 bool Buttons_releaseAll(ButtonMask& buttons)
318 {
319   Buttons_release(buttons, MouseButton::Left | MouseButton::Middle | MouseButton::Right, 0);
320   return false;
321 }
322
323 struct PressedButtons
324 {
325   ButtonMask buttons;
326
327   PressedButtons() : buttons(0)
328   {
329   }
330 };
331
332 gboolean PressedButtons_button_press(GtkWidget* widget, GdkEventButton* event, PressedButtons* pressed)
333 {
334   if(event->type == GDK_BUTTON_PRESS)
335   {
336     return Buttons_press(pressed->buttons, event->button, event->state);
337   }
338   return FALSE;
339 }
340
341 gboolean PressedButtons_button_release(GtkWidget* widget, GdkEventButton* event, PressedButtons* pressed)
342 {
343   if(event->type == GDK_BUTTON_RELEASE)
344   {
345     return Buttons_release(pressed->buttons, event->button, event->state);
346   }
347   return FALSE;
348 }
349
350 gboolean PressedButtons_focus_out(GtkWidget* widget, GdkEventFocus* event, PressedButtons* pressed)
351 {
352   Buttons_releaseAll(pressed->buttons);
353   return FALSE;
354 }
355
356 void PressedButtons_connect(PressedButtons& pressedButtons, GtkWidget* widget)
357 {
358   g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(PressedButtons_button_press), &pressedButtons);
359   g_signal_connect(G_OBJECT(widget), "button_release_event", G_CALLBACK(PressedButtons_button_release), &pressedButtons);
360   g_signal_connect(G_OBJECT(widget), "focus_out_event", G_CALLBACK(PressedButtons_focus_out), &pressedButtons);
361 }
362
363 PressedButtons g_pressedButtons;
364
365
366 #include <set>
367
368 struct PressedKeys
369 {
370   typedef std::set<guint> Keys;
371   Keys keys;
372   std::size_t refcount;
373
374   PressedKeys() : refcount(0)
375   {
376   }
377 };
378
379 AcceleratorMap g_keydown_accelerators;
380 AcceleratorMap g_keyup_accelerators;
381
382 bool Keys_press(PressedKeys::Keys& keys, guint keyval)
383 {
384   if(keys.insert(keyval).second)
385   {
386     return AcceleratorMap_activate(g_keydown_accelerators, accelerator_for_event_key(keyval, 0));
387   }
388   return g_keydown_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keydown_accelerators.end();
389 }
390
391 bool Keys_release(PressedKeys::Keys& keys, guint keyval)
392 {
393   if(keys.erase(keyval) != 0)
394   {
395     return AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(keyval, 0));
396   }
397   return g_keyup_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keyup_accelerators.end();
398 }
399
400 void Keys_releaseAll(PressedKeys::Keys& keys, guint state)
401 {
402   for(PressedKeys::Keys::iterator i = keys.begin(); i != keys.end(); ++i)
403   {
404     AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(*i, state));
405   }
406   keys.clear();
407 }
408
409 gboolean PressedKeys_key_press(GtkWidget* widget, GdkEventKey* event, PressedKeys* pressedKeys)
410 {
411   //globalOutputStream() << "pressed: " << event->keyval << "\n";
412   return event->state == 0 && Keys_press(pressedKeys->keys, event->keyval);
413 }
414
415 gboolean PressedKeys_key_release(GtkWidget* widget, GdkEventKey* event, PressedKeys* pressedKeys)
416 {
417   //globalOutputStream() << "released: " << event->keyval << "\n";
418   return Keys_release(pressedKeys->keys, event->keyval);
419 }
420
421 gboolean PressedKeys_focus_in(GtkWidget* widget, GdkEventFocus* event, PressedKeys* pressedKeys)
422 {
423   ++pressedKeys->refcount;
424   return FALSE;
425 }
426
427 gboolean PressedKeys_focus_out(GtkWidget* widget, GdkEventFocus* event, PressedKeys* pressedKeys)
428 {
429   if(--pressedKeys->refcount == 0)
430   {
431     Keys_releaseAll(pressedKeys->keys, 0);
432   }
433   return FALSE;
434 }
435
436 PressedKeys g_pressedKeys;
437
438 void GlobalPressedKeys_releaseAll()
439 {
440   Keys_releaseAll(g_pressedKeys.keys, 0);
441 }
442
443 void GlobalPressedKeys_connect(GtkWindow* window)
444 {
445   unsigned int key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(PressedKeys_key_press), &g_pressedKeys);
446   unsigned int key_release_handler = g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(PressedKeys_key_release), &g_pressedKeys);
447   g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler));
448   g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler));
449   unsigned int focus_in_handler = g_signal_connect(G_OBJECT(window), "focus_in_event", G_CALLBACK(PressedKeys_focus_in), &g_pressedKeys);
450   unsigned int focus_out_handler = g_signal_connect(G_OBJECT(window), "focus_out_event", G_CALLBACK(PressedKeys_focus_out), &g_pressedKeys);
451   g_object_set_data(G_OBJECT(window), "focus_in_handler", gint_to_pointer(focus_in_handler));
452   g_object_set_data(G_OBJECT(window), "focus_out_handler", gint_to_pointer(focus_out_handler));
453 }
454
455 void GlobalPressedKeys_disconnect(GtkWindow* window)
456 {
457   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler")));
458   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler")));
459   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_in_handler")));
460   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_out_handler")));
461 }
462
463
464
465 void special_accelerators_add(Accelerator accelerator, const Callback& callback)
466 {
467   //globalOutputStream() << "special_accelerators_add: " << makeQuoted(accelerator) << "\n";
468   if(!accelerator_map_insert(g_special_accelerators, accelerator, callback))
469   {
470     globalErrorStream() << "special_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
471   }
472 }
473 void special_accelerators_remove(Accelerator accelerator)
474 {
475   //globalOutputStream() << "special_accelerators_remove: " << makeQuoted(accelerator) << "\n";
476   if(!accelerator_map_erase(g_special_accelerators, accelerator))
477   {
478     globalErrorStream() << "special_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
479   }
480 }
481
482 void keydown_accelerators_add(Accelerator accelerator, const Callback& callback)
483 {
484   //globalOutputStream() << "keydown_accelerators_add: " << makeQuoted(accelerator) << "\n";
485   if(!accelerator_map_insert(g_keydown_accelerators, accelerator, callback))
486   {
487     globalErrorStream() << "keydown_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
488   }
489 }
490 void keydown_accelerators_remove(Accelerator accelerator)
491 {
492   //globalOutputStream() << "keydown_accelerators_remove: " << makeQuoted(accelerator) << "\n";
493   if(!accelerator_map_erase(g_keydown_accelerators, accelerator))
494   {
495     globalErrorStream() << "keydown_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
496   }
497 }
498
499 void keyup_accelerators_add(Accelerator accelerator, const Callback& callback)
500 {
501   //globalOutputStream() << "keyup_accelerators_add: " << makeQuoted(accelerator) << "\n";
502   if(!accelerator_map_insert(g_keyup_accelerators, accelerator, callback))
503   {
504     globalErrorStream() << "keyup_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
505   }
506 }
507 void keyup_accelerators_remove(Accelerator accelerator)
508 {
509   //globalOutputStream() << "keyup_accelerators_remove: " << makeQuoted(accelerator) << "\n";
510   if(!accelerator_map_erase(g_keyup_accelerators, accelerator))
511   {
512     globalErrorStream() << "keyup_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
513   }
514 }
515
516
517 gboolean accel_closure_callback(GtkAccelGroup* group, GtkWidget* widget, guint key, GdkModifierType modifiers, gpointer data)
518 {
519   (*reinterpret_cast<Callback*>(data))();
520   return TRUE;
521 }
522
523 GClosure* accel_group_add_accelerator(GtkAccelGroup* group, Accelerator accelerator, const Callback& callback)
524 {
525   if(accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers))
526   {
527     //globalOutputStream() << "global_accel_connect: " << makeQuoted(accelerator) << "\n";
528     GClosure* closure = create_cclosure(G_CALLBACK(accel_closure_callback), callback);
529     gtk_accel_group_connect(group, accelerator.key, accelerator.modifiers, GTK_ACCEL_VISIBLE, closure);
530     return closure;
531   }
532   else
533   {
534     special_accelerators_add(accelerator, callback);
535     return 0;
536   }
537 }
538
539 void accel_group_remove_accelerator(GtkAccelGroup* group, Accelerator accelerator)
540 {
541   if(accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers))
542   {
543     //globalOutputStream() << "global_accel_disconnect: " << makeQuoted(accelerator) << "\n";
544     gtk_accel_group_disconnect_key(group, accelerator.key, accelerator.modifiers);
545   }
546   else
547   {
548     special_accelerators_remove(accelerator);
549   }
550 }
551
552 GtkAccelGroup* global_accel = 0;
553
554 void global_accel_init()
555 {
556   global_accel = gtk_accel_group_new();
557 }
558
559 void global_accel_destroy()
560 {
561   g_object_unref(global_accel);
562 }
563
564 GClosure* global_accel_group_add_accelerator(Accelerator accelerator, const Callback& callback)
565 {
566   if(!global_accel_enabled())
567   {
568     // workaround: cannot add to GtkAccelGroup while it is disabled
569     //globalOutputStream() << "queued for add: " << accelerator << "\n";
570     globalQueuedAccelerators_add(accelerator, callback);
571     return 0;
572   }
573   return accel_group_add_accelerator(global_accel, accelerator, callback);
574 }
575 void global_accel_group_remove_accelerator(Accelerator accelerator)
576 {
577   if(!global_accel_enabled())
578   {
579     //globalOutputStream() << "queued for remove: " << accelerator << "\n";
580     globalQueuedAccelerators_remove(accelerator);
581     return;
582   }
583   accel_group_remove_accelerator(global_accel, accelerator);
584 }
585
586 /// \brief Propagates key events to the focus-widget, overriding global accelerators.
587 static gboolean override_global_accelerators(GtkWindow* window, GdkEventKey* event, gpointer data)
588 {
589   gboolean b = gtk_window_propagate_key_event(window, event);
590   return b;
591 }
592
593 void global_accel_connect_window(GtkWindow* window)
594 {
595 #if 1
596   unsigned int override_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(override_global_accelerators), 0);
597   g_object_set_data(G_OBJECT(window), "override_handler", gint_to_pointer(override_handler));
598
599   unsigned int special_key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(accelerator_key_event), &g_special_accelerators);
600   g_object_set_data(G_OBJECT(window), "special_key_press_handler", gint_to_pointer(special_key_press_handler));
601
602   GlobalPressedKeys_connect(window);
603 #else
604   unsigned int key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(accelerator_key_event), &g_keydown_accelerators);
605   unsigned int key_release_handler = g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(accelerator_key_event), &g_keyup_accelerators);
606   g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler));
607   g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler));
608 #endif
609   g_accel_windows.insert(window);
610   gtk_window_add_accel_group(window, global_accel);
611 }
612 void global_accel_disconnect_window(GtkWindow* window)
613 {
614 #if 1
615   GlobalPressedKeys_disconnect(window);
616
617   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "override_handler")));
618   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "special_key_press_handler")));
619 #else
620   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler")));
621   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler")));
622 #endif
623   gtk_window_remove_accel_group(window, global_accel);
624   std::size_t count = g_accel_windows.erase(window);
625   ASSERT_MESSAGE(count == 1, "failed to remove accel group\n");
626 }
627
628
629 GClosure* global_accel_group_find(Accelerator accelerator)
630 {
631   guint numEntries = 0;
632   GtkAccelGroupEntry* entry = gtk_accel_group_query(global_accel, accelerator.key, accelerator.modifiers, &numEntries);
633   if(numEntries != 0)
634   {
635     if(numEntries != 1)
636     {
637       char* name = gtk_accelerator_name(accelerator.key, accelerator.modifiers);
638       globalErrorStream() << "accelerator already in-use: " << name << "\n";
639       g_free(name);
640     }
641     return entry->closure;
642   }
643   return 0;
644 }
645
646 void global_accel_group_connect(const Accelerator& accelerator, const Callback& callback)
647 {
648   if(accelerator.key != 0)
649   {
650     global_accel_group_add_accelerator(accelerator, callback);
651   }
652 }
653
654 void global_accel_group_disconnect(const Accelerator& accelerator, const Callback& callback)
655 {
656   if(accelerator.key != 0)
657   {
658     global_accel_group_remove_accelerator(accelerator);
659   }
660 }
661
662