strip X specific from keyboard.cc; fix up many buttons to avoid prelight (mostly...
[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         if (Meta) {
80                 cerr << "Using " << possible_meta[i] << " for Meta\n";
81         } else {
82                 cerr << "NO Meta\n";
83         }
84
85         snooper_id = gtk_key_snooper_install (_snooper, (gpointer) this);
86
87         XMLNode* node = ARDOUR_UI::instance()->keyboard_settings();
88         set_state (*node);
89 }
90
91 Keyboard::~Keyboard ()
92 {
93         gtk_key_snooper_remove (snooper_id);
94 }
95
96 XMLNode& 
97 Keyboard::get_state (void)
98 {
99         XMLNode* node = new XMLNode ("Keyboard");
100         char buf[32];
101
102         snprintf (buf, sizeof (buf), "%d", edit_but);
103         node->add_property ("edit-button", buf);
104         snprintf (buf, sizeof (buf), "%d", edit_mod);
105         node->add_property ("edit-modifier", buf);
106         snprintf (buf, sizeof (buf), "%d", delete_but);
107         node->add_property ("delete-button", buf);
108         snprintf (buf, sizeof (buf), "%d", delete_mod);
109         node->add_property ("delete-modifier", buf);
110         snprintf (buf, sizeof (buf), "%d", snap_mod);
111         node->add_property ("snap-modifier", buf);
112
113         return *node;
114 }
115
116 int 
117 Keyboard::set_state (const XMLNode& node)
118 {
119         const XMLProperty* prop;
120
121         if ((prop = node.property ("edit-button")) != 0) {
122                 sscanf (prop->value().c_str(), "%d", &edit_but);
123         } 
124
125         if ((prop = node.property ("edit-modifier")) != 0) {
126                 sscanf (prop->value().c_str(), "%d", &edit_mod);
127         } 
128
129         if ((prop = node.property ("delete-button")) != 0) {
130                 sscanf (prop->value().c_str(), "%d", &delete_but);
131         } 
132
133         if ((prop = node.property ("delete-modifier")) != 0) {
134                 sscanf (prop->value().c_str(), "%d", &delete_mod);
135         } 
136
137         if ((prop = node.property ("snap-modifier")) != 0) {
138                 sscanf (prop->value().c_str(), "%d", &snap_mod);
139         } 
140
141         return 0;
142 }
143
144 gint
145 Keyboard::_snooper (GtkWidget *widget, GdkEventKey *event, gpointer data)
146 {
147         return ((Keyboard *) data)->snooper (widget, event);
148 }
149
150 gint
151 Keyboard::snooper (GtkWidget *widget, GdkEventKey *event)
152 {
153         uint32_t keyval;
154
155 #if KBD_DEBUG
156         if (debug_keyboard) {
157                 cerr << "snoop widget " << widget << " key " << event->keyval << " type: " << event->type 
158                      << endl;
159         }
160 #endif
161
162         if (event->keyval == GDK_Shift_R) {
163                 keyval = GDK_Shift_L;
164
165         } else  if (event->keyval == GDK_Control_R) {
166                 keyval = GDK_Control_L;
167
168         } else {
169                 keyval = event->keyval;
170         }
171                 
172         if (event->type == GDK_KEY_PRESS) {
173
174                 if (find (state.begin(), state.end(), keyval) == state.end()) {
175                         state.push_back (keyval);
176                         sort (state.begin(), state.end());
177                 }
178
179         } else if (event->type == GDK_KEY_RELEASE) {
180
181                 State::iterator i;
182                 
183                 if ((i = find (state.begin(), state.end(), keyval)) != state.end()) {
184                         state.erase (i);
185                         sort (state.begin(), state.end());
186                 } 
187
188         }
189
190         return false;
191 }
192
193 bool
194 Keyboard::key_is_down (uint32_t keyval)
195 {
196         return find (state.begin(), state.end(), keyval) != state.end();
197 }
198
199 bool
200 Keyboard::enter_window (GdkEventCrossing *ev, Gtk::Window* win)
201 {
202         return false;
203 }
204
205 bool
206 Keyboard::leave_window (GdkEventCrossing *ev, Gtk::Window* win)
207 {
208         switch (ev->detail) {
209         case GDK_NOTIFY_INFERIOR:
210                 if (debug_keyboard) {
211                         cerr << "INFERIOR crossing ... out\n";
212                 }
213                 break;
214
215         case GDK_NOTIFY_VIRTUAL:
216                 if (debug_keyboard) {
217                         cerr << "VIRTUAL crossing ... out\n";
218                 }
219                 /* fallthru */
220
221         default:
222                 if (debug_keyboard) {
223                         cerr << "REAL CROSSING ... out\n";
224                         cerr << "clearing current target\n";
225                 }
226                 state.clear ();
227         }
228
229         return false;
230 }
231
232 void
233 Keyboard::set_edit_button (guint but)
234 {
235         edit_but = but;
236 }
237
238 void
239 Keyboard::set_edit_modifier (guint mod)
240 {
241         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~edit_mod);
242         edit_mod = mod;
243         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | edit_mod);
244 }
245
246 void
247 Keyboard::set_delete_button (guint but)
248 {
249         delete_but = but;
250 }
251
252 void
253 Keyboard::set_delete_modifier (guint mod)
254 {
255         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~delete_mod);
256         delete_mod = mod;
257         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | delete_mod);
258 }
259
260 void
261 Keyboard::set_meta_modifier (guint mod)
262 {
263         /* we don't include Meta in the RelevantModifierKeyMask because its not used
264            in the same way as snap_mod, delete_mod etc. the only reason we allow it to be
265            set at all is that X Window has no convention for the keyboard modifier
266            that Meta should use. Some Linux distributions bind NumLock to Mod2, which
267            is our default Meta modifier, and this causes severe problems.
268         */
269         Meta = mod;
270 }
271
272 void
273 Keyboard::set_snap_modifier (guint mod)
274 {
275         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~snap_mod);
276         snap_mod = mod;
277         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | snap_mod);
278 }
279
280 bool
281 Keyboard::is_edit_event (GdkEventButton *ev)
282 {
283
284         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
285                 (ev->button == Keyboard::edit_button()) && 
286                 ((ev->state & RelevantModifierKeyMask) == Keyboard::edit_modifier());
287 }
288
289 bool
290 Keyboard::is_delete_event (GdkEventButton *ev)
291 {
292         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
293                 (ev->button == Keyboard::delete_button()) && 
294                 ((ev->state & RelevantModifierKeyMask) == Keyboard::delete_modifier());
295 }
296
297 bool
298 Keyboard::is_context_menu_event (GdkEventButton *ev)
299 {
300         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
301                 (ev->button == 3) && 
302                 ((ev->state & RelevantModifierKeyMask) == 0);
303 }
304
305 bool 
306 Keyboard::no_modifiers_active (guint state)
307 {
308         return (state & RelevantModifierKeyMask) == 0;
309 }
310
311 bool
312 Keyboard::modifier_state_contains (guint state, ModifierMask mask)
313 {
314         return (state & mask) == (guint) mask;
315 }
316
317 bool
318 Keyboard::modifier_state_equals (guint state, ModifierMask mask)
319 {
320         return (state & RelevantModifierKeyMask) == (guint) mask;
321 }
322
323 Selection::Operation
324 Keyboard::selection_type (guint state)
325 {
326         /* note that there is no modifier for "Add" */
327
328         if (modifier_state_equals (state, Shift)) {
329                 return Selection::Extend;
330         } else if (modifier_state_equals (state, Control)) {
331                 return Selection::Toggle;
332         } else {
333                 return Selection::Set;
334         }
335 }