2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
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.
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.
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
22 #include "accelerator.h"
24 #include "debugging/debugging.h"
30 #include "generic/callback.h"
31 #include "generic/bitfield.h"
32 #include "string/string.h"
37 #include <gdk/gdkkeysyms.h>
40 const char *global_keys_find(unsigned int key)
46 s = gdk_keyval_name(key);
53 unsigned int global_keys_find(const char *name)
56 if (!name || !*name) {
59 k = gdk_keyval_from_name(name);
60 if (k == GDK_KEY_VoidSymbol) {
66 void accelerator_write(const Accelerator &accelerator, TextOutputStream &ostream)
69 if ( accelerator.modifiers & GDK_SHIFT_MASK ) {
70 ostream << "Shift + ";
72 if ( accelerator.modifiers & GDK_MOD1_MASK ) {
75 if ( accelerator.modifiers & GDK_CONTROL_MASK ) {
76 ostream << "Control + ";
79 const char* keyName = global_keys_find( accelerator.key );
80 if ( !string_empty( keyName ) ) {
85 ostream << static_cast<char>( accelerator.key );
88 ostream << gtk_accelerator_get_label(accelerator.key, accelerator.modifiers);
91 typedef std::map<Accelerator, Callback<void()>> AcceleratorMap;
92 typedef std::set<Accelerator> AcceleratorSet;
94 bool accelerator_map_insert(AcceleratorMap &acceleratorMap, Accelerator accelerator, const Callback<void()> &callback)
96 if (accelerator.key != 0) {
97 return acceleratorMap.insert(AcceleratorMap::value_type(accelerator, callback)).second;
102 bool accelerator_map_erase(AcceleratorMap &acceleratorMap, Accelerator accelerator)
104 if (accelerator.key != 0) {
105 AcceleratorMap::iterator i = acceleratorMap.find(accelerator);
106 if (i == acceleratorMap.end()) {
109 acceleratorMap.erase(i);
114 Accelerator accelerator_for_event_key(guint keyval, guint state)
116 keyval = gdk_keyval_to_upper(keyval);
117 if (keyval == GDK_KEY_ISO_Left_Tab) {
118 keyval = GDK_KEY_Tab;
120 return Accelerator(keyval, (GdkModifierType) (state & gtk_accelerator_get_default_mod_mask()));
123 bool AcceleratorMap_activate(const AcceleratorMap &acceleratorMap, const Accelerator &accelerator)
125 AcceleratorMap::const_iterator i = acceleratorMap.find(accelerator);
126 if (i != acceleratorMap.end()) {
134 static gboolean accelerator_key_event(ui::Window window, GdkEventKey *event, AcceleratorMap *acceleratorMap)
136 return AcceleratorMap_activate(*acceleratorMap, accelerator_for_event_key(event->keyval, event->state));
140 AcceleratorMap g_special_accelerators;
143 namespace MouseButton {
151 typedef unsigned int ButtonMask;
153 void print_buttons(ButtonMask mask)
155 globalOutputStream() << "button state: ";
156 if ((mask & MouseButton::Left) != 0) {
157 globalOutputStream() << "Left ";
159 if ((mask & MouseButton::Right) != 0) {
160 globalOutputStream() << "Right ";
162 if ((mask & MouseButton::Middle) != 0) {
163 globalOutputStream() << "Middle ";
165 globalOutputStream() << "\n";
168 ButtonMask ButtonMask_for_event_button(guint button)
172 return MouseButton::Left;
174 return MouseButton::Middle;
176 return MouseButton::Right;
181 bool window_has_accel(ui::Window toplevel)
183 return g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) != 0;
187 bool g_accel_enabled = true;
190 bool global_accel_enabled()
192 return g_accel_enabled;
196 GClosure *accel_group_add_accelerator(ui::AccelGroup group, Accelerator accelerator, const Callback<void()> &callback);
198 void accel_group_remove_accelerator(ui::AccelGroup group, Accelerator accelerator);
200 AcceleratorMap g_queuedAcceleratorsAdd;
201 AcceleratorSet g_queuedAcceleratorsRemove;
203 void globalQueuedAccelerators_add(Accelerator accelerator, const Callback<void()> &callback)
205 if (!g_queuedAcceleratorsAdd.insert(AcceleratorMap::value_type(accelerator, callback)).second) {
206 globalErrorStream() << "globalQueuedAccelerators_add: accelerator already queued: " << accelerator << "\n";
210 void globalQueuedAccelerators_remove(Accelerator accelerator)
212 if (g_queuedAcceleratorsAdd.erase(accelerator) == 0) {
213 if (!g_queuedAcceleratorsRemove.insert(accelerator).second) {
214 globalErrorStream() << "globalQueuedAccelerators_remove: accelerator already queued: " << accelerator
220 void globalQueuedAccelerators_commit()
222 for (AcceleratorSet::const_iterator i = g_queuedAcceleratorsRemove.begin();
223 i != g_queuedAcceleratorsRemove.end(); ++i) {
224 //globalOutputStream() << "removing: " << (*i).first << "\n";
225 accel_group_remove_accelerator(global_accel, *i);
227 g_queuedAcceleratorsRemove.clear();
228 for (AcceleratorMap::const_iterator i = g_queuedAcceleratorsAdd.begin(); i != g_queuedAcceleratorsAdd.end(); ++i) {
229 //globalOutputStream() << "adding: " << (*i).first << "\n";
230 accel_group_add_accelerator(global_accel, (*i).first, (*i).second);
232 g_queuedAcceleratorsAdd.clear();
235 typedef std::set<ui::Window> WindowSet;
236 WindowSet g_accel_windows;
238 bool Buttons_press(ButtonMask &buttons, guint button, guint state)
240 if (buttons == 0 && bitfield_enable(buttons, ButtonMask_for_event_button(button)) != 0) {
241 ASSERT_MESSAGE(g_accel_enabled, "Buttons_press: accelerators not enabled");
242 g_accel_enabled = false;
243 for (WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i) {
244 ui::Window toplevel = *i;
245 ASSERT_MESSAGE(window_has_accel(toplevel), "ERROR");
246 ASSERT_MESSAGE(gtk_widget_is_toplevel(toplevel), "disabling accel for non-toplevel window");
247 gtk_window_remove_accel_group(toplevel, global_accel);
249 globalOutputStream() << reinterpret_cast<unsigned int>( toplevel ) << ": disabled global accelerators\n";
253 buttons = bitfield_enable(buttons, ButtonMask_for_event_button(button));
255 globalOutputStream() << "Buttons_press: ";
256 print_buttons( buttons );
261 bool Buttons_release(ButtonMask &buttons, guint button, guint state)
263 if (buttons != 0 && bitfield_disable(buttons, ButtonMask_for_event_button(button)) == 0) {
264 ASSERT_MESSAGE(!g_accel_enabled, "Buttons_release: accelerators are enabled");
265 g_accel_enabled = true;
266 for (WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i) {
267 ui::Window toplevel = *i;
268 ASSERT_MESSAGE(!window_has_accel(toplevel), "ERROR");
269 ASSERT_MESSAGE(gtk_widget_is_toplevel(toplevel), "enabling accel for non-toplevel window");
270 toplevel.add_accel_group(global_accel);
272 globalOutputStream() << reinterpret_cast<unsigned int>( toplevel ) << ": enabled global accelerators\n";
275 globalQueuedAccelerators_commit();
277 buttons = bitfield_disable(buttons, ButtonMask_for_event_button(button));
279 globalOutputStream() << "Buttons_release: ";
280 print_buttons( buttons );
285 bool Buttons_releaseAll(ButtonMask &buttons)
287 Buttons_release(buttons, MouseButton::Left | MouseButton::Middle | MouseButton::Right, 0);
291 struct PressedButtons {
294 PressedButtons() : buttons(0)
299 gboolean PressedButtons_button_press(ui::Widget widget, GdkEventButton *event, PressedButtons *pressed)
301 if (event->type == GDK_BUTTON_PRESS) {
302 return Buttons_press(pressed->buttons, event->button, event->state);
307 gboolean PressedButtons_button_release(ui::Widget widget, GdkEventButton *event, PressedButtons *pressed)
309 if (event->type == GDK_BUTTON_RELEASE) {
310 return Buttons_release(pressed->buttons, event->button, event->state);
315 gboolean PressedButtons_focus_out(ui::Widget widget, GdkEventFocus *event, PressedButtons *pressed)
317 Buttons_releaseAll(pressed->buttons);
321 void PressedButtons_connect(PressedButtons &pressedButtons, ui::Widget widget)
323 widget.connect("button_press_event", G_CALLBACK(PressedButtons_button_press), &pressedButtons);
324 widget.connect("button_release_event", G_CALLBACK(PressedButtons_button_release), &pressedButtons);
325 widget.connect("focus_out_event", G_CALLBACK(PressedButtons_focus_out), &pressedButtons);
328 PressedButtons g_pressedButtons;
332 #include <uilib/uilib.h>
335 typedef std::set<guint> Keys;
337 std::size_t refcount;
339 PressedKeys() : refcount(0)
344 AcceleratorMap g_keydown_accelerators;
345 AcceleratorMap g_keyup_accelerators;
347 bool Keys_press(PressedKeys::Keys &keys, guint keyval)
349 if (keys.insert(keyval).second) {
350 return AcceleratorMap_activate(g_keydown_accelerators, accelerator_for_event_key(keyval, 0));
352 return g_keydown_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keydown_accelerators.end();
355 bool Keys_release(PressedKeys::Keys &keys, guint keyval)
357 if (keys.erase(keyval) != 0) {
358 return AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(keyval, 0));
360 return g_keyup_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keyup_accelerators.end();
363 void Keys_releaseAll(PressedKeys::Keys &keys, guint state)
365 for (PressedKeys::Keys::iterator i = keys.begin(); i != keys.end(); ++i) {
366 AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(*i, state));
371 gboolean PressedKeys_key_press(ui::Widget widget, GdkEventKey *event, PressedKeys *pressedKeys)
373 //globalOutputStream() << "pressed: " << event->keyval << "\n";
374 return event->state == 0 && Keys_press(pressedKeys->keys, event->keyval);
377 gboolean PressedKeys_key_release(ui::Widget widget, GdkEventKey *event, PressedKeys *pressedKeys)
379 //globalOutputStream() << "released: " << event->keyval << "\n";
380 return Keys_release(pressedKeys->keys, event->keyval);
383 gboolean PressedKeys_focus_in(ui::Widget widget, GdkEventFocus *event, PressedKeys *pressedKeys)
385 ++pressedKeys->refcount;
389 gboolean PressedKeys_focus_out(ui::Widget widget, GdkEventFocus *event, PressedKeys *pressedKeys)
391 if (--pressedKeys->refcount == 0) {
392 Keys_releaseAll(pressedKeys->keys, 0);
397 PressedKeys g_pressedKeys;
399 void GlobalPressedKeys_releaseAll()
401 Keys_releaseAll(g_pressedKeys.keys, 0);
404 void GlobalPressedKeys_connect(ui::Window window)
406 unsigned int key_press_handler = window.connect("key_press_event", G_CALLBACK(PressedKeys_key_press),
408 unsigned int key_release_handler = window.connect("key_release_event", G_CALLBACK(PressedKeys_key_release),
410 g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler));
411 g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler));
412 unsigned int focus_in_handler = window.connect("focus_in_event", G_CALLBACK(PressedKeys_focus_in), &g_pressedKeys);
413 unsigned int focus_out_handler = window.connect("focus_out_event", G_CALLBACK(PressedKeys_focus_out),
415 g_object_set_data(G_OBJECT(window), "focus_in_handler", gint_to_pointer(focus_in_handler));
416 g_object_set_data(G_OBJECT(window), "focus_out_handler", gint_to_pointer(focus_out_handler));
419 void GlobalPressedKeys_disconnect(ui::Window window)
421 g_signal_handler_disconnect(G_OBJECT(window),
422 gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler")));
423 g_signal_handler_disconnect(G_OBJECT(window),
424 gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler")));
425 g_signal_handler_disconnect(G_OBJECT(window),
426 gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_in_handler")));
427 g_signal_handler_disconnect(G_OBJECT(window),
428 gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_out_handler")));
432 void special_accelerators_add(Accelerator accelerator, const Callback<void()> &callback)
434 //globalOutputStream() << "special_accelerators_add: " << makeQuoted(accelerator) << "\n";
435 if (!accelerator_map_insert(g_special_accelerators, accelerator, callback)) {
436 globalErrorStream() << "special_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
440 void special_accelerators_remove(Accelerator accelerator)
442 //globalOutputStream() << "special_accelerators_remove: " << makeQuoted(accelerator) << "\n";
443 if (!accelerator_map_erase(g_special_accelerators, accelerator)) {
444 globalErrorStream() << "special_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
448 void keydown_accelerators_add(Accelerator accelerator, const Callback<void()> &callback)
450 //globalOutputStream() << "keydown_accelerators_add: " << makeQuoted(accelerator) << "\n";
451 if (!accelerator_map_insert(g_keydown_accelerators, accelerator, callback)) {
452 globalErrorStream() << "keydown_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
456 void keydown_accelerators_remove(Accelerator accelerator)
458 //globalOutputStream() << "keydown_accelerators_remove: " << makeQuoted(accelerator) << "\n";
459 if (!accelerator_map_erase(g_keydown_accelerators, accelerator)) {
460 globalErrorStream() << "keydown_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
464 void keyup_accelerators_add(Accelerator accelerator, const Callback<void()> &callback)
466 //globalOutputStream() << "keyup_accelerators_add: " << makeQuoted(accelerator) << "\n";
467 if (!accelerator_map_insert(g_keyup_accelerators, accelerator, callback)) {
468 globalErrorStream() << "keyup_accelerators_add: already exists: " << makeQuoted(accelerator) << "\n";
472 void keyup_accelerators_remove(Accelerator accelerator)
474 //globalOutputStream() << "keyup_accelerators_remove: " << makeQuoted(accelerator) << "\n";
475 if (!accelerator_map_erase(g_keyup_accelerators, accelerator)) {
476 globalErrorStream() << "keyup_accelerators_remove: not found: " << makeQuoted(accelerator) << "\n";
482 accel_closure_callback(ui::AccelGroup group, ui::Widget widget, guint key, GdkModifierType modifiers, gpointer data)
484 (*reinterpret_cast<Callback<void()> *>( data ))();
488 GClosure *accel_group_add_accelerator(ui::AccelGroup group, Accelerator accelerator, const Callback<void()> &callback)
490 if (accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers)) {
491 //globalOutputStream() << "global_accel_connect: " << makeQuoted(accelerator) << "\n";
492 GClosure *closure = create_cclosure(G_CALLBACK(accel_closure_callback), callback);
493 gtk_accel_group_connect(group, accelerator.key, accelerator.modifiers, GTK_ACCEL_VISIBLE, closure);
496 special_accelerators_add(accelerator, callback);
501 void accel_group_remove_accelerator(ui::AccelGroup group, Accelerator accelerator)
503 if (accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers)) {
504 //globalOutputStream() << "global_accel_disconnect: " << makeQuoted(accelerator) << "\n";
505 gtk_accel_group_disconnect_key(group, accelerator.key, accelerator.modifiers);
507 special_accelerators_remove(accelerator);
511 ui::AccelGroup global_accel{ui::New};
513 GClosure *global_accel_group_add_accelerator(Accelerator accelerator, const Callback<void()> &callback)
515 if (!global_accel_enabled()) {
516 // workaround: cannot add to GtkAccelGroup while it is disabled
517 //globalOutputStream() << "queued for add: " << accelerator << "\n";
518 globalQueuedAccelerators_add(accelerator, callback);
521 return accel_group_add_accelerator(global_accel, accelerator, callback);
524 void global_accel_group_remove_accelerator(Accelerator accelerator)
526 if (!global_accel_enabled()) {
527 //globalOutputStream() << "queued for remove: " << accelerator << "\n";
528 globalQueuedAccelerators_remove(accelerator);
531 accel_group_remove_accelerator(global_accel, accelerator);
534 /// \brief Propagates key events to the focus-widget, overriding global accelerators.
535 static gboolean override_global_accelerators(ui::Window window, GdkEventKey *event, gpointer data)
537 gboolean b = gtk_window_propagate_key_event(window, event);
541 void global_accel_connect_window(ui::Window window)
544 unsigned int override_handler = window.connect("key_press_event", G_CALLBACK(override_global_accelerators), 0);
545 g_object_set_data(G_OBJECT(window), "override_handler", gint_to_pointer(override_handler));
547 unsigned int special_key_press_handler = window.connect("key_press_event", G_CALLBACK(accelerator_key_event),
548 &g_special_accelerators);
549 g_object_set_data(G_OBJECT(window), "special_key_press_handler", gint_to_pointer(special_key_press_handler));
551 GlobalPressedKeys_connect(window);
553 unsigned int key_press_handler = window.connect( "key_press_event", G_CALLBACK( accelerator_key_event ), &g_keydown_accelerators );
554 unsigned int key_release_handler = window.connect( "key_release_event", G_CALLBACK( accelerator_key_event ), &g_keyup_accelerators );
555 g_object_set_data( G_OBJECT( window ), "key_press_handler", gint_to_pointer( key_press_handler ) );
556 g_object_set_data( G_OBJECT( window ), "key_release_handler", gint_to_pointer( key_release_handler ) );
558 g_accel_windows.insert(window);
559 window.add_accel_group(global_accel);
562 void global_accel_disconnect_window(ui::Window window)
565 GlobalPressedKeys_disconnect(window);
567 g_signal_handler_disconnect(G_OBJECT(window),
568 gpointer_to_int(g_object_get_data(G_OBJECT(window), "override_handler")));
569 g_signal_handler_disconnect(G_OBJECT(window),
570 gpointer_to_int(g_object_get_data(G_OBJECT(window), "special_key_press_handler")));
572 g_signal_handler_disconnect( G_OBJECT( window ), gpointer_to_int( g_object_get_data( G_OBJECT( window ), "key_press_handler" ) ) );
573 g_signal_handler_disconnect( G_OBJECT( window ), gpointer_to_int( g_object_get_data( G_OBJECT( window ), "key_release_handler" ) ) );
575 gtk_window_remove_accel_group(window, global_accel);
576 std::size_t count = g_accel_windows.erase(window);
577 ASSERT_MESSAGE(count == 1, "failed to remove accel group\n");
581 GClosure *global_accel_group_find(Accelerator accelerator)
583 guint numEntries = 0;
584 GtkAccelGroupEntry *entry = gtk_accel_group_query(global_accel, accelerator.key, accelerator.modifiers,
586 if (numEntries != 0) {
587 if (numEntries != 1) {
588 char *name = gtk_accelerator_name(accelerator.key, accelerator.modifiers);
589 globalErrorStream() << "accelerator already in-use: " << name << "\n";
592 return entry->closure;
597 void global_accel_group_connect(const Accelerator &accelerator, const Callback<void()> &callback)
599 if (accelerator.key != 0) {
600 global_accel_group_add_accelerator(accelerator, callback);
604 void global_accel_group_disconnect(const Accelerator &accelerator, const Callback<void()> &callback)
606 if (accelerator.key != 0) {
607 global_accel_group_remove_accelerator(accelerator);