widespread changes to get the new (oldArdour binding scheme to be used for keyboard...
[ardour.git] / libs / gtkmm2ext / bindings.cc
1 /*
2     Copyright (C) 2012 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 <iostream>
21
22 #include "pbd/gstdio_compat.h"
23
24 #include "pbd/xml++.h"
25 #include "pbd/convert.h"
26
27 #include "gtkmm2ext/actions.h"
28 #include "gtkmm2ext/bindings.h"
29 #include "gtkmm2ext/keyboard.h"
30
31 #include "i18n.h"
32
33 using namespace std;
34 using namespace Glib;
35 using namespace Gtk;
36 using namespace Gtkmm2ext;
37
38 uint32_t Bindings::_ignored_state = 0;
39
40 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
41 {
42         uint32_t ignore = Bindings::ignored_state();
43
44         if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
45                 /* key is not subject to case, so ignore SHIFT
46                  */
47                 ignore |= GDK_SHIFT_MASK;
48         }
49
50         _val = (state & ~ignore);
51         _val <<= 32;
52         _val |= keycode;
53 };
54
55 bool
56 MouseButton::make_button (const string& str, MouseButton& b)
57 {
58         int s = 0;
59
60         if (str.find ("Primary") != string::npos) {
61                 s |= Keyboard::PrimaryModifier;
62         }
63
64         if (str.find ("Secondary") != string::npos) {
65                 s |= Keyboard::SecondaryModifier;
66         }
67
68         if (str.find ("Tertiary") != string::npos) {
69                 s |= Keyboard::TertiaryModifier;
70         }
71
72         if (str.find ("Level4") != string::npos) {
73                 s |= Keyboard::Level4Modifier;
74         }
75
76         string::size_type lastmod = str.find_last_of ('-');
77         uint32_t button_number;
78
79         if (lastmod == string::npos) {
80                 button_number = PBD::atoi (str);
81         } else {
82                 button_number = PBD::atoi (str.substr (lastmod+1));
83         }
84
85         b = MouseButton (s, button_number);
86         return true;
87 }
88
89 string
90 MouseButton::name () const
91 {
92         int s = state();
93
94         string str;
95
96         if (s & Keyboard::PrimaryModifier) {
97                 str += "Primary";
98         }
99         if (s & Keyboard::SecondaryModifier) {
100                 if (!str.empty()) {
101                         str += '-';
102                 }
103                 str += "Secondary";
104         }
105         if (s & Keyboard::TertiaryModifier) {
106                 if (!str.empty()) {
107                         str += '-';
108                 }
109                 str += "Tertiary";
110         }
111         if (s & Keyboard::Level4Modifier) {
112                 if (!str.empty()) {
113                         str += '-';
114                 }
115                 str += "Level4";
116         }
117
118         if (!str.empty()) {
119                 str += '-';
120         }
121
122         char buf[16];
123         snprintf (buf, sizeof (buf), "%u", button());
124         str += buf;
125
126         return str;
127 }
128
129 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
130 {
131         uint32_t ignore = Bindings::ignored_state();
132
133         if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
134                 /* key is not subject to case, so ignore SHIFT
135                  */
136                 ignore |= GDK_SHIFT_MASK;
137         }
138
139         _val = (state & ~ignore);
140         _val <<= 32;
141         _val |= keycode;
142 };
143
144
145 string
146 KeyboardKey::name () const
147 {
148         int s = state();
149
150         string str;
151
152         if (s & Keyboard::PrimaryModifier) {
153                 str += "Primary";
154         }
155         if (s & Keyboard::SecondaryModifier) {
156                 if (!str.empty()) {
157                         str += '-';
158                 }
159                 str += "Secondary";
160         }
161         if (s & Keyboard::TertiaryModifier) {
162                 if (!str.empty()) {
163                         str += '-';
164                 }
165                 str += "Tertiary";
166         }
167         if (s & Keyboard::Level4Modifier) {
168                 if (!str.empty()) {
169                         str += '-';
170                 }
171                 str += "Level4";
172         }
173
174         if (!str.empty()) {
175                 str += '-';
176         }
177
178         str += gdk_keyval_name (key());
179
180         return str;
181 }
182
183 bool
184 KeyboardKey::make_key (const string& str, KeyboardKey& k)
185 {
186         int s = 0;
187
188         if (str.find ("Primary") != string::npos) {
189                 s |= Keyboard::PrimaryModifier;
190         }
191
192         if (str.find ("Secondary") != string::npos) {
193                 s |= Keyboard::SecondaryModifier;
194         }
195
196         if (str.find ("Tertiary") != string::npos) {
197                 s |= Keyboard::TertiaryModifier;
198         }
199
200         if (str.find ("Level4") != string::npos) {
201                 s |= Keyboard::Level4Modifier;
202         }
203
204         string::size_type lastmod = str.find_last_of ('-');
205         guint keyval;
206
207         if (lastmod == string::npos) {
208                 keyval = gdk_keyval_from_name (str.c_str());
209         } else {
210                 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
211         }
212
213         if (keyval == GDK_VoidSymbol) {
214                 return false;
215         }
216
217         k = KeyboardKey (s, keyval);
218         return true;
219 }
220
221 Bindings::Bindings ()
222         : action_map (0)
223 {
224 }
225
226 Bindings::~Bindings()
227 {
228 }
229
230 bool
231 Bindings::empty_keys() const
232 {
233         return press_bindings.empty() && release_bindings.empty();
234 }
235
236 bool
237 Bindings::empty_mouse () const
238 {
239         return button_press_bindings.empty() && button_release_bindings.empty();
240 }
241
242 bool
243 Bindings::empty() const
244 {
245         return empty_keys() && empty_mouse ();
246 }
247
248 void
249 Bindings::set_action_map (ActionMap& am)
250 {
251         action_map = &am;
252         press_bindings.clear ();
253         release_bindings.clear ();
254 }
255
256 bool
257 Bindings::activate (KeyboardKey kb, Operation op)
258 {
259         KeybindingMap* kbm = 0;
260
261         switch (op) {
262         case Press:
263                 kbm = &press_bindings;
264                 break;
265         case Release:
266                 kbm = &release_bindings;
267                 break;
268         }
269
270         KeybindingMap::iterator k = kbm->find (kb);
271
272         if (k == kbm->end()) {
273                 /* no entry for this key in the state map */
274                 return false;
275         }
276
277         /* lets do it ... */
278
279         k->second->activate ();
280         return true;
281 }
282
283 void
284 Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what)
285 {
286         KeybindingMap* kbm = 0;
287
288         switch (op) {
289         case Press:
290                 kbm = &press_bindings;
291                 break;
292         case Release:
293                 kbm = &release_bindings;
294                 break;
295         }
296
297         KeybindingMap::iterator k = kbm->find (kb);
298
299         if (k == kbm->end()) {
300                 pair<KeyboardKey,RefPtr<Action> > newpair (kb, what);
301                 kbm->insert (newpair);
302         } else {
303                 k->second = what;
304         }
305 }
306
307 void
308 Bindings::remove (KeyboardKey kb, Operation op)
309 {
310         KeybindingMap* kbm = 0;
311
312         switch (op) {
313         case Press:
314                 kbm = &press_bindings;
315                 break;
316         case Release:
317                 kbm = &release_bindings;
318                 break;
319         }
320
321         KeybindingMap::iterator k = kbm->find (kb);
322
323         if (k != kbm->end()) {
324                 kbm->erase (k);
325         }
326 }
327
328 bool
329 Bindings::activate (MouseButton bb, Operation op)
330 {
331         MouseButtonBindingMap* bbm = 0;
332
333         switch (op) {
334         case Press:
335                 bbm = &button_press_bindings;
336                 break;
337         case Release:
338                 bbm = &button_release_bindings;
339                 break;
340         }
341
342         MouseButtonBindingMap::iterator b = bbm->find (bb);
343
344         if (b == bbm->end()) {
345                 /* no entry for this key in the state map */
346                 return false;
347         }
348
349         /* lets do it ... */
350
351         b->second->activate ();
352         return true;
353 }
354
355 void
356 Bindings::add (MouseButton bb, Operation op, RefPtr<Action> what)
357 {
358         MouseButtonBindingMap* bbm = 0;
359
360         switch (op) {
361         case Press:
362                 bbm = &button_press_bindings;
363                 break;
364         case Release:
365                 bbm = &button_release_bindings;
366                 break;
367         }
368
369         MouseButtonBindingMap::iterator b = bbm->find (bb);
370
371         if (b == bbm->end()) {
372                 pair<MouseButton,RefPtr<Action> > newpair (bb, what);
373                 bbm->insert (newpair);
374                 // cerr << "Bindings added mouse button " << bb.button() << " w/ " << bb.state() << " => " << what->get_name() << endl;
375         } else {
376                 b->second = what;
377         }
378 }
379
380 void
381 Bindings::remove (MouseButton bb, Operation op)
382 {
383         MouseButtonBindingMap* bbm = 0;
384
385         switch (op) {
386         case Press:
387                 bbm = &button_press_bindings;
388                 break;
389         case Release:
390                 bbm = &button_release_bindings;
391                 break;
392         }
393
394         MouseButtonBindingMap::iterator b = bbm->find (bb);
395
396         if (b != bbm->end()) {
397                 bbm->erase (b);
398         }
399 }
400
401 bool
402 Bindings::save (const string& path)
403 {
404         XMLTree tree;
405         XMLNode* root = new XMLNode (X_("Bindings"));
406         tree.set_root (root);
407
408         save (*root);
409
410         if (!tree.write (path)) {
411                 ::g_unlink (path.c_str());
412                 return false;
413         }
414
415         return true;
416 }
417
418 void
419 Bindings::save (XMLNode& root)
420 {
421         XMLNode* presses = new XMLNode (X_("Press"));
422         root.add_child_nocopy (*presses);
423
424         for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
425                 XMLNode* child;
426                 child = new XMLNode (X_("Binding"));
427                 child->add_property (X_("key"), k->first.name());
428                 string ap = k->second->get_accel_path();
429                 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
430                 presses->add_child_nocopy (*child);
431         }
432
433         for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
434                 XMLNode* child;
435                 child = new XMLNode (X_("Binding"));
436                 child->add_property (X_("button"), k->first.name());
437                 string ap = k->second->get_accel_path();
438                 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
439                 presses->add_child_nocopy (*child);
440         }
441
442         XMLNode* releases = new XMLNode (X_("Release"));
443         root.add_child_nocopy (*releases);
444
445         for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
446                 XMLNode* child;
447                 child = new XMLNode (X_("Binding"));
448                 child->add_property (X_("key"), k->first.name());
449                 string ap = k->second->get_accel_path();
450                 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
451                 releases->add_child_nocopy (*child);
452         }
453
454         for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
455                 XMLNode* child;
456                 child = new XMLNode (X_("Binding"));
457                 child->add_property (X_("button"), k->first.name());
458                 string ap = k->second->get_accel_path();
459                 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
460                 releases->add_child_nocopy (*child);
461         }
462
463 }
464
465 bool
466 Bindings::load (const string& path)
467 {
468         XMLTree tree;
469
470         if (!action_map) {
471                 return false;
472         }
473
474         if (!tree.read (path)) {
475                 return false;
476         }
477
478         press_bindings.clear ();
479         release_bindings.clear ();
480
481         XMLNode& root (*tree.root());
482         const XMLNodeList& children (root.children());
483
484         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
485                 load (**i);
486         }
487
488         return true;
489 }
490
491 void
492 Bindings::load (const XMLNode& node)
493 {
494         if (node.name() == X_("Press") || node.name() == X_("Release")) {
495
496                 Operation op;
497
498                 if (node.name() == X_("Press")) {
499                         op = Press;
500                 } else {
501                         op = Release;
502                 }
503
504                 const XMLNodeList& children (node.children());
505
506                 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
507
508                         XMLProperty* ap;
509                         XMLProperty* kp;
510                         XMLProperty* bp;
511
512                         ap = (*p)->property ("action");
513                         kp = (*p)->property ("key");
514                         bp = (*p)->property ("button");
515
516                         if (!ap || (!kp && !bp)) {
517                                 continue;
518                         }
519
520                         RefPtr<Action> act;
521
522                         if (action_map) {
523                                 act = action_map->find_action (ap->value());
524                         }
525
526                         if (!act) {
527                                 string::size_type slash = ap->value().find ('/');
528                                 if (slash != string::npos) {
529                                         string group = ap->value().substr (0, slash);
530                                         string action = ap->value().substr (slash+1);
531                                         act = ActionManager::get_action (group.c_str(), action.c_str());
532                                 }
533                         }
534
535                         if (!act) {
536                                 continue;
537                         }
538
539                         if (kp) {
540                                 KeyboardKey k;
541                                 if (!KeyboardKey::make_key (kp->value(), k)) {
542                                         continue;
543                                 }
544                                 add (k, op, act);
545                         } else {
546                                 MouseButton b;
547                                 if (!MouseButton::make_button (bp->value(), b)) {
548                                         continue;
549                                 }
550                                 add (b, op, act);
551                         }
552                 }
553         }
554 }
555
556 RefPtr<Action>
557 ActionMap::find_action (const string& name)
558 {
559         _ActionMap::iterator a = actions.find (name);
560
561         if (a != actions.end()) {
562                 return a->second;
563         }
564
565         return RefPtr<Action>();
566 }
567
568 RefPtr<Action>
569 ActionMap::register_action (const char* path,
570                             const char* name, const char* label, sigc::slot<void> sl)
571 {
572         string fullpath;
573
574         RefPtr<Action> act = Action::create (name, label);
575
576         act->signal_activate().connect (sl);
577
578         fullpath = path;
579         fullpath += '/';
580         fullpath += name;
581
582         actions.insert (_ActionMap::value_type (fullpath, act));
583         return act;
584 }
585
586 RefPtr<Action>
587 ActionMap::register_radio_action (const char* path, Gtk::RadioAction::Group& rgroup,
588                                   const char* name, const char* label,
589                                   sigc::slot<void,GtkAction*> sl,
590                                   int value)
591 {
592         string fullpath;
593
594         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
595         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
596         ract->property_value() = value;
597
598         act->signal_activate().connect (sigc::bind (sl, act->gobj()));
599
600         fullpath = path;
601         fullpath += '/';
602         fullpath += name;
603
604         actions.insert (_ActionMap::value_type (fullpath, act));
605         return act;
606 }
607
608 RefPtr<Action>
609 ActionMap::register_toggle_action (const char* path,
610                                    const char* name, const char* label, sigc::slot<void> sl)
611 {
612         string fullpath;
613
614         RefPtr<Action> act = ToggleAction::create (name, label);
615
616         act->signal_activate().connect (sl);
617
618         fullpath = path;
619         fullpath += '/';
620         fullpath += name;
621
622         actions.insert (_ActionMap::value_type (fullpath, act));
623         return act;
624 }
625
626 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey& k) {
627         return out << "Key " << k.key() << " state " << k.state();
628 }
629