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::load (XMLNode const& node)
682 const XMLNodeList& children (node.children());
684 press_bindings.clear ();
685 release_bindings.clear ();
687 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
688 /* each node could be Press or Release */
689 load_operation (**i);
696 Bindings::load_operation (XMLNode const& node)
698 if (node.name() == X_("Press") || node.name() == X_("Release")) {
702 if (node.name() == X_("Press")) {
708 const XMLNodeList& children (node.children());
710 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
716 ap = (*p)->property ("action");
717 kp = (*p)->property ("key");
718 bp = (*p)->property ("button");
720 if (!ap || (!kp && !bp)) {
726 if (!KeyboardKey::make_key (kp->value(), k)) {
729 add (k, op, ap->value());
732 if (!MouseButton::make_button (bp->value(), b)) {
735 add (b, op, ap->value());
742 Bindings::get_all_actions (std::vector<std::string>& paths,
743 std::vector<std::string>& labels,
744 std::vector<std::string>& tooltips,
745 std::vector<std::string>& keys,
746 std::vector<RefPtr<Action> >& actions)
752 /* build a reverse map from actions to bindings */
754 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
757 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
758 rmap.insert (make_pair (k->second.action, k->first));
761 /* get a list of all actions */
763 ActionMap::Actions all_actions;
764 _action_map->get_actions (all_actions);
766 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
768 paths.push_back ((*act)->get_accel_path());
769 labels.push_back ((*act)->get_label());
770 tooltips.push_back ((*act)->get_tooltip());
772 ReverseMap::iterator r = rmap.find (*act);
774 if (r != rmap.end()) {
775 keys.push_back (r->second.display_label());
777 keys.push_back (string());
780 actions.push_back (*act);
785 Bindings::get_bindings (string const& name, ActionMap& map)
787 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
788 if ((*b)->name() == name) {
789 (*b)->set_action_map (map);
798 Bindings::associate_all ()
800 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
806 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
808 const KeybindingMap& km = get_keymap(op);
809 return km.find(kb) != km.end();
813 Bindings::is_registered (Operation op, std::string const& action_name) const
815 const KeybindingMap& km = get_keymap(op);
816 return std::find_if(km.begin(), km.end(), ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
819 Bindings::KeybindingMap&
820 Bindings::get_keymap (Operation op)
824 return press_bindings;
827 return release_bindings;
831 const Bindings::KeybindingMap&
832 Bindings::get_keymap (Operation op) const
836 return press_bindings;
839 return release_bindings;
843 Bindings::MouseButtonBindingMap&
844 Bindings::get_mousemap (Operation op)
848 return button_press_bindings;
851 return button_release_bindings;
855 /*==========================================ACTION MAP =========================================*/
857 ActionMap::ActionMap (string const & name)
861 action_maps.push_back (this);
864 ActionMap::~ActionMap ()
866 action_maps.remove (this);
870 ActionMap::set_bindings (Bindings* b)
876 ActionMap::get_actions (ActionMap::Actions& acts)
878 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
879 acts.push_back (a->second);
884 ActionMap::find_action (const string& name)
886 _ActionMap::iterator a = _actions.find (name);
888 if (a != _actions.end()) {
892 return RefPtr<Action>();
896 ActionMap::create_action_group (const string& name)
898 RefPtr<ActionGroup> g = ActionGroup::create (name);
900 /* this is one of the places where our own Action management code
901 has to touch the GTK one, because we want the GtkUIManager to
902 be able to create widgets (particularly Menus) from our actions.
904 This is a a necessary step for that to happen.
908 ActionManager::ui_manager->insert_action_group (g);
915 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
919 RefPtr<Action> act = Action::create (name, label);
921 fullpath = group->get_name();
925 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
930 /* already registered */
931 return RefPtr<Action> ();
935 ActionMap::register_action (RefPtr<ActionGroup> group,
936 const char* name, const char* label, sigc::slot<void> sl)
940 RefPtr<Action> act = Action::create (name, label);
942 fullpath = group->get_name();
946 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
947 group->add (act, sl);
951 /* already registered */
952 return RefPtr<Action>();
956 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
957 Gtk::RadioAction::Group& rgroup,
958 const char* name, const char* label,
963 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
964 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
966 fullpath = group->get_name();
970 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
971 group->add (act, sl);
975 /* already registered */
976 return RefPtr<Action>();
980 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
981 Gtk::RadioAction::Group& rgroup,
982 const char* name, const char* label,
983 sigc::slot<void,GtkAction*> sl,
988 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
989 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
990 ract->property_value() = value;
992 fullpath = group->get_name();
996 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
997 group->add (act, sigc::bind (sl, act->gobj()));
1001 /* already registered */
1003 return RefPtr<Action>();
1007 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1008 const char* name, const char* label, sigc::slot<void> sl)
1012 fullpath = group->get_name();
1016 RefPtr<Action> act = ToggleAction::create (name, label);
1018 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1019 group->add (act, sl);
1023 /* already registered */
1024 return RefPtr<Action>();
1028 ActionMap::get_all_actions (std::vector<std::string>& paths,
1029 std::vector<std::string>& labels,
1030 std::vector<std::string>& tooltips,
1031 std::vector<std::string>& keys,
1032 std::vector<RefPtr<Action> >& actions)
1034 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1036 ActionMap::Actions these_actions;
1037 (*map)->get_actions (these_actions);
1039 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1041 paths.push_back ((*act)->get_accel_path());
1042 labels.push_back ((*act)->get_label());
1043 tooltips.push_back ((*act)->get_tooltip());
1044 actions.push_back (*act);
1046 Bindings* bindings = (*map)->bindings();
1051 Bindings::Operation op;
1053 key = bindings->get_binding_for_action (*act, op);
1055 if (key == KeyboardKey::null_key()) {
1056 keys.push_back (string());
1058 keys.push_back (key.display_label());
1061 keys.push_back (string());
1065 these_actions.clear ();
1069 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1070 char const *gdk_name = gdk_keyval_name (k.key());
1071 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;