changes to support new key bindings editor design
[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 "pbd/convert.h"
21 #include "pbd/error.h"
22 #include "pbd/file_utils.h"
23
24 #include "ardour/filesystem_paths.h"
25
26 #include "ardour_ui.h"
27 #include "public_editor.h"
28 #include "keyboard.h"
29 #include "opts.h"
30
31 #include "i18n.h"
32
33 using namespace std;
34 using namespace Gtk;
35 using namespace PBD;
36 using namespace ARDOUR;
37 using Gtkmm2ext::Keyboard;
38  
39 static void
40 accel_map_changed (GtkAccelMap* /*map*/,
41                    gchar* /*path*/,
42                    guint /*key*/,
43                    GdkModifierType /*mod*/,
44                    gpointer keyboard)
45 {
46         ArdourKeyboard* me = (ArdourKeyboard*)keyboard;
47         Keyboard::keybindings_changed ();
48         me->ui.setup_tooltips ();
49 }
50
51 #ifdef GTKOSX
52 guint ArdourKeyboard::constraint_mod = Keyboard::PrimaryModifier;
53 #else
54 guint ArdourKeyboard::constraint_mod = Keyboard::SecondaryModifier;
55 #endif
56 guint ArdourKeyboard::trim_contents_mod = Keyboard::PrimaryModifier;
57 guint ArdourKeyboard::trim_overlap_mod = Keyboard::TertiaryModifier;
58 guint ArdourKeyboard::trim_anchored_mod = Keyboard::TertiaryModifier;
59 guint ArdourKeyboard::fine_adjust_mod = Keyboard::SecondaryModifier;
60 guint ArdourKeyboard::push_points_mod = Keyboard::PrimaryModifier;
61 guint ArdourKeyboard::note_size_relative_mod = Keyboard::PrimaryModifier;
62
63 void
64 ArdourKeyboard::setup_keybindings ()
65 {
66         using namespace ARDOUR_COMMAND_LINE;
67         string default_bindings = "mnemonic-us.bindings";
68         vector<string> strs;
69
70         binding_files.clear ();
71
72         ARDOUR::find_bindings_files (binding_files);
73
74         /* set up the per-user bindings path */
75
76         string lowercase_program_name = downcase (string(PROGRAM_NAME));
77
78         user_keybindings_path = Glib::build_filename (user_config_directory(), lowercase_program_name + ".bindings");
79
80         if (Glib::file_test (user_keybindings_path, Glib::FILE_TEST_EXISTS)) {
81                 std::pair<string,string> newpair;
82                 newpair.first = _("your own");
83                 newpair.second = user_keybindings_path;
84                 binding_files.insert (newpair);
85         }
86
87         /* check to see if they gave a style name ("SAE", "ergonomic") or
88            an actual filename (*.bindings)
89         */
90
91         if (!keybindings_path.empty() && keybindings_path.find (".bindings") == string::npos) {
92
93                 // just a style name - allow user to
94                 // specify the layout type.
95
96                 char* layout;
97
98                 if ((layout = getenv ("ARDOUR_KEYBOARD_LAYOUT")) != 0 && layout[0] != '\0') {
99
100                         /* user-specified keyboard layout */
101
102                         keybindings_path += '-';
103                         keybindings_path += layout;
104
105                 } else {
106
107                         /* default to US/ANSI - we have to pick something */
108
109                         keybindings_path += "-us";
110                 }
111
112                 keybindings_path += ".bindings";
113         }
114
115         if (keybindings_path.empty()) {
116
117                 /* no path or binding name given: check the user one first */
118
119                 if (!Glib::file_test (user_keybindings_path, Glib::FILE_TEST_EXISTS)) {
120
121                         keybindings_path = "";
122
123                 } else {
124
125                         keybindings_path = user_keybindings_path;
126                 }
127         }
128
129         /* if we still don't have a path at this point, use the default */
130
131         if (keybindings_path.empty()) {
132                 keybindings_path = default_bindings;
133         }
134
135         while (true) {
136
137                 if (!Glib::path_is_absolute (keybindings_path)) {
138
139                         /* not absolute - look in the usual places */
140                         std::string keybindings_file;
141
142                         if ( ! find_file (ardour_config_search_path(), keybindings_path, keybindings_file)) {
143
144                                 if (keybindings_path == default_bindings) {
145                                         error << string_compose (_("Default keybindings not found - %1 will be hard to use!"), PROGRAM_NAME) << endmsg;
146                                         return;
147                                 } else {
148                                         warning << string_compose (_("Key bindings file \"%1\" not found. Default bindings used instead"),
149                                                                    keybindings_path)
150                                                 << endmsg;
151                                         keybindings_path = default_bindings;
152                                 }
153
154                         } else {
155
156                                 /* use it */
157
158                                 keybindings_path = keybindings_file;
159                                 break;
160
161                         }
162
163                 } else {
164
165                         /* path is absolute already */
166
167                         if (!Glib::file_test (keybindings_path, Glib::FILE_TEST_EXISTS)) {
168                                 if (keybindings_path == default_bindings) {
169                                         error << string_compose (_("Default keybindings not found - %1 will be hard to use!"), PROGRAM_NAME) << endmsg;
170                                         return;
171                                 } else {
172                                         keybindings_path = default_bindings;
173                                 }
174
175                         } else {
176                                 break;
177                         }
178                 }
179         }
180
181         load_keybindings (keybindings_path);
182         
183         /* catch changes made via some GTK mechanism */
184
185         // GtkAccelMap* accelmap = gtk_accel_map_get();
186         // g_signal_connect (accelmap, "changed", (GCallback) accel_map_changed, this);
187 }
188
189 XMLNode&
190 ArdourKeyboard::get_state (void)
191 {
192         XMLNode* node = &Keyboard::get_state ();
193         char buf[32];
194
195         snprintf (buf, sizeof (buf), "%d", constraint_mod);
196         node->add_property ("constraint-modifier", buf);
197         snprintf (buf, sizeof (buf), "%d", trim_contents_mod);
198         node->add_property ("trim-contents-modifier", buf);
199         snprintf (buf, sizeof (buf), "%d", trim_overlap_mod);
200         node->add_property ("trim-overlap-modifier", buf);
201         snprintf (buf, sizeof (buf), "%d", trim_anchored_mod);
202         node->add_property ("trim-anchored-modifier", buf);
203         snprintf (buf, sizeof (buf), "%d", fine_adjust_mod);
204         node->add_property ("fine-adjust-modifier", buf);
205         snprintf (buf, sizeof (buf), "%d", push_points_mod);
206         node->add_property ("push-points-modifier", buf);
207         snprintf (buf, sizeof (buf), "%d", note_size_relative_mod);
208         node->add_property ("note-size-relative-modifier", buf);
209
210         return *node;
211 }
212
213 int
214 ArdourKeyboard::set_state (const XMLNode& node, int version)
215 {
216         const XMLProperty* prop;
217
218         if ((prop = node.property ("constraint-modifier")) != 0) {
219                 sscanf (prop->value().c_str(), "%d", &constraint_mod);
220         }
221
222         if ((prop = node.property ("trim-contents-modifier")) != 0) {
223                 sscanf (prop->value().c_str(), "%d", &trim_contents_mod);
224         }
225
226         if ((prop = node.property ("trim-overlap-modifier")) != 0) {
227                 sscanf (prop->value().c_str(), "%d", &trim_overlap_mod);
228         }
229
230         if ((prop = node.property ("trim-anchored-modifier")) != 0) {
231                 sscanf (prop->value().c_str(), "%d", &trim_anchored_mod);
232         }
233
234         if ((prop = node.property ("fine-adjust-modifier")) != 0) {
235                 sscanf (prop->value().c_str(), "%d", &fine_adjust_mod);
236         }
237
238         if ((prop = node.property ("push-points-modifier")) != 0) {
239                 sscanf (prop->value().c_str(), "%d", &push_points_mod);
240         }
241
242         if ((prop = node.property ("note-size-relative-modifier")) != 0) {
243                 sscanf (prop->value().c_str(), "%d", &note_size_relative_mod);
244         }
245
246         return Keyboard::set_state (node, version);
247 }
248
249 /* Snap and snap delta modifiers may contain each other, so we use the
250  * following two methods to sort that out:
251  */
252 bool
253 ArdourKeyboard::indicates_snap (guint state)
254 {
255         const bool contains_s = Keyboard::modifier_state_contains (state, Keyboard::snap_modifier ());
256         const bool contains_d = Keyboard::modifier_state_contains (state, Keyboard::snap_delta_modifier ());
257         const bool s_contains_d = Keyboard::modifier_state_contains (Keyboard::snap_modifier (), Keyboard::snap_delta_modifier ());
258
259         return  (contains_s && ((contains_d && s_contains_d) || !contains_d));
260 }
261
262 bool
263 ArdourKeyboard::indicates_snap_delta (guint state)
264 {
265         const bool contains_d = Keyboard::modifier_state_contains (state, Keyboard::snap_delta_modifier ());
266         const bool contains_s = Keyboard::modifier_state_contains (state, Keyboard::snap_modifier ());
267         const bool d_contains_s = Keyboard::modifier_state_contains (Keyboard::snap_delta_modifier (), Keyboard::snap_modifier ());
268
269         return (contains_d && ((contains_s && d_contains_s) || !contains_s));
270 }
271
272 void
273 ArdourKeyboard::set_constraint_modifier (guint mod)
274 {
275         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~constraint_mod);
276         constraint_mod = mod;
277         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | constraint_mod);
278 }
279
280 void
281 ArdourKeyboard::set_trim_contents_modifier (guint mod)
282 {
283         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~trim_contents_mod);
284         trim_contents_mod = mod;
285         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | trim_contents_mod);
286 }
287
288 void
289 ArdourKeyboard::set_trim_overlap_modifier (guint mod)
290 {
291         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~trim_overlap_mod);
292         trim_overlap_mod = mod;
293         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | trim_overlap_mod);
294 }
295
296 void
297 ArdourKeyboard::set_trim_anchored_modifier (guint mod)
298 {
299         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~trim_anchored_mod);
300         trim_anchored_mod = mod;
301         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | trim_anchored_mod);
302 }
303
304 void
305 ArdourKeyboard::set_fine_adjust_modifier (guint mod)
306 {
307         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~fine_adjust_mod);
308         fine_adjust_mod = mod;
309         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | fine_adjust_mod);
310 }
311
312 void
313 ArdourKeyboard::set_push_points_modifier (guint mod)
314 {
315         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~push_points_mod);
316         push_points_mod = mod;
317         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | push_points_mod);
318 }
319
320 void
321 ArdourKeyboard::set_note_size_relative_modifier (guint mod)
322 {
323         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask & ~note_size_relative_mod);
324         note_size_relative_mod = mod;
325         RelevantModifierKeyMask = GdkModifierType (RelevantModifierKeyMask | note_size_relative_mod);
326 }
327
328 Selection::Operation
329 ArdourKeyboard::selection_type (guint state)
330 {
331         /* note that there is no modifier for "Add" */
332
333         if (modifier_state_equals (state, RangeSelectModifier)) {
334                 return Selection::Extend;
335         } else if (modifier_state_equals (state, PrimaryModifier)) {
336                 return Selection::Toggle;
337         } else {
338                 return Selection::Set;
339         }
340 }