Cleanup the vala code of gtk3 panel and tool by:
[platform/upstream/ibus.git] / ui / gtk3 / keybindingmanager.vala
1 /* vim:set et sts=4 sw=4:
2 valac --pkg gtk+-2.0 --pkg x11 --pkg gdk-x11-2.0 --pkg gee-1.0 keybinding-manager.vala
3 */
4
5 /**
6  * This class is in charge to grab keybindings on the X11 display
7  * and filter X11-events and passing on such events to the registed
8  * handler methods.
9  *
10  * @author Oliver Sauder <os@esite.ch>
11  */
12
13 extern bool grab_keycode (Gdk.Display display,
14                           uint keyval,
15                           uint modifiers);
16
17 extern bool ungrab_keycode (Gdk.Display display,
18                             uint keyval,
19                             uint modifiers);
20
21 public class KeybindingManager : GLib.Object {
22     /**
23      * list of binded keybindings
24      */
25     private GLib.List<Keybinding> m_bindings = new GLib.List<Keybinding>();
26
27     private static KeybindingManager m_instance = null;
28
29     public static const uint MODIFIER_FILTER =
30         Gdk.ModifierType.MODIFIER_MASK & ~(
31         Gdk.ModifierType.LOCK_MASK |  // Caps Lock
32         // Gdk.ModifierType.MOD1_MASK |  // Alt
33         Gdk.ModifierType.MOD2_MASK |  // Num Lock
34         // Gdk.ModifierType.MOD3_MASK |
35         // Gdk.ModifierType.MOD4_MASK |  // Super, Hyper
36         // Gdk.ModifierType.MOD5_MASK |  //
37         Gdk.ModifierType.BUTTON1_MASK |
38         Gdk.ModifierType.BUTTON2_MASK |
39         Gdk.ModifierType.BUTTON3_MASK |
40         Gdk.ModifierType.BUTTON4_MASK |
41         Gdk.ModifierType.BUTTON5_MASK |
42         Gdk.ModifierType.SUPER_MASK |
43         Gdk.ModifierType.HYPER_MASK |
44         Gdk.ModifierType.META_MASK);
45
46     /**
47      * Helper class to store keybinding
48      */
49     private class Keybinding {
50         public Keybinding(uint keysym,
51                           Gdk.ModifierType modifiers,
52                           KeybindingHandlerFunc handler) {
53             this.keysym = keysym;
54             this.modifiers = modifiers;
55             this.handler = handler;
56         }
57
58         public uint keysym { get; set; }
59         public Gdk.ModifierType modifiers { get; set; }
60         public unowned KeybindingHandlerFunc handler { get; set; }
61     }
62
63     /**
64      * Keybinding func needed to bind key to handler
65      *
66      * @param event passing on gdk event
67      */
68     public delegate void KeybindingHandlerFunc(Gdk.Event event);
69
70
71     private  KeybindingManager() {
72         Gdk.Event.handler_set(event_handler);
73     }
74
75     /**
76      * Bind accelerator to given handler
77      *
78      * @param keysym
79      * @param modifiers
80      * @param handler handler called when given accelerator is pressed
81      */
82     public bool bind(uint keysym,
83                      Gdk.ModifierType modifiers,
84                      KeybindingHandlerFunc handler) {
85         unowned X.Display display = Gdk.x11_get_default_xdisplay();
86
87         int keycode = display.keysym_to_keycode(keysym);
88
89         if (keycode == 0)
90             return false;
91
92         grab_keycode (Gdk.Display.get_default(), keysym, modifiers);
93
94         // store binding
95         Keybinding binding = new Keybinding(keysym, modifiers, handler);
96         m_bindings.append(binding);
97
98         return true;
99     }
100
101     /**
102      * Unbind given accelerator.
103      *
104      * @param keysym
105      * @param modifiers
106      */
107     public void unbind(uint keysym,
108                        Gdk.ModifierType modifiers) {
109         // unbind all keys with given accelerator
110         GLib.List<Keybinding> remove_bindings = new GLib.List<Keybinding>();
111         foreach(Keybinding binding in m_bindings) {
112             if (binding.keysym == keysym && binding.modifiers == modifiers) {
113                 ungrab_keycode (Gdk.Display.get_default(),
114                                 binding.keysym,
115                                 binding.modifiers);
116                 remove_bindings.append(binding);
117             }
118         }
119
120         // remove unbinded keys
121         foreach (Keybinding binding in remove_bindings)
122             m_bindings.remove (binding);
123     }
124
125     public static KeybindingManager get_instance () {
126         if (m_instance == null)
127             m_instance = new KeybindingManager ();
128         return m_instance;
129     }
130
131     public static Gdk.ModifierType get_primary_modifier (uint binding_mask) {
132         const Gdk.ModifierType[] masks = {
133             Gdk.ModifierType.MOD5_MASK,
134             Gdk.ModifierType.MOD4_MASK,
135             Gdk.ModifierType.MOD3_MASK,
136             Gdk.ModifierType.MOD2_MASK,
137             Gdk.ModifierType.MOD1_MASK,
138             Gdk.ModifierType.CONTROL_MASK,
139             Gdk.ModifierType.SHIFT_MASK,
140             Gdk.ModifierType.LOCK_MASK
141         };
142         foreach (Gdk.ModifierType mask in masks) {
143             if ((binding_mask & mask) == mask)
144                 return mask;
145         }
146         return 0;
147     }
148
149     public static bool primary_modifier_still_pressed(Gdk.Event event,
150                                                       uint primary_modifier) {
151         Gdk.EventKey keyevent = event.key;
152         if (primary_modifier == 0)
153             return false;
154
155         Gdk.Device device = event.get_device();
156         Gdk.Device pointer;
157         if (device.get_source() == Gdk.InputSource.KEYBOARD)
158             pointer = device.get_associated_device();
159         else
160             pointer = device;
161
162         uint modifier = 0;
163         pointer.get_state(keyevent.window, null, out modifier);
164         if ((primary_modifier & modifier) == primary_modifier)
165             return true;
166
167         return false;
168     }
169
170     public static uint keyval_to_modifier (uint keyval) {
171         switch(keyval) {
172             case 0xffe3: /* Control_L */
173             case 0xffe4: /* Control_R */
174                 return Gdk.ModifierType.CONTROL_MASK;
175             case 0xffe1: /* Shift_L */
176             case 0xffe2: /* Shift_R */
177                 return Gdk.ModifierType.SHIFT_MASK;
178             case 0xffe5: /* Caps_Lock */
179                 return Gdk.ModifierType.LOCK_MASK;
180             case 0xffe9: /* Alt_L */
181             case 0xffea: /* Alt_R */
182                 return Gdk.ModifierType.MOD1_MASK;
183             case 0xffe7: /* Meta_L */
184             case 0xffe8: /* Meta_R */
185                 return Gdk.ModifierType.META_MASK;
186             case 0xffeb: /* Super_L */
187             case 0xffec: /* Super_R */
188                 return Gdk.ModifierType.SUPER_MASK;
189             case 0xffed: /* Hyper_L */
190             case 0xffee: /* Hyper_R */
191                 return Gdk.ModifierType.HYPER_MASK;
192             default:
193                 return 0;
194         }
195     }
196
197     private void event_handler(Gdk.Event event) {
198         do {
199             if (event.any.window != Gdk.get_default_root_window()) {
200                 break;
201             }
202
203             if (event.type == Gdk.EventType.KEY_PRESS) {
204                 uint modifiers = event.key.state & MODIFIER_FILTER;
205                 foreach (var binding in m_bindings) {
206                     if (event.key.keyval != binding.keysym ||
207                         modifiers != binding.modifiers)
208                         continue;
209                     binding.handler(event);
210                     return;
211                 }
212             }
213         } while (false);
214         Gtk.main_do_event(event);
215     }
216 }
217
218 /*
219 public static int main (string[] args)
220 {
221     Gtk.init (ref args);
222
223     KeybindingManager manager = new KeybindingManager();
224     manager.bind("<Ctrl><Alt>V", test);
225
226     Gtk.main ();
227     return 0;
228 }
229
230 private static void test()
231 {
232     debug("hotkey pressed");
233 }
234 */