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"
23 #include <gtkmm/accelmap.h>
24 #include <gtkmm/uimanager.h>
26 #include "pbd/convert.h"
27 #include "pbd/debug.h"
28 #include "pbd/error.h"
29 #include "pbd/xml++.h"
31 #include "gtkmm2ext/actions.h"
32 #include "gtkmm2ext/bindings.h"
33 #include "gtkmm2ext/debug.h"
34 #include "gtkmm2ext/keyboard.h"
35 #include "gtkmm2ext/utils.h"
42 using namespace Gtkmm2ext;
45 uint32_t Bindings::_ignored_state = 0;
46 map<string,Bindings*> Bindings::bindings_for_state;
48 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
50 uint32_t ignore = Bindings::ignored_state();
52 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
53 /* key is not subject to case, so ignore SHIFT
55 ignore |= GDK_SHIFT_MASK;
58 _val = (state & ~ignore);
64 MouseButton::make_button (const string& str, MouseButton& b)
68 if (str.find ("Primary") != string::npos) {
69 s |= Keyboard::PrimaryModifier;
72 if (str.find ("Secondary") != string::npos) {
73 s |= Keyboard::SecondaryModifier;
76 if (str.find ("Tertiary") != string::npos) {
77 s |= Keyboard::TertiaryModifier;
80 if (str.find ("Level4") != string::npos) {
81 s |= Keyboard::Level4Modifier;
84 string::size_type lastmod = str.find_last_of ('-');
85 uint32_t button_number;
87 if (lastmod == string::npos) {
88 button_number = PBD::atoi (str);
90 button_number = PBD::atoi (str.substr (lastmod+1));
93 b = MouseButton (s, button_number);
98 MouseButton::name () const
104 if (s & Keyboard::PrimaryModifier) {
107 if (s & Keyboard::SecondaryModifier) {
113 if (s & Keyboard::TertiaryModifier) {
119 if (s & Keyboard::Level4Modifier) {
131 snprintf (buf, sizeof (buf), "%u", button());
137 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
139 uint32_t ignore = Bindings::ignored_state();
141 _val = (state & ~ignore);
148 KeyboardKey::name () const
154 if (s & Keyboard::PrimaryModifier) {
157 if (s & Keyboard::SecondaryModifier) {
163 if (s & Keyboard::TertiaryModifier) {
169 if (s & Keyboard::Level4Modifier) {
180 str += gdk_keyval_name (key());
186 KeyboardKey::make_key (const string& str, KeyboardKey& k)
190 if (str.find ("Primary") != string::npos) {
191 s |= Keyboard::PrimaryModifier;
194 if (str.find ("Secondary") != string::npos) {
195 s |= Keyboard::SecondaryModifier;
198 if (str.find ("Tertiary") != string::npos) {
199 s |= Keyboard::TertiaryModifier;
202 if (str.find ("Level4") != string::npos) {
203 s |= Keyboard::Level4Modifier;
206 string::size_type lastmod = str.find_last_of ('-');
209 if (lastmod == string::npos) {
210 keyval = gdk_keyval_from_name (str.c_str());
212 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
215 if (keyval == GDK_VoidSymbol) {
219 k = KeyboardKey (s, keyval);
224 Bindings::Bindings ()
229 Bindings::~Bindings()
231 if (!_name.empty()) {
232 remove_bindings_for_state (_name, *this);
237 Bindings::empty_keys() const
239 return press_bindings.empty() && release_bindings.empty();
243 Bindings::empty_mouse () const
245 return button_press_bindings.empty() && button_release_bindings.empty();
249 Bindings::empty() const
251 return empty_keys() && empty_mouse ();
255 Bindings::set_action_map (ActionMap& am)
258 press_bindings.clear ();
259 release_bindings.clear ();
263 Bindings::activate (KeyboardKey kb, Operation op)
265 KeybindingMap* kbm = 0;
269 kbm = &press_bindings;
272 kbm = &release_bindings;
276 KeybindingMap::iterator k = kbm->find (kb);
278 if (k == kbm->end()) {
279 /* no entry for this key in the state map */
280 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", kb));
286 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", kb, k->second->get_name()));
288 k->second->activate ();
293 Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what)
295 KeybindingMap* kbm = 0;
299 kbm = &press_bindings;
302 kbm = &release_bindings;
306 KeybindingMap::iterator k = kbm->find (kb);
308 if (k == kbm->end()) {
309 pair<KeyboardKey,RefPtr<Action> > newpair (kb, what);
310 kbm->insert (newpair);
315 Gtk::AccelKey gtk_key;
317 /* tweak the binding so that GTK will accept it and display something
321 uint32_t gtk_legal_keyval = kb.key();
322 possibly_translate_keyval_to_make_legal_accelerator (gtk_legal_keyval);
323 KeyboardKey gtk_binding (kb.state(), gtk_legal_keyval);
326 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
328 if (!entry_exists || gtk_key.get_key() == 0) {
329 Gtk::AccelMap::add_entry (what->get_accel_path(),
331 (Gdk::ModifierType) gtk_binding.state());
333 warning << string_compose (_("There is more than one key binding defined for %1. Both will work, but only the first will be visible in menus"), what->get_accel_path()) << endmsg;
336 if (!Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key) || gtk_key.get_key() == 0) {
337 cerr << "GTK binding using " << gtk_binding << " failed for " << what->get_accel_path() << " existing = " << gtk_key.get_key() << " + " << gtk_key.get_mod() << endl;
342 Bindings::remove (KeyboardKey kb, Operation op)
344 KeybindingMap* kbm = 0;
348 kbm = &press_bindings;
351 kbm = &release_bindings;
355 KeybindingMap::iterator k = kbm->find (kb);
357 if (k != kbm->end()) {
363 Bindings::activate (MouseButton bb, Operation op)
365 MouseButtonBindingMap* bbm = 0;
369 bbm = &button_press_bindings;
372 bbm = &button_release_bindings;
376 MouseButtonBindingMap::iterator b = bbm->find (bb);
378 if (b == bbm->end()) {
379 /* no entry for this key in the state map */
385 b->second->activate ();
390 Bindings::add (MouseButton bb, Operation op, RefPtr<Action> what)
392 MouseButtonBindingMap* bbm = 0;
396 bbm = &button_press_bindings;
399 bbm = &button_release_bindings;
403 MouseButtonBindingMap::iterator b = bbm->find (bb);
405 if (b == bbm->end()) {
406 pair<MouseButton,RefPtr<Action> > newpair (bb, what);
407 bbm->insert (newpair);
408 // cerr << "Bindings added mouse button " << bb.button() << " w/ " << bb.state() << " => " << what->get_name() << endl;
415 Bindings::remove (MouseButton bb, Operation op)
417 MouseButtonBindingMap* bbm = 0;
421 bbm = &button_press_bindings;
424 bbm = &button_release_bindings;
428 MouseButtonBindingMap::iterator b = bbm->find (bb);
430 if (b != bbm->end()) {
436 Bindings::save (const string& path)
439 XMLNode* root = new XMLNode (X_("Bindings"));
440 tree.set_root (root);
444 if (!tree.write (path)) {
445 ::g_unlink (path.c_str());
453 Bindings::save (XMLNode& root)
455 XMLNode* presses = new XMLNode (X_("Press"));
456 root.add_child_nocopy (*presses);
458 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
460 child = new XMLNode (X_("Binding"));
461 child->add_property (X_("key"), k->first.name());
462 string ap = k->second->get_accel_path();
463 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
464 presses->add_child_nocopy (*child);
467 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
469 child = new XMLNode (X_("Binding"));
470 child->add_property (X_("button"), k->first.name());
471 string ap = k->second->get_accel_path();
472 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
473 presses->add_child_nocopy (*child);
476 XMLNode* releases = new XMLNode (X_("Release"));
477 root.add_child_nocopy (*releases);
479 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
481 child = new XMLNode (X_("Binding"));
482 child->add_property (X_("key"), k->first.name());
483 string ap = k->second->get_accel_path();
484 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
485 releases->add_child_nocopy (*child);
488 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
490 child = new XMLNode (X_("Binding"));
491 child->add_property (X_("button"), k->first.name());
492 string ap = k->second->get_accel_path();
493 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
494 releases->add_child_nocopy (*child);
500 Bindings::load (string const & name)
508 XMLNode const * node = Keyboard::bindings_node();
511 error << string_compose (_("No keyboard binding information when loading bindings for \"%1\""), name) << endmsg;
515 if (!_name.empty()) {
516 remove_bindings_for_state (_name, *this);
519 const XMLNodeList& children (node->children());
522 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
524 if ((*i)->name() == X_("Bindings")) {
525 XMLProperty const * prop = (*i)->property (X_("name"));
531 if (prop->value() == name) {
540 error << string_compose (_("Bindings for \"%1\" not found in keyboard binding node\n"), name) << endmsg;
544 press_bindings.clear ();
545 release_bindings.clear ();
547 const XMLNodeList& bindings (node->children());
549 for (XMLNodeList::const_iterator i = bindings.begin(); i != bindings.end(); ++i) {
550 /* each node could be Press or Release */
554 add_bindings_for_state (_name, *this);
561 Bindings::load (const XMLNode& node)
563 if (node.name() == X_("Press") || node.name() == X_("Release")) {
567 if (node.name() == X_("Press")) {
573 const XMLNodeList& children (node.children());
575 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
581 ap = (*p)->property ("action");
582 kp = (*p)->property ("key");
583 bp = (*p)->property ("button");
585 if (!ap || (!kp && !bp)) {
592 act = action_map->find_action (ap->value());
596 string::size_type slash = ap->value().find ('/');
597 if (slash != string::npos) {
598 string group = ap->value().substr (0, slash);
599 string action = ap->value().substr (slash+1);
600 act = ActionManager::get_action (group.c_str(), action.c_str());
610 if (!KeyboardKey::make_key (kp->value(), k)) {
616 if (!MouseButton::make_button (bp->value(), b)) {
626 Bindings::get_all_actions (std::vector<std::string>& names,
627 std::vector<std::string>& paths,
628 std::vector<std::string>& tooltips,
629 std::vector<std::string>& keys,
630 std::vector<KeyboardKey>& bindings)
636 /* build a reverse map from actions to bindings */
638 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
641 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
642 rmap.insert (make_pair (k->second, k->first));
645 /* get a list of all actions */
647 ActionMap::Actions actions;
648 action_map->get_actions (actions);
650 for (ActionMap::Actions::const_iterator act = actions.begin(); act != actions.end(); ++act) {
651 names.push_back ((*act)->get_name());
652 paths.push_back ((*act)->get_accel_path());
653 tooltips.push_back ((*act)->get_tooltip());
655 ReverseMap::iterator r = rmap.find (*act);
656 if (r != rmap.end()) {
657 keys.push_back (gtk_accelerator_get_label (r->second.key(), (GdkModifierType) r->second.state()));
658 bindings.push_back (r->second);
660 keys.push_back (string());
661 bindings.push_back (KeyboardKey::null_key());
667 Bindings::get_all_actions (std::vector<std::string>& groups,
668 std::vector<std::string>& paths,
669 std::vector<std::string>& tooltips,
670 std::vector<KeyboardKey>& bindings)
672 /* build a reverse map from actions to bindings */
674 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
677 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
678 rmap.insert (make_pair (k->second, k->first));
681 /* get a list of all actions */
683 ActionMap::Actions actions;
684 action_map->get_actions (actions);
686 for (ActionMap::Actions::const_iterator act = actions.begin(); act != actions.end(); ++act) {
687 groups.push_back ((*act)->get_name());
688 paths.push_back ((*act)->get_accel_path());
689 tooltips.push_back ((*act)->get_tooltip());
691 ReverseMap::iterator r = rmap.find (*act);
692 if (r != rmap.end()) {
693 bindings.push_back (r->second);
695 bindings.push_back (KeyboardKey::null_key());
701 ActionMap::get_actions (ActionMap::Actions& acts)
703 for (_ActionMap::iterator a = actions.begin(); a != actions.end(); ++a) {
704 acts.push_back (a->second);
709 ActionMap::find_action (const string& name)
711 _ActionMap::iterator a = actions.find (name);
713 if (a != actions.end()) {
717 return RefPtr<Action>();
721 ActionMap::create_action_group (const string& name)
723 RefPtr<ActionGroup> g = ActionGroup::create (name);
728 ActionMap::install_action_group (RefPtr<ActionGroup> group)
730 ActionManager::ui_manager->insert_action_group (group);
734 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
738 RefPtr<Action> act = Action::create (name, label);
740 fullpath = group->get_name();
744 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
749 /* already registered */
750 return RefPtr<Action> ();
754 ActionMap::register_action (RefPtr<ActionGroup> group,
755 const char* name, const char* label, sigc::slot<void> sl)
759 RefPtr<Action> act = Action::create (name, label);
761 fullpath = group->get_name();
765 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
766 group->add (act, sl);
770 /* already registered */
771 return RefPtr<Action>();
775 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
776 Gtk::RadioAction::Group& rgroup,
777 const char* name, const char* label,
782 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
783 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
785 fullpath = group->get_name();
789 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
790 group->add (act, sl);
794 /* already registered */
795 return RefPtr<Action>();
799 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
800 Gtk::RadioAction::Group& rgroup,
801 const char* name, const char* label,
802 sigc::slot<void,GtkAction*> sl,
807 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
808 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
809 ract->property_value() = value;
811 fullpath = group->get_name();
815 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
816 group->add (act, sigc::bind (sl, act->gobj()));
820 /* already registered */
822 return RefPtr<Action>();
826 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
827 const char* name, const char* label, sigc::slot<void> sl)
831 fullpath = group->get_name();
835 RefPtr<Action> act = ToggleAction::create (name, label);
837 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
838 group->add (act, sl);
842 /* already registered */
843 return RefPtr<Action>();
847 Bindings::add_bindings_for_state (std::string const& name, Bindings& bindings)
849 bindings_for_state.insert (make_pair (name, &bindings));
853 Bindings::remove_bindings_for_state (std::string const& name, Bindings& bindings)
855 bindings_for_state.erase (name);
858 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
859 return out << "Key " << k.key() << " (" << (k.key() > 0 ? gdk_keyval_name (k.key()) : "no-key") << ") state " << k.state();