e7a537716e3709b62c7c4eacb8d693a70b350510
[ardour.git] / gtk2_ardour / keyboard.cc
1 /*
2     Copyright (C) 2001 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program 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
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include "ardour_ui.h"
22
23 #include <algorithm>
24 #include <fstream>
25
26 #include <ctype.h>
27
28 #include <gdk/gdkkeysyms.h>
29 #include <pbd/error.h>
30
31 #include "keyboard.h"
32 #include "gui_thread.h"
33
34 #include "i18n.h"
35
36 using namespace PBD;
37
38 #define KBD_DEBUG 1
39 bool debug_keyboard = false;
40
41 guint Keyboard::edit_but = 3;
42 guint Keyboard::edit_mod = GDK_CONTROL_MASK;
43 guint Keyboard::delete_but = 3;
44 guint Keyboard::delete_mod = GDK_SHIFT_MASK;
45 guint Keyboard::snap_mod = GDK_MOD3_MASK;
46
47 uint32_t Keyboard::Control = GDK_CONTROL_MASK;
48 uint32_t Keyboard::Shift = GDK_SHIFT_MASK;
49 uint32_t Keyboard::Alt = GDK_MOD1_MASK;
50 uint32_t Keyboard::Meta;
51
52 Keyboard* Keyboard::_the_keyboard = 0;
53
54 /* set this to initially contain the modifiers we care about, then track changes in ::set_edit_modifier() etc. */
55
56 GdkModifierType Keyboard::RelevantModifierKeyMask;
57
58
59 Keyboard::Keyboard ()
60 {
61         if (_the_keyboard == 0) {
62                 _the_keyboard = this;
63         }
64
65         RelevantModifierKeyMask = (GdkModifierType) gtk_accelerator_get_default_mod_mask ();
66
67         /* figure out Meta */
68
69         uint32_t possible_meta[] = { GDK_MOD2_MASK, GDK_MOD3_MASK, GDK_MOD4_MASK, GDK_MOD5_MASK, 0};
70         int i;
71
72         for (i = 0; possible_meta[i]; ++i) {
73                 if (!(RelevantModifierKeyMask & possible_meta[i])) {
74                         break;
75                 }
76         }
77         Meta = possible_meta[i];
78
79         snooper_id = gtk_key_snooper_install (_snooper, (gpointer) this);
80
81         XMLNode* node = ARDOUR_UI::instance()->keyboard_settings();
82         set_state (*node);
83 }
84
85 Keyboard::~Keyboard ()
86 {
87         gtk_key_snooper_remove (snooper_id);
88 }
89
90 XMLNode& 
91 Keyboard::get_state (void)
92 {
93         XMLNode* node = new XMLNode ("Keyboard");
94         char buf[32];
95
96         snprintf (buf, sizeof (buf), "%d", edit_but);
97         node->add_property ("edit-button", buf);
98         snprintf (buf, sizeof (buf), "%d", edit_mod);
99         node->add_property ("edit-modifier", buf);
100         snprintf (buf, sizeof (buf), "%d", delete_but);
101         node->add_property ("delete-button", buf);
102         snprintf (buf, sizeof (buf), "%d", delete_mod);
103         node->add_property ("delete-modifier", buf);
104         snprintf (buf, sizeof (buf), "%d", snap_mod);
105         node->add_property ("snap-modifier", buf);
106
107         return *node;
108 }
109
110 int 
111 Keyboard::set_state (const XMLNode& node)
112 {
113         const XMLProperty* prop;
114
115         if ((prop = node.property ("edit-button")) != 0) {
116                 sscanf (prop->value().c_str(), "%d", &edit_but);
117         } 
118
119         if ((prop = node.property ("edit-modifier")) != 0) {
120                 sscanf (prop->value().c_str(), "%d", &edit_mod);
121         } 
122
123         if ((prop = node.property ("delete-button")) != 0) {
124                 sscanf (prop->value().c_str(), "%d", &delete_but);
125         } 
126
127         if ((prop = node.property ("delete-modifier")) != 0) {
128                 sscanf (prop->value().c_str(), "%d", &delete_mod);
129         } 
130
131         if ((prop = node.property ("snap-modifier")) != 0) {
132                 sscanf (prop->value().c_str(), "%d", &snap_mod);
133         } 
134
135         return 0;
136 }
137
138 gint
139 Keyboard::_snooper (GtkWidget *widget, GdkEventKey *event, gpointer data)
140 {
141         return ((Keyboard *) data)->snooper (widget, event);
142 }
143
144 gint
145 Keyboard::snooper (GtkWidget *widget, GdkEventKey *event)
146 {
147         uint32_t keyval;
148
149 #if KBD_DEBUG
150         if (debug_keyboard) {
151                 cerr << "snoop widget " << widget << " key " << event->keyval << " type: " << event->type 
152                      << endl;
153         }
154 #endif
155
156         if (event->keyval == GDK_Shift_R) {
157                 keyval = GDK_Shift_L;
158
159         } else  if (event->keyval == GDK_Control_R) {
160                 keyval = GDK_Control_L;
161
162         } else {
163                 keyval = event->keyval;
164         }
165                 
166         if (event->type == GDK_KEY_PRESS) {
167
168                 if (find (state.begin(), state.end(), keyval) == state.end()) {
169                         state.push_back (keyval);
170                         sort (state.begin(), state.end());
171                 }
172
173         } else if (event->type == GDK_KEY_RELEASE) {
174
175                 State::iterator i;
176                 
177                 if ((i = find (state.begin(), state.end(), keyval)) != state.end()) {
178                         state.erase (i);
179                         sort (state.begin(), state.end());
180                 } 
181
182         }
183
184         return false;
185 }
186
187 bool
188 Keyboard::key_is_down (uint32_t keyval)
189 {
190         return find (state.begin(), state.end(), keyval) != state.end();
191 }
192
193 bool
194 Keyboard::enter_window (GdkEventCrossing *ev, Gtk::Window* win)
195 {
196         return false;
197 }
198
199 bool
200 Keyboard::leave_window (GdkEventCrossing *ev, Gtk::Window* win)
201 {
202         switch (ev->detail) {
203         case GDK_NOTIFY_INFERIOR:
204                 if (debug_keyboard) {
205                         cerr << "INFERIOR crossing ... out\n";
206                 }
207                 break;
208
209         case GDK_NOTIFY_VIRTUAL:
210                 if (debug_keyboard) {
211                         cerr << "VIRTUAL crossing ... out\n";
212                 }
213                 /* fallthru */
214
215         default:
216                 if (debug_keyboard) {
217                         cerr << "REAL CROSSING ... out\n";
218                         cerr << "clearing current target\n";
219                 }
220                 state.clear ();
221         }
222
223         return false;
224 }
225
226 void
227 Keyboard::set_edit_button (guint but)
228 {
229         edit_but = but;
230 }
231
232 void
233 Keyboard::set_edit_modifier (guint mod)
234 {
235         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~edit_mod);
236         edit_mod = mod;
237         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | edit_mod);
238 }
239
240 void
241 Keyboard::set_delete_button (guint but)
242 {
243         delete_but = but;
244 }
245
246 void
247 Keyboard::set_delete_modifier (guint mod)
248 {
249         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~delete_mod);
250         delete_mod = mod;
251         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | delete_mod);
252 }
253
254 void
255 Keyboard::set_meta_modifier (guint mod)
256 {
257         /* we don't include Meta in the RelevantModifierKeyMask because its not used
258            in the same way as snap_mod, delete_mod etc. the only reason we allow it to be
259            set at all is that X Window has no convention for the keyboard modifier
260            that Meta should use. Some Linux distributions bind NumLock to Mod2, which
261            is our default Meta modifier, and this causes severe problems.
262         */
263         Meta = mod;
264 }
265
266 void
267 Keyboard::set_snap_modifier (guint mod)
268 {
269         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~snap_mod);
270         snap_mod = mod;
271         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | snap_mod);
272 }
273
274 bool
275 Keyboard::is_edit_event (GdkEventButton *ev)
276 {
277
278         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
279                 (ev->button == Keyboard::edit_button()) && 
280                 ((ev->state & RelevantModifierKeyMask) == Keyboard::edit_modifier());
281 }
282
283 bool
284 Keyboard::is_delete_event (GdkEventButton *ev)
285 {
286         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
287                 (ev->button == Keyboard::delete_button()) && 
288                 ((ev->state & RelevantModifierKeyMask) == Keyboard::delete_modifier());
289 }
290
291 bool
292 Keyboard::is_context_menu_event (GdkEventButton *ev)
293 {
294         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
295                 (ev->button == 3) && 
296                 ((ev->state & RelevantModifierKeyMask) == 0);
297 }
298
299 bool 
300 Keyboard::no_modifiers_active (guint state)
301 {
302         return (state & RelevantModifierKeyMask) == 0;
303 }
304
305 bool
306 Keyboard::modifier_state_contains (guint state, ModifierMask mask)
307 {
308         return (state & mask) == (guint) mask;
309 }
310
311 bool
312 Keyboard::modifier_state_equals (guint state, ModifierMask mask)
313 {
314         return (state & RelevantModifierKeyMask) == (guint) mask;
315 }
316
317 Selection::Operation
318 Keyboard::selection_type (guint state)
319 {
320         /* note that there is no modifier for "Add" */
321
322         if (modifier_state_equals (state, Shift)) {
323                 return Selection::Extend;
324         } else if (modifier_state_equals (state, Control)) {
325                 return Selection::Toggle;
326         } else {
327                 return Selection::Set;
328         }
329 }