ignore unpaired noteoff's when writing part of a MidiModel to a new source. in realit...
[ardour2.git] / libs / gtkmm2ext / bindings.cc
blob6002f16371814dd9d12f07c10ef069bc805196d1
1 #include <iostream>
3 #include "pbd/xml++.h"
4 #include "pbd/convert.h"
6 #include "gtkmm2ext/actions.h"
7 #include "gtkmm2ext/bindings.h"
8 #include "gtkmm2ext/keyboard.h"
10 #include "i18n.h"
12 using namespace std;
13 using namespace Glib;
14 using namespace Gtk;
15 using namespace Gtkmm2ext;
17 uint32_t Bindings::_ignored_state = 0;
19 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
21 uint32_t ignore = Bindings::ignored_state();
23 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
24 /* key is not subject to case, so ignore SHIFT
26 ignore |= GDK_SHIFT_MASK;
29 _val = (state & ~ignore);
30 _val <<= 32;
31 _val |= keycode;
34 bool
35 MouseButton::make_button (const string& str, MouseButton& b)
37 int s = 0;
39 if (str.find ("Primary") != string::npos) {
40 s |= Keyboard::PrimaryModifier;
43 if (str.find ("Secondary") != string::npos) {
44 s |= Keyboard::SecondaryModifier;
47 if (str.find ("Tertiary") != string::npos) {
48 s |= Keyboard::TertiaryModifier;
51 if (str.find ("Level4") != string::npos) {
52 s |= Keyboard::Level4Modifier;
55 string::size_type lastmod = str.find_last_of ('-');
56 uint32_t button_number;
58 if (lastmod == string::npos) {
59 button_number = PBD::atoi (str);
60 } else {
61 button_number = PBD::atoi (str.substr (lastmod+1));
64 b = MouseButton (s, button_number);
65 return true;
68 string
69 MouseButton::name () const
71 int s = state();
73 string str;
75 if (s & Keyboard::PrimaryModifier) {
76 str += "Primary";
78 if (s & Keyboard::SecondaryModifier) {
79 if (!str.empty()) {
80 str += '-';
82 str += "Secondary";
84 if (s & Keyboard::TertiaryModifier) {
85 if (!str.empty()) {
86 str += '-';
88 str += "Tertiary";
90 if (s & Keyboard::Level4Modifier) {
91 if (!str.empty()) {
92 str += '-';
94 str += "Level4";
97 if (!str.empty()) {
98 str += '-';
101 char buf[16];
102 snprintf (buf, sizeof (buf), "%u", button());
103 str += buf;
105 return str;
108 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
110 uint32_t ignore = Bindings::ignored_state();
112 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
113 /* key is not subject to case, so ignore SHIFT
115 ignore |= GDK_SHIFT_MASK;
118 _val = (state & ~ignore);
119 _val <<= 32;
120 _val |= keycode;
124 string
125 KeyboardKey::name () const
127 int s = state();
129 string str;
131 if (s & Keyboard::PrimaryModifier) {
132 str += "Primary";
134 if (s & Keyboard::SecondaryModifier) {
135 if (!str.empty()) {
136 str += '-';
138 str += "Secondary";
140 if (s & Keyboard::TertiaryModifier) {
141 if (!str.empty()) {
142 str += '-';
144 str += "Tertiary";
146 if (s & Keyboard::Level4Modifier) {
147 if (!str.empty()) {
148 str += '-';
150 str += "Level4";
153 if (!str.empty()) {
154 str += '-';
157 str += gdk_keyval_name (key());
159 return str;
162 bool
163 KeyboardKey::make_key (const string& str, KeyboardKey& k)
165 int s = 0;
167 if (str.find ("Primary") != string::npos) {
168 s |= Keyboard::PrimaryModifier;
171 if (str.find ("Secondary") != string::npos) {
172 s |= Keyboard::SecondaryModifier;
175 if (str.find ("Tertiary") != string::npos) {
176 s |= Keyboard::TertiaryModifier;
179 if (str.find ("Level4") != string::npos) {
180 s |= Keyboard::Level4Modifier;
183 string::size_type lastmod = str.find_last_of ('-');
184 guint keyval;
186 if (lastmod == string::npos) {
187 keyval = gdk_keyval_from_name (str.c_str());
188 } else {
189 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
192 if (keyval == GDK_VoidSymbol) {
193 return false;
196 k = KeyboardKey (s, keyval);
197 return true;
200 Bindings::Bindings ()
201 : action_map (0)
205 Bindings::~Bindings()
209 void
210 Bindings::set_action_map (ActionMap& am)
212 action_map = &am;
213 press_bindings.clear ();
214 release_bindings.clear ();
217 bool
218 Bindings::activate (KeyboardKey kb, Operation op)
220 KeybindingMap* kbm;
222 switch (op) {
223 case Press:
224 kbm = &press_bindings;
225 break;
226 case Release:
227 kbm = &release_bindings;
228 break;
231 KeybindingMap::iterator k = kbm->find (kb);
233 if (k == kbm->end()) {
234 /* no entry for this key in the state map */
235 return false;
238 /* lets do it ... */
240 k->second->activate ();
241 return true;
244 void
245 Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what)
247 KeybindingMap* kbm;
249 switch (op) {
250 case Press:
251 kbm = &press_bindings;
252 break;
253 case Release:
254 kbm = &release_bindings;
255 break;
258 KeybindingMap::iterator k = kbm->find (kb);
260 if (k == kbm->end()) {
261 pair<KeyboardKey,RefPtr<Action> > newpair (kb, what);
262 kbm->insert (newpair);
263 cerr << "Bindings added " << kb.key() << " w/ " << kb.state() << " => " << what->get_name() << endl;
264 } else {
265 k->second = what;
269 void
270 Bindings::remove (KeyboardKey kb, Operation op)
272 KeybindingMap* kbm;
274 switch (op) {
275 case Press:
276 kbm = &press_bindings;
277 break;
278 case Release:
279 kbm = &release_bindings;
280 break;
283 KeybindingMap::iterator k = kbm->find (kb);
285 if (k != kbm->end()) {
286 kbm->erase (k);
290 bool
291 Bindings::activate (MouseButton bb, Operation op)
293 MouseButtonBindingMap* bbm;
295 switch (op) {
296 case Press:
297 bbm = &button_press_bindings;
298 break;
299 case Release:
300 bbm = &button_release_bindings;
301 break;
304 MouseButtonBindingMap::iterator b = bbm->find (bb);
306 if (b == bbm->end()) {
307 /* no entry for this key in the state map */
308 return false;
311 /* lets do it ... */
313 b->second->activate ();
314 return true;
317 void
318 Bindings::add (MouseButton bb, Operation op, RefPtr<Action> what)
320 MouseButtonBindingMap* bbm;
322 switch (op) {
323 case Press:
324 bbm = &button_press_bindings;
325 break;
326 case Release:
327 bbm = &button_release_bindings;
328 break;
331 MouseButtonBindingMap::iterator b = bbm->find (bb);
333 if (b == bbm->end()) {
334 pair<MouseButton,RefPtr<Action> > newpair (bb, what);
335 bbm->insert (newpair);
336 cerr << "Bindings added mouse button " << bb.button() << " w/ " << bb.state() << " => " << what->get_name() << endl;
337 } else {
338 b->second = what;
342 void
343 Bindings::remove (MouseButton bb, Operation op)
345 MouseButtonBindingMap* bbm;
347 switch (op) {
348 case Press:
349 bbm = &button_press_bindings;
350 break;
351 case Release:
352 bbm = &button_release_bindings;
353 break;
356 MouseButtonBindingMap::iterator b = bbm->find (bb);
358 if (b != bbm->end()) {
359 bbm->erase (b);
363 bool
364 Bindings::save (const string& path)
366 XMLTree tree;
367 XMLNode* root = new XMLNode (X_("Bindings"));
368 tree.set_root (root);
370 save (*root);
372 if (!tree.write (path)) {
373 ::unlink (path.c_str());
374 return false;
377 return true;
380 void
381 Bindings::save (XMLNode& root)
383 XMLNode* presses = new XMLNode (X_("Press"));
384 root.add_child_nocopy (*presses);
386 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
387 XMLNode* child;
388 child = new XMLNode (X_("Binding"));
389 child->add_property (X_("key"), k->first.name());
390 string ap = k->second->get_accel_path();
391 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
392 presses->add_child_nocopy (*child);
395 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
396 XMLNode* child;
397 child = new XMLNode (X_("Binding"));
398 child->add_property (X_("button"), k->first.name());
399 string ap = k->second->get_accel_path();
400 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
401 presses->add_child_nocopy (*child);
404 XMLNode* releases = new XMLNode (X_("Release"));
405 root.add_child_nocopy (*releases);
407 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
408 XMLNode* child;
409 child = new XMLNode (X_("Binding"));
410 child->add_property (X_("key"), k->first.name());
411 string ap = k->second->get_accel_path();
412 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
413 releases->add_child_nocopy (*child);
416 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
417 XMLNode* child;
418 child = new XMLNode (X_("Binding"));
419 child->add_property (X_("button"), k->first.name());
420 string ap = k->second->get_accel_path();
421 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
422 releases->add_child_nocopy (*child);
427 bool
428 Bindings::load (const string& path)
430 XMLTree tree;
432 if (!action_map) {
433 return false;
436 if (!tree.read (path)) {
437 return false;
440 press_bindings.clear ();
441 release_bindings.clear ();
443 XMLNode& root (*tree.root());
444 const XMLNodeList& children (root.children());
446 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
447 load (**i);
450 return true;
453 void
454 Bindings::load (const XMLNode& node)
456 if (node.name() == X_("Press") || node.name() == X_("Release")) {
458 Operation op;
460 if (node.name() == X_("Press")) {
461 op = Press;
462 } else {
463 op = Release;
466 const XMLNodeList& children (node.children());
468 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
470 XMLProperty* ap;
471 XMLProperty* kp;
472 XMLProperty* bp;
474 ap = (*p)->property ("action");
475 kp = (*p)->property ("key");
476 bp = (*p)->property ("button");
478 if (!ap || (!kp && !bp)) {
479 continue;
482 RefPtr<Action> act;
484 if (action_map) {
485 act = action_map->find_action (ap->value());
488 if (!act) {
489 string::size_type slash = ap->value().find ('/');
490 if (slash != string::npos) {
491 string group = ap->value().substr (0, slash);
492 string action = ap->value().substr (slash+1);
493 act = ActionManager::get_action (group.c_str(), action.c_str());
497 if (!act) {
498 continue;
501 if (kp) {
502 KeyboardKey k;
503 if (!KeyboardKey::make_key (kp->value(), k)) {
504 continue;
506 add (k, op, act);
507 } else {
508 MouseButton b;
509 if (!MouseButton::make_button (bp->value(), b)) {
510 continue;
512 add (b, op, act);
518 RefPtr<Action>
519 ActionMap::find_action (const string& name)
521 _ActionMap::iterator a = actions.find (name);
523 if (a != actions.end()) {
524 return a->second;
527 return RefPtr<Action>();
530 RefPtr<Action>
531 ActionMap::register_action (const char* path,
532 const char* name, const char* label, sigc::slot<void> sl)
534 string fullpath;
536 RefPtr<Action> act = Action::create (name, label);
538 act->signal_activate().connect (sl);
540 fullpath = path;
541 fullpath += '/';
542 fullpath += name;
544 actions.insert (_ActionMap::value_type (fullpath, act));
545 return act;
548 RefPtr<Action>
549 ActionMap::register_radio_action (const char* path, Gtk::RadioAction::Group& rgroup,
550 const char* name, const char* label,
551 sigc::slot<void,GtkAction*> sl,
552 int value)
554 string fullpath;
556 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
557 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
558 ract->property_value() = value;
560 act->signal_activate().connect (sigc::bind (sl, act->gobj()));
562 fullpath = path;
563 fullpath += '/';
564 fullpath += name;
566 actions.insert (_ActionMap::value_type (fullpath, act));
567 return act;
570 RefPtr<Action>
571 ActionMap::register_toggle_action (const char* path,
572 const char* name, const char* label, sigc::slot<void> sl)
574 string fullpath;
576 RefPtr<Action> act = ToggleAction::create (name, label);
578 act->signal_activate().connect (sl);
580 fullpath = path;
581 fullpath += '/';
582 fullpath += name;
584 actions.insert (_ActionMap::value_type (fullpath, act));
585 return act;