2 Copyright (C) 2012 Paul Davis
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.
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.
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.
22 #include "pbd/gstdio_compat.h"
24 #include "pbd/xml++.h"
25 #include "pbd/convert.h"
27 #include "gtkmm2ext/actions.h"
28 #include "gtkmm2ext/bindings.h"
29 #include "gtkmm2ext/keyboard.h"
36 using namespace Gtkmm2ext;
38 uint32_t Bindings::_ignored_state = 0;
40 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
42 uint32_t ignore = Bindings::ignored_state();
44 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
45 /* key is not subject to case, so ignore SHIFT
47 ignore |= GDK_SHIFT_MASK;
50 _val = (state & ~ignore);
56 MouseButton::make_button (const string& str, MouseButton& b)
60 if (str.find ("Primary") != string::npos) {
61 s |= Keyboard::PrimaryModifier;
64 if (str.find ("Secondary") != string::npos) {
65 s |= Keyboard::SecondaryModifier;
68 if (str.find ("Tertiary") != string::npos) {
69 s |= Keyboard::TertiaryModifier;
72 if (str.find ("Level4") != string::npos) {
73 s |= Keyboard::Level4Modifier;
76 string::size_type lastmod = str.find_last_of ('-');
77 uint32_t button_number;
79 if (lastmod == string::npos) {
80 button_number = PBD::atoi (str);
82 button_number = PBD::atoi (str.substr (lastmod+1));
85 b = MouseButton (s, button_number);
90 MouseButton::name () const
96 if (s & Keyboard::PrimaryModifier) {
99 if (s & Keyboard::SecondaryModifier) {
105 if (s & Keyboard::TertiaryModifier) {
111 if (s & Keyboard::Level4Modifier) {
123 snprintf (buf, sizeof (buf), "%u", button());
129 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
131 uint32_t ignore = Bindings::ignored_state();
133 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
134 /* key is not subject to case, so ignore SHIFT
136 ignore |= GDK_SHIFT_MASK;
139 _val = (state & ~ignore);
146 KeyboardKey::name () const
152 if (s & Keyboard::PrimaryModifier) {
155 if (s & Keyboard::SecondaryModifier) {
161 if (s & Keyboard::TertiaryModifier) {
167 if (s & Keyboard::Level4Modifier) {
178 str += gdk_keyval_name (key());
184 KeyboardKey::make_key (const string& str, KeyboardKey& k)
188 if (str.find ("Primary") != string::npos) {
189 s |= Keyboard::PrimaryModifier;
192 if (str.find ("Secondary") != string::npos) {
193 s |= Keyboard::SecondaryModifier;
196 if (str.find ("Tertiary") != string::npos) {
197 s |= Keyboard::TertiaryModifier;
200 if (str.find ("Level4") != string::npos) {
201 s |= Keyboard::Level4Modifier;
204 string::size_type lastmod = str.find_last_of ('-');
207 if (lastmod == string::npos) {
208 keyval = gdk_keyval_from_name (str.c_str());
210 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
213 if (keyval == GDK_VoidSymbol) {
217 k = KeyboardKey (s, keyval);
221 Bindings::Bindings ()
226 Bindings::~Bindings()
231 Bindings::empty_keys() const
233 return press_bindings.empty() && release_bindings.empty();
237 Bindings::empty_mouse () const
239 return button_press_bindings.empty() && button_release_bindings.empty();
243 Bindings::empty() const
245 return empty_keys() && empty_mouse ();
249 Bindings::set_action_map (ActionMap& am)
252 press_bindings.clear ();
253 release_bindings.clear ();
257 Bindings::activate (KeyboardKey kb, Operation op)
259 KeybindingMap* kbm = 0;
263 kbm = &press_bindings;
266 kbm = &release_bindings;
270 KeybindingMap::iterator k = kbm->find (kb);
272 if (k == kbm->end()) {
273 /* no entry for this key in the state map */
279 k->second->activate ();
284 Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what)
286 KeybindingMap* kbm = 0;
290 kbm = &press_bindings;
293 kbm = &release_bindings;
297 KeybindingMap::iterator k = kbm->find (kb);
299 if (k == kbm->end()) {
300 pair<KeyboardKey,RefPtr<Action> > newpair (kb, what);
301 kbm->insert (newpair);
308 Bindings::remove (KeyboardKey kb, Operation op)
310 KeybindingMap* kbm = 0;
314 kbm = &press_bindings;
317 kbm = &release_bindings;
321 KeybindingMap::iterator k = kbm->find (kb);
323 if (k != kbm->end()) {
329 Bindings::activate (MouseButton bb, Operation op)
331 MouseButtonBindingMap* bbm = 0;
335 bbm = &button_press_bindings;
338 bbm = &button_release_bindings;
342 MouseButtonBindingMap::iterator b = bbm->find (bb);
344 if (b == bbm->end()) {
345 /* no entry for this key in the state map */
351 b->second->activate ();
356 Bindings::add (MouseButton bb, Operation op, RefPtr<Action> what)
358 MouseButtonBindingMap* bbm = 0;
362 bbm = &button_press_bindings;
365 bbm = &button_release_bindings;
369 MouseButtonBindingMap::iterator b = bbm->find (bb);
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;
381 Bindings::remove (MouseButton bb, Operation op)
383 MouseButtonBindingMap* bbm = 0;
387 bbm = &button_press_bindings;
390 bbm = &button_release_bindings;
394 MouseButtonBindingMap::iterator b = bbm->find (bb);
396 if (b != bbm->end()) {
402 Bindings::save (const string& path)
405 XMLNode* root = new XMLNode (X_("Bindings"));
406 tree.set_root (root);
410 if (!tree.write (path)) {
411 ::g_unlink (path.c_str());
419 Bindings::save (XMLNode& root)
421 XMLNode* presses = new XMLNode (X_("Press"));
422 root.add_child_nocopy (*presses);
424 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
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);
433 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
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);
442 XMLNode* releases = new XMLNode (X_("Release"));
443 root.add_child_nocopy (*releases);
445 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
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);
454 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
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);
466 Bindings::load (const string& path)
474 if (!tree.read (path)) {
478 press_bindings.clear ();
479 release_bindings.clear ();
481 XMLNode& root (*tree.root());
482 const XMLNodeList& children (root.children());
484 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
492 Bindings::load (const XMLNode& node)
494 if (node.name() == X_("Press") || node.name() == X_("Release")) {
498 if (node.name() == X_("Press")) {
504 const XMLNodeList& children (node.children());
506 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
512 ap = (*p)->property ("action");
513 kp = (*p)->property ("key");
514 bp = (*p)->property ("button");
516 if (!ap || (!kp && !bp)) {
523 act = action_map->find_action (ap->value());
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());
541 if (!KeyboardKey::make_key (kp->value(), k)) {
547 if (!MouseButton::make_button (bp->value(), b)) {
557 ActionMap::find_action (const string& name)
559 _ActionMap::iterator a = actions.find (name);
561 if (a != actions.end()) {
565 return RefPtr<Action>();
569 ActionMap::register_action (const char* path,
570 const char* name, const char* label, sigc::slot<void> sl)
574 RefPtr<Action> act = Action::create (name, label);
576 act->signal_activate().connect (sl);
582 actions.insert (_ActionMap::value_type (fullpath, act));
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,
594 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
595 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
596 ract->property_value() = value;
598 act->signal_activate().connect (sigc::bind (sl, act->gobj()));
604 actions.insert (_ActionMap::value_type (fullpath, act));
609 ActionMap::register_toggle_action (const char* path,
610 const char* name, const char* label, sigc::slot<void> sl)
614 RefPtr<Action> act = ToggleAction::create (name, label);
616 act->signal_activate().connect (sl);
622 actions.insert (_ActionMap::value_type (fullpath, act));
626 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey& k) {
627 return out << "Key " << k.key() << " state " << k.state();