fixed camera freemove shortcuts bug
[xonotic/netradiant.git] / 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 struct SKeyInfo
43 {
44   const char* m_strName;
45   unsigned int m_nVKKey;
46 };
47
48 SKeyInfo g_Keys[] =
49 {
50   {"Space", GDK_space},
51   {"Backspace", GDK_BackSpace},
52   {"Escape", GDK_Escape},
53   {"End", GDK_End},
54   {"Insert", GDK_Insert},
55   {"Delete", GDK_Delete},
56   {"PageUp", GDK_Prior},
57   {"PageDown", GDK_Next},
58   {"Up", GDK_Up},
59   {"Down", GDK_Down},
60   {"Left", GDK_Left},
61   {"Right", GDK_Right},
62   {"F1", GDK_F1},
63   {"F2", GDK_F2},
64   {"F3", GDK_F3},
65   {"F4", GDK_F4},
66   {"F5", GDK_F5},
67   {"F6", GDK_F6},
68   {"F7", GDK_F7},
69   {"F8", GDK_F8},
70   {"F9", GDK_F9},
71   {"F10", GDK_F10},
72   {"F11", GDK_F11},
73   {"F12", GDK_F12},
74   {"Tab", GDK_Tab},
75   {"Return", GDK_Return},                           
76   {"Comma", GDK_comma},
77   {"Period", GDK_period},
78   {"Plus", GDK_KP_Add},
79   {"Multiply", GDK_multiply},
80   {"Minus", GDK_KP_Subtract},
81   {"NumPad0", GDK_KP_0},
82   {"NumPad1", GDK_KP_1},
83   {"NumPad2", GDK_KP_2},
84   {"NumPad3", GDK_KP_3},
85   {"NumPad4", GDK_KP_4},
86   {"NumPad5", GDK_KP_5},
87   {"NumPad6", GDK_KP_6},
88   {"NumPad7", GDK_KP_7},
89   {"NumPad8", GDK_KP_8},
90   {"NumPad9", GDK_KP_9},
91   {"[", 219},
92   {"]", 221},
93   {"\\", 220},
94   {"Home", GDK_Home}
95 };
96
97 int g_nKeyCount = sizeof(g_Keys) / sizeof(SKeyInfo);
98
99 const char* global_keys_find(unsigned int key)
100 {
101   for(int i = 0; i < g_nKeyCount; ++i)
102   {
103     if(g_Keys[i].m_nVKKey == key)
104     {
105       return g_Keys[i].m_strName;
106     }
107   }
108   return "";
109 }
110
111 unsigned int global_keys_find(const char* name)
112 {
113   for(int i = 0; i < g_nKeyCount; ++i)
114   {
115     if(string_equal_nocase(g_Keys[i].m_strName, name))
116     {
117       return g_Keys[i].m_nVKKey;
118     }
119   }
120   return 0;
121 }
122
123 void accelerator_write(const Accelerator& accelerator, TextOutputStream& ostream)
124 {
125   if(accelerator.modifiers & GDK_SHIFT_MASK)
126   {
127     ostream << "Shift + ";
128   }
129   if(accelerator.modifiers & GDK_MOD1_MASK)
130   {
131     ostream << "Alt + ";
132   }
133   if(accelerator.modifiers & GDK_CONTROL_MASK)
134   {
135     ostream << "Control + ";
136   }
137
138   const char* keyName = global_keys_find(accelerator.key);
139   if(!string_empty(keyName))
140   {
141     ostream << keyName;
142   }
143   else
144   {
145     ostream << static_cast<char>(accelerator.key);
146   }
147 }
148
149 typedef std::map<Accelerator, Callback> AcceleratorMap;
150 typedef std::set<Accelerator> AcceleratorSet;
151
152 bool accelerator_map_insert(AcceleratorMap& acceleratorMap, Accelerator accelerator, const Callback& callback)
153 {
154   if(accelerator.key != 0)
155   {
156     return acceleratorMap.insert(AcceleratorMap::value_type(accelerator, callback)).second;
157   }
158   return true;
159 }
160
161 bool accelerator_map_erase(AcceleratorMap& acceleratorMap, Accelerator accelerator)
162 {
163   if(accelerator.key != 0)
164   {
165     AcceleratorMap::iterator i = acceleratorMap.find(accelerator);
166     if(i == acceleratorMap.end())
167     {
168       return false;
169     }
170     acceleratorMap.erase(i);
171   }
172   return true;
173 }
174
175 Accelerator accelerator_for_event_key(guint keyval, guint state)
176 {
177   keyval = gdk_keyval_to_upper(keyval);
178   if(keyval == GDK_ISO_Left_Tab)
179     keyval = GDK_Tab;
180   return Accelerator(keyval, (GdkModifierType)(state & gtk_accelerator_get_default_mod_mask()));
181 }
182
183 bool AcceleratorMap_activate(const AcceleratorMap& acceleratorMap, const Accelerator& accelerator)
184 {
185   AcceleratorMap::const_iterator i = acceleratorMap.find(accelerator);
186   if(i != acceleratorMap.end())
187   {
188     (*i).second();
189     return true;
190   }
191
192   return false;
193 }
194
195 static gboolean accelerator_key_event(GtkWindow* window, GdkEventKey* event, AcceleratorMap* acceleratorMap)
196 {
197   return AcceleratorMap_activate(*acceleratorMap, accelerator_for_event_key(event->keyval, event->state));
198 }
199
200
201 AcceleratorMap g_special_accelerators;
202
203
204 namespace MouseButton
205 {
206   enum 
207   {
208     Left = 1 << 0,
209     Right = 1 << 1,
210     Middle = 1 << 2,
211   };
212 }
213
214 typedef unsigned int ButtonMask;
215
216 void print_buttons(ButtonMask mask)
217 {
218   globalOutputStream() << "button state: ";
219   if((mask & MouseButton::Left) != 0)
220   {
221     globalOutputStream() << "Left ";
222   }
223   if((mask & MouseButton::Right) != 0)
224   {
225     globalOutputStream() << "Right ";
226   }
227   if((mask & MouseButton::Middle) != 0)
228   {
229     globalOutputStream() << "Middle ";
230   }
231   globalOutputStream() << "\n";
232 }
233
234 ButtonMask ButtonMask_for_event_button(guint button)
235 {
236   switch(button)
237   {
238   case 1:
239     return MouseButton::Left;
240   case 2:
241     return MouseButton::Middle;
242   case 3:
243     return MouseButton::Right;
244   }
245   return 0;
246 }
247
248 bool window_has_accel(GtkWindow* toplevel)
249 {
250   return g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) != 0;
251 }
252
253 namespace
254 {
255   bool g_accel_enabled = true;
256 }
257
258 bool global_accel_enabled()
259 {
260   return g_accel_enabled;
261 }
262
263
264 GClosure* accel_group_add_accelerator(GtkAccelGroup* group, Accelerator accelerator, const Callback& callback);
265 void accel_group_remove_accelerator(GtkAccelGroup* group, Accelerator accelerator);
266
267 AcceleratorMap g_queuedAcceleratorsAdd;
268 AcceleratorSet g_queuedAcceleratorsRemove;
269
270 void globalQueuedAccelerators_add(Accelerator accelerator, const Callback& callback)
271 {
272   if(!g_queuedAcceleratorsAdd.insert(AcceleratorMap::value_type(accelerator, callback)).second)
273   {
274     globalErrorStream() << "globalQueuedAccelerators_add: accelerator already queued: " << accelerator << "\n";
275   }
276 }
277
278 void globalQueuedAccelerators_remove(Accelerator accelerator)
279 {
280   if(g_queuedAcceleratorsAdd.erase(accelerator) == 0)
281   {
282     if(!g_queuedAcceleratorsRemove.insert(accelerator).second)
283     {
284       globalErrorStream() << "globalQueuedAccelerators_remove: accelerator already queued: " << accelerator << "\n";
285     }
286   }
287 }
288
289 void globalQueuedAccelerators_commit()
290 {
291   for(AcceleratorSet::const_iterator i = g_queuedAcceleratorsRemove.begin(); i != g_queuedAcceleratorsRemove.end(); ++i)
292   {
293     //globalOutputStream() << "removing: " << (*i).first << "\n";
294     accel_group_remove_accelerator(global_accel, *i);
295   }
296   g_queuedAcceleratorsRemove.clear();
297   for(AcceleratorMap::const_iterator i = g_queuedAcceleratorsAdd.begin(); i != g_queuedAcceleratorsAdd.end(); ++i)
298   {
299     //globalOutputStream() << "adding: " << (*i).first << "\n";
300     accel_group_add_accelerator(global_accel, (*i).first, (*i).second);
301   }
302   g_queuedAcceleratorsAdd.clear();
303 }
304
305 void accel_group_test(GtkWindow* toplevel, GtkAccelGroup* accel)
306 {
307   guint n_entries;
308   gtk_accel_group_query(accel, '4', (GdkModifierType)0, &n_entries);
309   globalOutputStream() << "grid4: " << n_entries << "\n";
310   globalOutputStream() << "toplevel accelgroups: " << g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) << "\n";
311 }
312
313 typedef std::set<GtkWindow*> WindowSet;
314 WindowSet g_accel_windows;
315
316 bool Buttons_press(ButtonMask& buttons, guint button, guint state)
317 {
318   if(buttons == 0 && bitfield_enable(buttons, ButtonMask_for_event_button(button)) != 0)
319   {
320     ASSERT_MESSAGE(g_accel_enabled, "Buttons_press: accelerators not enabled");
321     g_accel_enabled = false;
322     for(WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i)
323     {
324       GtkWindow* toplevel = *i;
325       ASSERT_MESSAGE(window_has_accel(toplevel), "ERROR");
326       ASSERT_MESSAGE(GTK_WIDGET_TOPLEVEL(toplevel), "disabling accel for non-toplevel window");
327       gtk_window_remove_accel_group(toplevel,  global_accel);
328 #if 0
329       globalOutputStream() << reinterpret_cast<unsigned int>(toplevel) << ": disabled global accelerators\n";
330 #endif
331 #if 0
332       accel_group_test(toplevel, global_accel);
333 #endif
334     }
335   }
336   buttons = bitfield_enable(buttons, ButtonMask_for_event_button(button));
337 #if 0
338   globalOutputStream() << "Buttons_press: ";
339   print_buttons(buttons);
340 #endif
341   return false;
342 }
343
344 bool Buttons_release(ButtonMask& buttons, guint button, guint state)
345 {
346   if(buttons != 0 && bitfield_disable(buttons, ButtonMask_for_event_button(button)) == 0)
347   {
348     ASSERT_MESSAGE(!g_accel_enabled, "Buttons_release: accelerators are enabled");
349     g_accel_enabled = true;
350     for(WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i)
351     {
352       GtkWindow* toplevel = *i;
353       ASSERT_MESSAGE(!window_has_accel(toplevel), "ERROR");
354       ASSERT_MESSAGE(GTK_WIDGET_TOPLEVEL(toplevel), "enabling accel for non-toplevel window");
355       gtk_window_add_accel_group(toplevel, global_accel);
356 #if 0
357       globalOutputStream() << reinterpret_cast<unsigned int>(toplevel) << ": enabled global accelerators\n";
358 #endif
359 #if 0
360       accel_group_test(toplevel, global_accel);
361 #endif
362     }
363     globalQueuedAccelerators_commit();
364   }
365   buttons = bitfield_disable(buttons, ButtonMask_for_event_button(button));
366 #if 0
367   globalOutputStream() << "Buttons_release: ";
368   print_buttons(buttons);
369 #endif
370   return false;
371 }
372
373 bool Buttons_releaseAll(ButtonMask& buttons)
374 {
375   Buttons_release(buttons, MouseButton::Left | MouseButton::Middle | MouseButton::Right, 0);
376   return false;
377 }
378
379 struct PressedButtons
380 {
381   ButtonMask buttons;
382
383   PressedButtons() : buttons(0)
384   {
385   }
386 };
387
388 gboolean PressedButtons_button_press(GtkWidget* widget, GdkEventButton* event, PressedButtons* pressed)
389 {
390   if(event->type == GDK_BUTTON_PRESS)
391   {
392     return Buttons_press(pressed->buttons, event->button, event->state);
393   }
394   return FALSE;
395 }
396
397 gboolean PressedButtons_button_release(GtkWidget* widget, GdkEventButton* event, PressedButtons* pressed)
398 {
399   if(event->type == GDK_BUTTON_RELEASE)
400   {
401     return Buttons_release(pressed->buttons, event->button, event->state);
402   }
403   return FALSE;
404 }
405
406 gboolean PressedButtons_focus_out(GtkWidget* widget, GdkEventFocus* event, PressedButtons* pressed)
407 {
408   Buttons_releaseAll(pressed->buttons);
409   return FALSE;
410 }
411
412 void PressedButtons_connect(PressedButtons& pressedButtons, GtkWidget* widget)
413 {
414   g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(PressedButtons_button_press), &pressedButtons);
415   g_signal_connect(G_OBJECT(widget), "button_release_event", G_CALLBACK(PressedButtons_button_release), &pressedButtons);
416   g_signal_connect(G_OBJECT(widget), "focus_out_event", G_CALLBACK(PressedButtons_focus_out), &pressedButtons);
417 }
418
419 PressedButtons g_pressedButtons;
420
421
422 #include <set>
423
424 struct PressedKeys
425 {
426   typedef std::set<guint> Keys;
427   Keys keys;
428   std::size_t refcount;
429
430   PressedKeys() : refcount(0)
431   {
432   }
433 };
434
435 AcceleratorMap g_keydown_accelerators;
436 AcceleratorMap g_keyup_accelerators;
437
438 bool Keys_press(PressedKeys::Keys& keys, guint keyval)
439 {
440   if(keys.insert(keyval).second)
441   {
442     return AcceleratorMap_activate(g_keydown_accelerators, accelerator_for_event_key(keyval, 0));
443   }
444   return g_keydown_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keydown_accelerators.end();
445 }
446
447 bool Keys_release(PressedKeys::Keys& keys, guint keyval)
448 {
449   if(keys.erase(keyval) != 0)
450   {
451     return AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(keyval, 0));
452   }
453   return g_keyup_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keyup_accelerators.end();
454 }
455
456 void Keys_releaseAll(PressedKeys::Keys& keys, guint state)
457 {
458   for(PressedKeys::Keys::iterator i = keys.begin(); i != keys.end(); ++i)
459   {
460     AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(*i, state));
461   }
462   keys.clear();
463 }
464
465 gboolean PressedKeys_key_press(GtkWidget* widget, GdkEventKey* event, PressedKeys* pressedKeys)
466 {
467   //globalOutputStream() << "pressed: " << event->keyval << "\n";
468   return event->state == 0 && Keys_press(pressedKeys->keys, event->keyval);
469 }
470
471 gboolean PressedKeys_key_release(GtkWidget* widget, GdkEventKey* event, PressedKeys* pressedKeys)
472 {
473   //globalOutputStream() << "released: " << event->keyval << "\n";
474   return Keys_release(pressedKeys->keys, event->keyval);
475 }
476
477 gboolean PressedKeys_focus_in(GtkWidget* widget, GdkEventFocus* event, PressedKeys* pressedKeys)
478 {
479   ++pressedKeys->refcount;
480   return FALSE;
481 }
482
483 gboolean PressedKeys_focus_out(GtkWidget* widget, GdkEventFocus* event, PressedKeys* pressedKeys)
484 {
485   if(--pressedKeys->refcount == 0)
486   {
487     Keys_releaseAll(pressedKeys->keys, 0);
488   }
489   return FALSE;
490 }
491
492 PressedKeys g_pressedKeys;
493
494 void GlobalPressedKeys_releaseAll()
495 {
496   Keys_releaseAll(g_pressedKeys.keys, 0);
497 }
498
499 void GlobalPressedKeys_connect(GtkWindow* window)
500 {
501   unsigned int key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(PressedKeys_key_press), &g_pressedKeys);
502   unsigned int key_release_handler = g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(PressedKeys_key_release), &g_pressedKeys);
503   g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler));
504   g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler));
505   unsigned int focus_in_handler = g_signal_connect(G_OBJECT(window), "focus_in_event", G_CALLBACK(PressedKeys_focus_in), &g_pressedKeys);
506   unsigned int focus_out_handler = g_signal_connect(G_OBJECT(window), "focus_out_event", G_CALLBACK(PressedKeys_focus_out), &g_pressedKeys);
507   g_object_set_data(G_OBJECT(window), "focus_in_handler", gint_to_pointer(focus_in_handler));
508   g_object_set_data(G_OBJECT(window), "focus_out_handler", gint_to_pointer(focus_out_handler));
509 }
510
511 void GlobalPressedKeys_disconnect(GtkWindow* window)
512 {
513   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler")));
514   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler")));
515   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_in_handler")));
516   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_out_handler")));
517 }
518
519
520
521 void special_accelerators_add(Accelerator accelerator, const Callback& callback)
522 {
523   //globalOutputStream() << "special_accelerators_add: " << makeQuoted(accelerator) << "\n";
524   if(!accelerator_map_insert(g_special_accelerators, accelerator, callback))
525   {
526     globalErrorStream() << "special_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
527   }
528 }
529 void special_accelerators_remove(Accelerator accelerator)
530 {
531   //globalOutputStream() << "special_accelerators_remove: " << makeQuoted(accelerator) << "\n";
532   if(!accelerator_map_erase(g_special_accelerators, accelerator))
533   {
534     globalErrorStream() << "special_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
535   }
536 }
537
538 void keydown_accelerators_add(Accelerator accelerator, const Callback& callback)
539 {
540   //globalOutputStream() << "keydown_accelerators_add: " << makeQuoted(accelerator) << "\n";
541   if(!accelerator_map_insert(g_keydown_accelerators, accelerator, callback))
542   {
543     globalErrorStream() << "keydown_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
544   }
545 }
546 void keydown_accelerators_remove(Accelerator accelerator)
547 {
548   //globalOutputStream() << "keydown_accelerators_remove: " << makeQuoted(accelerator) << "\n";
549   if(!accelerator_map_erase(g_keydown_accelerators, accelerator))
550   {
551     globalErrorStream() << "keydown_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
552   }
553 }
554
555 void keyup_accelerators_add(Accelerator accelerator, const Callback& callback)
556 {
557   //globalOutputStream() << "keyup_accelerators_add: " << makeQuoted(accelerator) << "\n";
558   if(!accelerator_map_insert(g_keyup_accelerators, accelerator, callback))
559   {
560     globalErrorStream() << "keyup_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
561   }
562 }
563 void keyup_accelerators_remove(Accelerator accelerator)
564 {
565   //globalOutputStream() << "keyup_accelerators_remove: " << makeQuoted(accelerator) << "\n";
566   if(!accelerator_map_erase(g_keyup_accelerators, accelerator))
567   {
568     globalErrorStream() << "keyup_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
569   }
570 }
571
572
573 gboolean accel_closure_callback(GtkAccelGroup* group, GtkWidget* widget, guint key, GdkModifierType modifiers, gpointer data)
574 {
575   (*reinterpret_cast<Callback*>(data))();
576   return TRUE;
577 }
578
579 GClosure* accel_group_add_accelerator(GtkAccelGroup* group, Accelerator accelerator, const Callback& callback)
580 {
581   if(accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers))
582   {
583     //globalOutputStream() << "global_accel_connect: " << makeQuoted(accelerator) << "\n";
584     GClosure* closure = create_cclosure(G_CALLBACK(accel_closure_callback), callback);
585     gtk_accel_group_connect(group, accelerator.key, accelerator.modifiers, GTK_ACCEL_VISIBLE, closure);
586     return closure;
587   }
588   else
589   {
590     special_accelerators_add(accelerator, callback);
591     return 0;
592   }
593 }
594
595 void accel_group_remove_accelerator(GtkAccelGroup* group, Accelerator accelerator)
596 {
597   if(accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers))
598   {
599     //globalOutputStream() << "global_accel_disconnect: " << makeQuoted(accelerator) << "\n";
600     gtk_accel_group_disconnect_key(group, accelerator.key, accelerator.modifiers);
601   }
602   else
603   {
604     special_accelerators_remove(accelerator);
605   }
606 }
607
608 GtkAccelGroup* global_accel = 0;
609
610 void global_accel_init()
611 {
612   global_accel = gtk_accel_group_new();
613 }
614
615 void global_accel_destroy()
616 {
617   g_object_unref(global_accel);
618 }
619
620 GClosure* global_accel_group_add_accelerator(Accelerator accelerator, const Callback& callback)
621 {
622   if(!global_accel_enabled())
623   {
624     // workaround: cannot add to GtkAccelGroup while it is disabled
625     //globalOutputStream() << "queued for add: " << accelerator << "\n";
626     globalQueuedAccelerators_add(accelerator, callback);
627     return 0;
628   }
629   return accel_group_add_accelerator(global_accel, accelerator, callback);
630 }
631 void global_accel_group_remove_accelerator(Accelerator accelerator)
632 {
633   if(!global_accel_enabled())
634   {
635     //globalOutputStream() << "queued for remove: " << accelerator << "\n";
636     globalQueuedAccelerators_remove(accelerator);
637     return;
638   }
639   accel_group_remove_accelerator(global_accel, accelerator);
640 }
641
642 /// \brief Propagates key events to the focus-widget, overriding global accelerators.
643 static gboolean override_global_accelerators(GtkWindow* window, GdkEventKey* event, gpointer data)
644 {
645   return gtk_window_propagate_key_event(window, event);
646 }
647
648 void global_accel_connect_window(GtkWindow* window)
649 {
650 #if 1
651   unsigned int override_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(override_global_accelerators), 0);
652   g_object_set_data(G_OBJECT(window), "override_handler", gint_to_pointer(override_handler));
653
654   unsigned int special_key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(accelerator_key_event), &g_special_accelerators);
655   g_object_set_data(G_OBJECT(window), "special_key_press_handler", gint_to_pointer(special_key_press_handler));
656
657   GlobalPressedKeys_connect(window);
658 #else
659   unsigned int key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(accelerator_key_event), &g_keydown_accelerators);
660   unsigned int key_release_handler = g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(accelerator_key_event), &g_keyup_accelerators);
661   g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler));
662   g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler));
663 #endif
664   g_accel_windows.insert(window);
665   gtk_window_add_accel_group(window, global_accel);
666 }
667 void global_accel_disconnect_window(GtkWindow* window)
668 {
669 #if 1
670   GlobalPressedKeys_disconnect(window);
671
672   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "override_handler")));
673   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "special_key_press_handler")));
674 #else
675   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler")));
676   g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler")));
677 #endif
678   gtk_window_remove_accel_group(window, global_accel);
679   std::size_t count = g_accel_windows.erase(window);
680   ASSERT_MESSAGE(count == 1, "failed to remove accel group\n");
681 }
682
683
684 GClosure* global_accel_group_find(Accelerator accelerator)
685 {
686   guint numEntries = 0;
687   GtkAccelGroupEntry* entry = gtk_accel_group_query(global_accel, accelerator.key, accelerator.modifiers, &numEntries);
688   if(numEntries != 0)
689   {
690     if(numEntries != 1)
691     {
692       char* name = gtk_accelerator_name(accelerator.key, accelerator.modifiers);
693       globalErrorStream() << "accelerator already in-use: " << name << "\n";
694       g_free(name);
695     }
696     return entry->closure;
697   }
698   return 0;
699 }
700
701 void global_accel_group_connect(const Accelerator& accelerator, const Callback& callback)
702 {
703   if(accelerator.key != 0)
704   {
705     global_accel_group_add_accelerator(accelerator, callback);
706   }
707 }
708
709 void global_accel_group_disconnect(const Accelerator& accelerator, const Callback& callback)
710 {
711   if(accelerator.key != 0)
712   {
713     global_accel_group_remove_accelerator(accelerator);
714   }
715 }
716
717