fix up forgotten Keyboard ideas of modifiers
[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 */
19
20 #include "ardour_ui.h"
21
22 #include <algorithm>
23 #include <fstream>
24 #include <iostream>
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 0
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 #ifdef GTKOSX
48 guint Keyboard::PrimaryModifier = GDK_META_MASK;   // Command
49 guint Keyboard::SecondaryModifier = GDK_MOD1_MASK; // Alt/Option
50 guint Keyboard::TertiaryModifier = GDK_SHIFT_MASK; // Shift
51 guint Keyboard::CopyModifier = GDK_MOD1_MASK;      // Alt/Option
52 guint Keyboard::RangeSelectModifier = GDK_SHIFT_MASK;   
53 #else
54 guint Keyboard::PrimaryModifier = GDK_CONTROL_MASK; // Control
55 guint Keyboard::SecondaryModifier = GDK_MOD1_MASK;  // Alt/Option
56 guint Keyboard::TertiaryModifier = GDK_SHIFT_MASK;  // Shift
57 guint Keyboard::CopyModifier = GDK_CONTROL_MASK;    
58 guint Keyboard::RangeSelectModifier = GDK_SHIFT_MASK;   
59 #endif
60
61 Keyboard*    Keyboard::_the_keyboard = 0;
62 Gtk::Window* Keyboard::current_window = 0;
63 bool         Keyboard::_some_magic_widget_has_focus = false;
64
65 /* set this to initially contain the modifiers we care about, then track changes in ::set_edit_modifier() etc. */
66
67 GdkModifierType Keyboard::RelevantModifierKeyMask;
68
69 void
70 Keyboard::magic_widget_grab_focus () 
71 {
72         _some_magic_widget_has_focus = true;
73 }
74
75 void
76 Keyboard::magic_widget_drop_focus ()
77 {
78         _some_magic_widget_has_focus = false;
79 }
80
81 bool
82 Keyboard::some_magic_widget_has_focus ()
83 {
84         return _some_magic_widget_has_focus;
85 }
86
87 Keyboard::Keyboard ()
88 {
89         if (_the_keyboard == 0) {
90                 _the_keyboard = this;
91         }
92
93         RelevantModifierKeyMask = (GdkModifierType) gtk_accelerator_get_default_mod_mask ();
94
95         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | PrimaryModifier);
96         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | SecondaryModifier);
97         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | TertiaryModifier);
98         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | CopyModifier);
99         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | RangeSelectModifier);
100
101         snooper_id = gtk_key_snooper_install (_snooper, (gpointer) this);
102
103         XMLNode* node = ARDOUR_UI::instance()->keyboard_settings();
104         set_state (*node);
105 }
106
107 Keyboard::~Keyboard ()
108 {
109         gtk_key_snooper_remove (snooper_id);
110 }
111
112 XMLNode& 
113 Keyboard::get_state (void)
114 {
115         XMLNode* node = new XMLNode ("Keyboard");
116         char buf[32];
117
118         snprintf (buf, sizeof (buf), "%d", edit_but);
119         node->add_property ("edit-button", buf);
120         snprintf (buf, sizeof (buf), "%d", edit_mod);
121         node->add_property ("edit-modifier", buf);
122         snprintf (buf, sizeof (buf), "%d", delete_but);
123         node->add_property ("delete-button", buf);
124         snprintf (buf, sizeof (buf), "%d", delete_mod);
125         node->add_property ("delete-modifier", buf);
126         snprintf (buf, sizeof (buf), "%d", snap_mod);
127         node->add_property ("snap-modifier", buf);
128
129         return *node;
130 }
131
132 int 
133 Keyboard::set_state (const XMLNode& node)
134 {
135         const XMLProperty* prop;
136
137         if ((prop = node.property ("edit-button")) != 0) {
138                 sscanf (prop->value().c_str(), "%d", &edit_but);
139         } 
140
141         if ((prop = node.property ("edit-modifier")) != 0) {
142                 sscanf (prop->value().c_str(), "%d", &edit_mod);
143         } 
144
145         if ((prop = node.property ("delete-button")) != 0) {
146                 sscanf (prop->value().c_str(), "%d", &delete_but);
147         } 
148
149         if ((prop = node.property ("delete-modifier")) != 0) {
150                 sscanf (prop->value().c_str(), "%d", &delete_mod);
151         } 
152
153         if ((prop = node.property ("snap-modifier")) != 0) {
154                 sscanf (prop->value().c_str(), "%d", &snap_mod);
155         } 
156
157         return 0;
158 }
159
160 gint
161 Keyboard::_snooper (GtkWidget *widget, GdkEventKey *event, gpointer data)
162 {
163         return ((Keyboard *) data)->snooper (widget, event);
164 }
165
166 gint
167 Keyboard::snooper (GtkWidget *widget, GdkEventKey *event)
168 {
169         uint32_t keyval;
170
171 #if 0
172         cerr << "snoop widget " << widget << " key " << event->keyval << " type: " << event->type 
173              << " state " << std::hex << event->state << std::dec
174              << endl;
175 #endif
176
177 #if KBD_DEBUG
178         if (debug_keyboard) {
179                 cerr << "snoop widget " << widget << " key " << event->keyval << " type: " << event->type 
180                      << endl;
181         }
182 #endif
183
184         if (event->keyval == GDK_Shift_R) {
185                 keyval = GDK_Shift_L;
186
187         } else  if (event->keyval == GDK_Control_R) {
188                 keyval = GDK_Control_L;
189
190         } else {
191                 keyval = event->keyval;
192         }
193                 
194         if (event->type == GDK_KEY_PRESS) {
195
196                 if (find (state.begin(), state.end(), keyval) == state.end()) {
197                         state.push_back (keyval);
198                         sort (state.begin(), state.end());
199                 } 
200
201         } else if (event->type == GDK_KEY_RELEASE) {
202
203                 State::iterator i;
204                 
205                 if ((i = find (state.begin(), state.end(), keyval)) != state.end()) {
206                         state.erase (i);
207                         sort (state.begin(), state.end());
208                 } 
209
210         }
211
212         if (event->type == GDK_KEY_RELEASE && event->keyval == GDK_w && modifier_state_equals (event->state, PrimaryModifier)) {
213                 if (current_window) {
214                         current_window->hide ();
215                         current_window = 0;
216                 }
217         }
218
219         return false;
220 }
221
222 bool
223 Keyboard::key_is_down (uint32_t keyval)
224 {
225         return find (state.begin(), state.end(), keyval) != state.end();
226 }
227
228 bool
229 Keyboard::enter_window (GdkEventCrossing *ev, Gtk::Window* win)
230 {
231         current_window = win;
232         return false;
233 }
234
235 bool
236 Keyboard::leave_window (GdkEventCrossing *ev, Gtk::Window* win)
237 {
238         switch (ev->detail) {
239         case GDK_NOTIFY_INFERIOR:
240                 if (debug_keyboard) {
241                         cerr << "INFERIOR crossing ... out\n";
242                 }
243                 break;
244
245         case GDK_NOTIFY_VIRTUAL:
246                 if (debug_keyboard) {
247                         cerr << "VIRTUAL crossing ... out\n";
248                 }
249                 /* fallthru */
250
251         default:
252                 if (debug_keyboard) {
253                         cerr << "REAL CROSSING ... out\n";
254                         cerr << "clearing current target\n";
255                 }
256                 state.clear ();
257                 current_window = 0;
258         }
259
260         return false;
261 }
262
263 void
264 Keyboard::set_edit_button (guint but)
265 {
266         edit_but = but;
267 }
268
269 void
270 Keyboard::set_edit_modifier (guint mod)
271 {
272         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~edit_mod);
273         edit_mod = mod;
274         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | edit_mod);
275 }
276
277 void
278 Keyboard::set_delete_button (guint but)
279 {
280         delete_but = but;
281 }
282
283 void
284 Keyboard::set_delete_modifier (guint mod)
285 {
286         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~delete_mod);
287         delete_mod = mod;
288         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | delete_mod);
289 }
290
291 void
292 Keyboard::set_modifier (uint32_t newval, uint32_t& var)
293 {
294         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~var);
295         var = newval;
296         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | var);
297 }
298
299 void
300 Keyboard::set_snap_modifier (guint mod)
301 {
302         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~snap_mod);
303         snap_mod = mod;
304         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | snap_mod);
305 }
306
307 bool
308 Keyboard::is_edit_event (GdkEventButton *ev)
309 {
310
311         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
312                 (ev->button == Keyboard::edit_button()) && 
313                 ((ev->state & RelevantModifierKeyMask) == Keyboard::edit_modifier());
314 }
315
316 bool
317 Keyboard::is_delete_event (GdkEventButton *ev)
318 {
319         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
320                 (ev->button == Keyboard::delete_button()) && 
321                 ((ev->state & RelevantModifierKeyMask) == Keyboard::delete_modifier());
322 }
323
324 bool
325 Keyboard::is_context_menu_event (GdkEventButton *ev)
326 {
327         return (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE) && 
328                 (ev->button == 3) && 
329                 ((ev->state & RelevantModifierKeyMask) == 0);
330 }
331
332 bool 
333 Keyboard::no_modifiers_active (guint state)
334 {
335         return (state & RelevantModifierKeyMask) == 0;
336 }
337
338 bool
339 Keyboard::modifier_state_contains (guint state, ModifierMask mask)
340 {
341         return (state & mask) == (guint) mask;
342 }
343
344 bool
345 Keyboard::modifier_state_equals (guint state, ModifierMask mask)
346 {
347         return (state & RelevantModifierKeyMask) == (guint) mask;
348 }
349
350 Selection::Operation
351 Keyboard::selection_type (guint state)
352 {
353         /* note that there is no modifier for "Add" */
354
355         if (modifier_state_equals (state, RangeSelectModifier)) {
356                 return Selection::Extend;
357         } else if (modifier_state_equals (state, PrimaryModifier)) {
358                 return Selection::Toggle;
359         } else {
360                 return Selection::Set;
361         }
362 }