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