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