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 list<Bindings*> Bindings::bindings; /* global. Gulp */
46 list<ActionMap*> ActionMap::action_maps; /* global. Gulp */
47 PBD::Signal1<void,Bindings*> Bindings::BindingsChanged;
49 template <typename IteratorValueType>
50 struct ActionNameRegistered
52 ActionNameRegistered(std::string const& name)
56 bool operator()(IteratorValueType elem) const {
57 return elem.second.action_name == action_name;
59 std::string const& action_name;
62 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
64 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
66 /* this is a slightly wierd test that relies on
67 * gdk_keyval_is_{upper,lower}() returning true for keys that have no
68 * case-sensitivity. This covers mostly non-alphanumeric keys.
71 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
72 /* key is not subject to case, so ignore SHIFT
74 ignore |= GDK_SHIFT_MASK;
77 _val = (state & ~ignore);
83 MouseButton::make_button (const string& str, MouseButton& b)
87 if (str.find ("Primary") != string::npos) {
88 s |= Keyboard::PrimaryModifier;
91 if (str.find ("Secondary") != string::npos) {
92 s |= Keyboard::SecondaryModifier;
95 if (str.find ("Tertiary") != string::npos) {
96 s |= Keyboard::TertiaryModifier;
99 if (str.find ("Level4") != string::npos) {
100 s |= Keyboard::Level4Modifier;
103 string::size_type lastmod = str.find_last_of ('-');
104 uint32_t button_number;
106 if (lastmod == string::npos) {
107 button_number = PBD::atoi (str);
109 button_number = PBD::atoi (str.substr (lastmod+1));
112 b = MouseButton (s, button_number);
117 MouseButton::name () const
123 if (s & Keyboard::PrimaryModifier) {
126 if (s & Keyboard::SecondaryModifier) {
132 if (s & Keyboard::TertiaryModifier) {
138 if (s & Keyboard::Level4Modifier) {
150 snprintf (buf, sizeof (buf), "%u", button());
156 /*================================ KeyboardKey ================================*/
157 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
159 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
161 _val = (state & ~ignore);
167 KeyboardKey::display_label () const
173 /* This magically returns a string that will display the right thing
174 * on all platforms, notably the command key on OS X.
177 uint32_t mod = state();
180 /* We use both bits (MOD2|META) for Primary on OS X,
181 * but we don't want MOD2 showing up in listings.
184 if (mod & GDK_MOD2_MASK) {
185 mod &= ~GDK_MOD2_MASK;
189 return gtk_accelerator_get_label (key(), (GdkModifierType) mod);
193 KeyboardKey::name () const
199 if (s & Keyboard::PrimaryModifier) {
202 if (s & Keyboard::SecondaryModifier) {
208 if (s & Keyboard::TertiaryModifier) {
214 if (s & Keyboard::Level4Modifier) {
225 char const *gdk_name = gdk_keyval_name (key());
238 KeyboardKey::make_key (const string& str, KeyboardKey& k)
242 if (str.find ("Primary") != string::npos) {
243 s |= Keyboard::PrimaryModifier;
246 if (str.find ("Secondary") != string::npos) {
247 s |= Keyboard::SecondaryModifier;
250 if (str.find ("Tertiary") != string::npos) {
251 s |= Keyboard::TertiaryModifier;
254 if (str.find ("Level4") != string::npos) {
255 s |= Keyboard::Level4Modifier;
258 /* since all SINGLE key events keycodes are changed to lower case
259 * before looking them up, make sure we only store lower case here. The
260 * Shift part will be stored in the modifier part of the KeyboardKey.
262 * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
267 string::size_type lastmod = str.find_last_of ('-');
269 if (lastmod != string::npos) {
270 actual = str.substr (lastmod+1);
276 if (actual.size() == 1) {
277 actual = PBD::downcase (actual);
281 keyval = gdk_keyval_from_name (actual.c_str());
283 if (keyval == GDK_VoidSymbol || keyval == 0) {
287 k = KeyboardKey (s, keyval);
292 /*================================= Bindings =================================*/
293 Bindings::Bindings (std::string const& name)
297 bindings.push_back (this);
300 Bindings::~Bindings()
302 bindings.remove (this);
306 Bindings::ardour_action_name (RefPtr<Action> action)
308 /* Skip "<Actions>/" */
309 return action->get_accel_path ().substr (10);
313 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
315 const string action_name = ardour_action_name (action);
317 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
319 /* option one: action has already been associated with the
323 if (k->second.action == action) {
327 /* option two: action name matches, so lookup the action,
328 * setup the association while we're here, and return the binding.
331 if (_action_map && k->second.action_name == action_name) {
332 k->second.action = _action_map->find_action (action_name);
338 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
340 /* option one: action has already been associated with the
344 if (k->second.action == action) {
348 /* option two: action name matches, so lookup the action,
349 * setup the association while we're here, and return the binding.
352 if (_action_map && k->second.action_name == action_name) {
353 k->second.action = _action_map->find_action (action_name);
359 return KeyboardKey::null_key();
363 Bindings::set_action_map (ActionMap& actions)
366 _action_map->set_bindings (0);
369 _action_map = &actions;
370 _action_map->set_bindings (this);
377 Bindings::empty_keys() const
379 return press_bindings.empty() && release_bindings.empty();
383 Bindings::empty_mouse () const
385 return button_press_bindings.empty() && button_release_bindings.empty();
389 Bindings::empty() const
391 return empty_keys() && empty_mouse ();
395 Bindings::activate (KeyboardKey kb, Operation op)
397 KeybindingMap& kbm = get_keymap (op);
399 /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
400 Our bindings all use the lower case character/keyname, so switch
401 to the lower case before doing the lookup.
404 KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
406 KeybindingMap::iterator k = kbm.find (unshifted);
408 if (k == kbm.end()) {
409 /* no entry for this key in the state map */
410 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
414 RefPtr<Action> action;
416 if (k->second.action) {
417 action = k->second.action;
420 action = _action_map->find_action (k->second.action_name);
426 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
430 /* return true even if the action could not be found */
436 Bindings::associate ()
438 KeybindingMap::iterator k;
444 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
445 k->second.action = _action_map->find_action (k->second.action_name);
446 if (k->second.action) {
447 push_to_gtk (k->first, k->second.action);
449 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
453 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
454 k->second.action = _action_map->find_action (k->second.action_name);
455 /* no working support in GTK for release bindings */
458 MouseButtonBindingMap::iterator b;
460 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
461 b->second.action = _action_map->find_action (b->second.action_name);
464 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
465 b->second.action = _action_map->find_action (b->second.action_name);
470 Bindings::dissociate ()
472 KeybindingMap::iterator k;
474 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
475 k->second.action.clear ();
477 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
478 k->second.action.clear ();
483 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
485 /* GTK has the useful feature of showing key bindings for actions in
486 * menus. As of August 2015, we have no interest in trying to
487 * reimplement this functionality, so we will use it even though we no
488 * longer use GTK accelerators for handling key events. To do this, we
489 * need to make sure that there is a fully populated GTK AccelMap set
490 * up with all bindings/actions.
493 Gtk::AccelKey gtk_key;
494 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
498 /* there is a trick happening here. It turns out that
499 * gtk_accel_map_add_entry() performs no validation checks on
500 * the accelerator keyval. This means we can use it to define
501 * ANY accelerator, even if they violate GTK's rules
502 * (e.g. about not using navigation keys). This works ONLY when
503 * the entry in the GTK accelerator map has not already been
504 * added. The entries will be added by the GTK UIManager when
505 * building menus, so this code must be called before that
509 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) kb.state());
514 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
520 if (is_registered(op, action_name)) {
521 remove(op, action_name, can_save);
523 add (kb, op, action_name, can_save);
528 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
530 if (is_registered (op, action_name)) {
534 KeybindingMap& kbm = get_keymap (op);
536 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
537 kbm.insert (new_pair).first;
540 Keyboard::keybindings_changed ();
543 BindingsChanged (this); /* EMIT SIGNAL */
548 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
550 bool erased_action = false;
551 KeybindingMap& kbm = get_keymap (op);
552 for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
553 if (k->second.action_name == action_name) {
555 erased_action = true;
560 if (!erased_action) {
561 return erased_action;
565 Keyboard::keybindings_changed ();
568 BindingsChanged (this); /* EMIT SIGNAL */
569 return erased_action;
574 Bindings::activate (MouseButton bb, Operation op)
576 MouseButtonBindingMap& bbm = get_mousemap(op);
578 MouseButtonBindingMap::iterator b = bbm.find (bb);
580 if (b == bbm.end()) {
581 /* no entry for this key in the state map */
585 RefPtr<Action> action;
587 if (b->second.action) {
588 action = b->second.action;
591 action = _action_map->find_action (b->second.action_name);
597 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
601 /* return true even if the action could not be found */
607 Bindings::add (MouseButton bb, Operation op, string const& action_name)
609 MouseButtonBindingMap& bbm = get_mousemap(op);
611 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
612 bbm.insert (newpair);
616 Bindings::remove (MouseButton bb, Operation op)
618 MouseButtonBindingMap& bbm = get_mousemap(op);
619 MouseButtonBindingMap::iterator b = bbm.find (bb);
621 if (b != bbm.end()) {
627 Bindings::save (XMLNode& root)
629 XMLNode* presses = new XMLNode (X_("Press"));
631 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
634 if (k->first.name().empty()) {
638 child = new XMLNode (X_("Binding"));
639 child->add_property (X_("key"), k->first.name());
640 child->add_property (X_("action"), k->second.action_name);
641 presses->add_child_nocopy (*child);
644 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
646 child = new XMLNode (X_("Binding"));
647 child->add_property (X_("button"), k->first.name());
648 child->add_property (X_("action"), k->second.action_name);
649 presses->add_child_nocopy (*child);
652 XMLNode* releases = new XMLNode (X_("Release"));
654 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
657 if (k->first.name().empty()) {
661 child = new XMLNode (X_("Binding"));
662 child->add_property (X_("key"), k->first.name());
663 child->add_property (X_("action"), k->second.action_name);
664 releases->add_child_nocopy (*child);
667 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
669 child = new XMLNode (X_("Binding"));
670 child->add_property (X_("button"), k->first.name());
671 child->add_property (X_("action"), k->second.action_name);
672 releases->add_child_nocopy (*child);
675 root.add_child_nocopy (*presses);
676 root.add_child_nocopy (*releases);
680 Bindings::save_all_bindings_as_html (ostream& ostr)
682 if (bindings.empty()) {
686 ostr << "<http>\n<head>\n<title>";
687 ostr << PROGRAM_NAME;
688 ostr << "</title>\n</head>\n<body>\n";
690 for (list<Bindings*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
691 (*b)->save_as_html (ostr);
699 Bindings::save_as_html (ostream& ostr) const
701 ostr << "<h1 class=\"binding-set-name\">";
705 if (!press_bindings.empty() || !button_press_bindings.empty()) {
707 ostr << "<h2 class=\"action-title\">";
711 if (!press_bindings.empty()) {
713 ostr << "<dl class=\"key-binding\">\n";
715 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
716 if (k->first.name().empty()) {
720 RefPtr<Action> action;
722 if (k->second.action) {
723 action = k->second.action;
726 action = _action_map->find_action (k->second.action_name);
734 ostr << "<dt class=\"key-name\">" << k->first.name() << "</dt>\n";
735 ostr << "<dd class=\"key-action\">" << action->get_label() << "</dd>\n";
742 if (!release_bindings.empty() || !release_bindings.empty()) {
744 ostr << "<h2 class=\"action-title\">";
745 ostr << _("Release");
748 if (!release_bindings.empty()) {
749 ostr << "<dl class=\"key-binding\">\n";
751 for (KeybindingMap::const_iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
753 if (k->first.name().empty()) {
757 RefPtr<Action> action;
759 if (k->second.action) {
760 action = k->second.action;
763 action = _action_map->find_action (k->second.action_name);
771 ostr << "<dt class=\"key-name\">" << k->first.name() << "</dt>\n";
772 ostr << "<dd class=\"key-action\">" << action->get_label() << "</dd>\n";
781 Bindings::load (XMLNode const& node)
783 const XMLNodeList& children (node.children());
785 press_bindings.clear ();
786 release_bindings.clear ();
788 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
789 /* each node could be Press or Release */
790 load_operation (**i);
797 Bindings::load_operation (XMLNode const& node)
799 if (node.name() == X_("Press") || node.name() == X_("Release")) {
803 if (node.name() == X_("Press")) {
809 const XMLNodeList& children (node.children());
811 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
813 XMLProperty const * ap;
814 XMLProperty const * kp;
815 XMLProperty const * bp;
816 XMLNode const * child = *p;
818 ap = child->property ("action");
819 kp = child->property ("key");
820 bp = child->property ("button");
822 if (!ap || (!kp && !bp)) {
828 if (!KeyboardKey::make_key (kp->value(), k)) {
831 add (k, op, ap->value());
834 if (!MouseButton::make_button (bp->value(), b)) {
837 add (b, op, ap->value());
844 Bindings::get_all_actions (std::vector<std::string>& paths,
845 std::vector<std::string>& labels,
846 std::vector<std::string>& tooltips,
847 std::vector<std::string>& keys,
848 std::vector<RefPtr<Action> >& actions)
854 /* build a reverse map from actions to bindings */
856 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
859 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
860 rmap.insert (make_pair (k->second.action, k->first));
863 /* get a list of all actions */
865 ActionMap::Actions all_actions;
866 _action_map->get_actions (all_actions);
868 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
870 paths.push_back ((*act)->get_accel_path());
871 labels.push_back ((*act)->get_label());
872 tooltips.push_back ((*act)->get_tooltip());
874 ReverseMap::iterator r = rmap.find (*act);
876 if (r != rmap.end()) {
877 keys.push_back (r->second.display_label());
879 keys.push_back (string());
882 actions.push_back (*act);
887 Bindings::get_bindings (string const& name, ActionMap& map)
889 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
890 if ((*b)->name() == name) {
891 (*b)->set_action_map (map);
900 Bindings::associate_all ()
902 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
908 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
910 const KeybindingMap& km = get_keymap(op);
911 return km.find(kb) != km.end();
915 Bindings::is_registered (Operation op, std::string const& action_name) const
917 const KeybindingMap& km = get_keymap(op);
918 return std::find_if(km.begin(), km.end(), ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
921 Bindings::KeybindingMap&
922 Bindings::get_keymap (Operation op)
926 return press_bindings;
929 return release_bindings;
933 const Bindings::KeybindingMap&
934 Bindings::get_keymap (Operation op) const
938 return press_bindings;
941 return release_bindings;
945 Bindings::MouseButtonBindingMap&
946 Bindings::get_mousemap (Operation op)
950 return button_press_bindings;
953 return button_release_bindings;
957 /*==========================================ACTION MAP =========================================*/
959 ActionMap::ActionMap (string const & name)
963 action_maps.push_back (this);
966 ActionMap::~ActionMap ()
968 action_maps.remove (this);
972 ActionMap::set_bindings (Bindings* b)
978 ActionMap::get_actions (ActionMap::Actions& acts)
980 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
981 acts.push_back (a->second);
986 ActionMap::find_action (const string& name)
988 _ActionMap::iterator a = _actions.find (name);
990 if (a != _actions.end()) {
994 return RefPtr<Action>();
998 ActionMap::create_action_group (const string& name)
1000 RefPtr<ActionGroup> g = ActionGroup::create (name);
1002 /* this is one of the places where our own Action management code
1003 has to touch the GTK one, because we want the GtkUIManager to
1004 be able to create widgets (particularly Menus) from our actions.
1006 This is a a necessary step for that to happen.
1010 ActionManager::ui_manager->insert_action_group (g);
1017 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
1021 RefPtr<Action> act = Action::create (name, label);
1023 fullpath = group->get_name();
1027 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1032 /* already registered */
1033 return RefPtr<Action> ();
1037 ActionMap::register_action (RefPtr<ActionGroup> group,
1038 const char* name, const char* label, sigc::slot<void> sl)
1042 RefPtr<Action> act = Action::create (name, label);
1044 fullpath = group->get_name();
1048 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1049 group->add (act, sl);
1053 /* already registered */
1054 return RefPtr<Action>();
1058 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1059 Gtk::RadioAction::Group& rgroup,
1060 const char* name, const char* label,
1061 sigc::slot<void> sl)
1065 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1066 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1068 fullpath = group->get_name();
1072 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1073 group->add (act, sl);
1077 /* already registered */
1078 return RefPtr<Action>();
1082 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1083 Gtk::RadioAction::Group& rgroup,
1084 const char* name, const char* label,
1085 sigc::slot<void,GtkAction*> sl,
1090 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1091 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1092 ract->property_value() = value;
1094 fullpath = group->get_name();
1098 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1099 group->add (act, sigc::bind (sl, act->gobj()));
1103 /* already registered */
1105 return RefPtr<Action>();
1109 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1110 const char* name, const char* label, sigc::slot<void> sl)
1114 fullpath = group->get_name();
1118 RefPtr<Action> act = ToggleAction::create (name, label);
1120 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1121 group->add (act, sl);
1125 /* already registered */
1126 return RefPtr<Action>();
1130 ActionMap::get_all_actions (std::vector<std::string>& paths,
1131 std::vector<std::string>& labels,
1132 std::vector<std::string>& tooltips,
1133 std::vector<std::string>& keys,
1134 std::vector<RefPtr<Action> >& actions)
1136 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1138 ActionMap::Actions these_actions;
1139 (*map)->get_actions (these_actions);
1141 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1143 paths.push_back ((*act)->get_accel_path());
1144 labels.push_back ((*act)->get_label());
1145 tooltips.push_back ((*act)->get_tooltip());
1146 actions.push_back (*act);
1148 Bindings* bindings = (*map)->bindings();
1153 Bindings::Operation op;
1155 key = bindings->get_binding_for_action (*act, op);
1157 if (key == KeyboardKey::null_key()) {
1158 keys.push_back (string());
1160 keys.push_back (key.display_label());
1163 keys.push_back (string());
1167 these_actions.clear ();
1171 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1172 char const *gdk_name = gdk_keyval_name (k.key());
1173 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;