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;
50 /*============================ ActionNameRegistered ===========================*/
51 template <typename IteratorValueType>
52 struct ActionNameRegistered
54 ActionNameRegistered(std::string const& name)
58 bool operator()(IteratorValueType elem) const {
59 return elem.second.action_name == action_name;
61 std::string const& action_name;
64 /*================================ MouseButton ================================*/
65 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
67 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
69 /* this is a slightly wierd test that relies on
70 * gdk_keyval_is_{upper,lower}() returning true for keys that have no
71 * case-sensitivity. This covers mostly non-alphanumeric keys.
74 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
75 /* key is not subject to case, so ignore SHIFT
77 ignore |= GDK_SHIFT_MASK;
80 _val = (state & ~ignore);
86 MouseButton::make_button (const string& str, MouseButton& b)
90 if (str.find ("Primary") != string::npos) {
91 s |= Keyboard::PrimaryModifier;
94 if (str.find ("Secondary") != string::npos) {
95 s |= Keyboard::SecondaryModifier;
98 if (str.find ("Tertiary") != string::npos) {
99 s |= Keyboard::TertiaryModifier;
102 if (str.find ("Level4") != string::npos) {
103 s |= Keyboard::Level4Modifier;
106 string::size_type lastmod = str.find_last_of ('-');
107 uint32_t button_number;
109 if (lastmod == string::npos) {
110 button_number = PBD::atoi (str);
112 button_number = PBD::atoi (str.substr (lastmod+1));
115 b = MouseButton (s, button_number);
120 MouseButton::name () const
126 if (s & Keyboard::PrimaryModifier) {
129 if (s & Keyboard::SecondaryModifier) {
135 if (s & Keyboard::TertiaryModifier) {
141 if (s & Keyboard::Level4Modifier) {
153 snprintf (buf, sizeof (buf), "%u", button());
159 /*================================ KeyboardKey ================================*/
160 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
162 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
164 _val = (state & ~ignore);
170 KeyboardKey::display_label () const
176 /* This magically returns a string that will display the right thing
177 * on all platforms, notably the command key on OS X.
180 uint32_t mod = state();
183 /* We use both bits (MOD2|META) for Primary on OS X,
184 * but we don't want MOD2 showing up in listings.
187 if (mod & GDK_MOD2_MASK) {
188 mod &= ~GDK_MOD2_MASK;
192 return gtk_accelerator_get_label (key(), (GdkModifierType) mod);
196 KeyboardKey::name () const
202 if (s & Keyboard::PrimaryModifier) {
205 if (s & Keyboard::SecondaryModifier) {
211 if (s & Keyboard::TertiaryModifier) {
217 if (s & Keyboard::Level4Modifier) {
228 char const *gdk_name = gdk_keyval_name (key());
241 KeyboardKey::make_key (const string& str, KeyboardKey& k)
245 if (str.find ("Primary") != string::npos) {
246 s |= Keyboard::PrimaryModifier;
249 if (str.find ("Secondary") != string::npos) {
250 s |= Keyboard::SecondaryModifier;
253 if (str.find ("Tertiary") != string::npos) {
254 s |= Keyboard::TertiaryModifier;
257 if (str.find ("Level4") != string::npos) {
258 s |= Keyboard::Level4Modifier;
261 string::size_type lastmod = str.find_last_of ('-');
264 /* since all key events keycodes are changed to lower case before
265 * looking them up, make sure we only store lower case here. The Shift
266 * part will be stored in the modifier part of the KeyboardKey.
268 * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
273 if (lastmod == string::npos) {
274 lower = PBD::downcase (str);
276 lower = PBD::downcase (str.substr (lastmod+1));
279 keyval = gdk_keyval_from_name (lower.c_str());
281 if (keyval == GDK_VoidSymbol || keyval == 0) {
285 k = KeyboardKey (s, keyval);
290 /*================================= Bindings =================================*/
291 Bindings::Bindings (std::string const& name)
295 bindings.push_back (this);
298 Bindings::~Bindings()
300 bindings.remove (this);
304 Bindings::ardour_action_name (RefPtr<Action> action)
306 /* Skip "<Actions>/" */
307 return action->get_accel_path ().substr (10);
311 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
313 const string action_name = ardour_action_name (action);
315 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
317 /* option one: action has already been associated with the
321 if (k->second.action == action) {
325 /* option two: action name matches, so lookup the action,
326 * setup the association while we're here, and return the binding.
329 if (_action_map && k->second.action_name == action_name) {
330 k->second.action = _action_map->find_action (action_name);
336 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
338 /* option one: action has already been associated with the
342 if (k->second.action == action) {
346 /* option two: action name matches, so lookup the action,
347 * setup the association while we're here, and return the binding.
350 if (_action_map && k->second.action_name == action_name) {
351 k->second.action = _action_map->find_action (action_name);
357 return KeyboardKey::null_key();
361 Bindings::set_action_map (ActionMap& actions)
364 _action_map->set_bindings (0);
367 _action_map = &actions;
368 _action_map->set_bindings (this);
375 Bindings::empty_keys() const
377 return press_bindings.empty() && release_bindings.empty();
381 Bindings::empty_mouse () const
383 return button_press_bindings.empty() && button_release_bindings.empty();
387 Bindings::empty() const
389 return empty_keys() && empty_mouse ();
393 Bindings::activate (KeyboardKey kb, Operation op)
395 KeybindingMap& kbm = get_keymap (op);
398 /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
399 Our bindings all use the lower case character/keyname, so switch
400 to the lower case before doing the lookup.
403 KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
405 KeybindingMap::iterator k = kbm.find (unshifted);
407 if (k == kbm.end()) {
408 /* no entry for this key in the state map */
409 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
413 RefPtr<Action> action;
415 if (k->second.action) {
416 action = k->second.action;
419 action = _action_map->find_action (k->second.action_name);
425 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
429 /* return true even if the action could not be found */
435 Bindings::associate ()
437 KeybindingMap::iterator k;
443 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
444 k->second.action = _action_map->find_action (k->second.action_name);
445 if (k->second.action) {
446 push_to_gtk (k->first, k->second.action);
448 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
452 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
453 k->second.action = _action_map->find_action (k->second.action_name);
454 /* no working support in GTK for release bindings */
457 MouseButtonBindingMap::iterator b;
459 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
460 b->second.action = _action_map->find_action (b->second.action_name);
463 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
464 b->second.action = _action_map->find_action (b->second.action_name);
469 Bindings::dissociate ()
471 KeybindingMap::iterator k;
473 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
474 k->second.action.clear ();
476 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
477 k->second.action.clear ();
482 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
484 /* GTK has the useful feature of showing key bindings for actions in
485 * menus. As of August 2015, we have no interest in trying to
486 * reimplement this functionality, so we will use it even though we no
487 * longer use GTK accelerators for handling key events. To do this, we
488 * need to make sure that there is a fully populated GTK AccelMap set
489 * up with all bindings/actions.
492 Gtk::AccelKey gtk_key;
493 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
497 /* there is a trick happening here. It turns out that
498 * gtk_accel_map_add_entry() performs no validation checks on
499 * the accelerator keyval. This means we can use it to define
500 * ANY accelerator, even if they violate GTK's rules
501 * (e.g. about not using navigation keys). This works ONLY when
502 * the entry in the GTK accelerator map has not already been
503 * added. The entries will be added by the GTK UIManager when
504 * building menus, so this code must be called before that
508 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) kb.state());
513 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
519 if (is_registered(op, action_name)) {
520 remove(op, action_name, can_save);
522 add (kb, op, action_name, can_save);
527 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
529 if (is_registered(op, action_name)) {
533 KeybindingMap& kbm = get_keymap (op);
535 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
536 kbm.insert (new_pair).first;
539 Keyboard::keybindings_changed ();
542 BindingsChanged (this); /* EMIT SIGNAL */
547 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
549 bool erased_action = false;
550 KeybindingMap& kbm = get_keymap (op);
551 for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
552 if (k->second.action_name == action_name) {
554 erased_action = true;
559 if (!erased_action) {
560 return erased_action;
564 Keyboard::keybindings_changed ();
567 BindingsChanged (this); /* EMIT SIGNAL */
568 return erased_action;
573 Bindings::activate (MouseButton bb, Operation op)
575 MouseButtonBindingMap& bbm = get_mousemap(op);
577 MouseButtonBindingMap::iterator b = bbm.find (bb);
579 if (b == bbm.end()) {
580 /* no entry for this key in the state map */
584 RefPtr<Action> action;
586 if (b->second.action) {
587 action = b->second.action;
590 action = _action_map->find_action (b->second.action_name);
596 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
600 /* return true even if the action could not be found */
606 Bindings::add (MouseButton bb, Operation op, string const& action_name)
608 MouseButtonBindingMap& bbm = get_mousemap(op);
610 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
611 bbm.insert (newpair);
615 Bindings::remove (MouseButton bb, Operation op)
617 MouseButtonBindingMap& bbm = get_mousemap(op);
618 MouseButtonBindingMap::iterator b = bbm.find (bb);
620 if (b != bbm.end()) {
626 Bindings::save (XMLNode& root)
628 XMLNode* presses = new XMLNode (X_("Press"));
630 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
633 if (k->first.name().empty()) {
637 child = new XMLNode (X_("Binding"));
638 child->add_property (X_("key"), k->first.name());
639 child->add_property (X_("action"), k->second.action_name);
640 presses->add_child_nocopy (*child);
643 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
645 child = new XMLNode (X_("Binding"));
646 child->add_property (X_("button"), k->first.name());
647 child->add_property (X_("action"), k->second.action_name);
648 presses->add_child_nocopy (*child);
651 XMLNode* releases = new XMLNode (X_("Release"));
653 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
656 if (k->first.name().empty()) {
660 child = new XMLNode (X_("Binding"));
661 child->add_property (X_("key"), k->first.name());
662 child->add_property (X_("action"), k->second.action_name);
663 releases->add_child_nocopy (*child);
666 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
668 child = new XMLNode (X_("Binding"));
669 child->add_property (X_("button"), k->first.name());
670 child->add_property (X_("action"), k->second.action_name);
671 releases->add_child_nocopy (*child);
674 root.add_child_nocopy (*presses);
675 root.add_child_nocopy (*releases);
679 Bindings::load (XMLNode const& node)
681 const XMLNodeList& children (node.children());
683 press_bindings.clear ();
684 release_bindings.clear ();
686 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
687 /* each node could be Press or Release */
688 load_operation (**i);
695 Bindings::load_operation (XMLNode const& node)
697 if (node.name() == X_("Press") || node.name() == X_("Release")) {
701 if (node.name() == X_("Press")) {
707 const XMLNodeList& children (node.children());
709 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
715 ap = (*p)->property ("action");
716 kp = (*p)->property ("key");
717 bp = (*p)->property ("button");
719 if (!ap || (!kp && !bp)) {
725 if (!KeyboardKey::make_key (kp->value(), k)) {
728 add (k, op, ap->value());
731 if (!MouseButton::make_button (bp->value(), b)) {
734 add (b, op, ap->value());
741 Bindings::get_all_actions (std::vector<std::string>& paths,
742 std::vector<std::string>& labels,
743 std::vector<std::string>& tooltips,
744 std::vector<std::string>& keys,
745 std::vector<RefPtr<Action> >& actions)
751 /* build a reverse map from actions to bindings */
753 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
756 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
757 rmap.insert (make_pair (k->second.action, k->first));
760 /* get a list of all actions */
762 ActionMap::Actions all_actions;
763 _action_map->get_actions (all_actions);
765 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
767 paths.push_back ((*act)->get_accel_path());
768 labels.push_back ((*act)->get_label());
769 tooltips.push_back ((*act)->get_tooltip());
771 ReverseMap::iterator r = rmap.find (*act);
773 if (r != rmap.end()) {
774 keys.push_back (r->second.display_label());
776 keys.push_back (string());
779 actions.push_back (*act);
784 Bindings::get_bindings (string const& name, ActionMap& map)
786 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
787 if ((*b)->name() == name) {
788 (*b)->set_action_map (map);
797 Bindings::associate_all ()
799 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
805 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
807 const KeybindingMap& km = get_keymap(op);
808 return km.find(kb) != km.end();
812 Bindings::is_registered (Operation op, std::string const& action_name) const
814 const KeybindingMap& km = get_keymap(op);
815 return std::find_if(km.begin(), km.end(), ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
818 Bindings::KeybindingMap&
819 Bindings::get_keymap (Operation op)
823 return press_bindings;
826 return release_bindings;
830 const Bindings::KeybindingMap&
831 Bindings::get_keymap (Operation op) const
835 return press_bindings;
838 return release_bindings;
842 Bindings::MouseButtonBindingMap&
843 Bindings::get_mousemap (Operation op)
847 return button_press_bindings;
850 return button_release_bindings;
854 /*==========================================ACTION MAP =========================================*/
856 ActionMap::ActionMap (string const & name)
860 action_maps.push_back (this);
863 ActionMap::~ActionMap ()
865 action_maps.remove (this);
869 ActionMap::set_bindings (Bindings* b)
875 ActionMap::get_actions (ActionMap::Actions& acts)
877 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
878 acts.push_back (a->second);
883 ActionMap::find_action (const string& name)
885 _ActionMap::iterator a = _actions.find (name);
887 if (a != _actions.end()) {
891 return RefPtr<Action>();
895 ActionMap::create_action_group (const string& name)
897 RefPtr<ActionGroup> g = ActionGroup::create (name);
899 /* this is one of the places where our own Action management code
900 has to touch the GTK one, because we want the GtkUIManager to
901 be able to create widgets (particularly Menus) from our actions.
903 This is a a necessary step for that to happen.
907 ActionManager::ui_manager->insert_action_group (g);
914 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
918 RefPtr<Action> act = Action::create (name, label);
920 fullpath = group->get_name();
924 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
929 /* already registered */
930 return RefPtr<Action> ();
934 ActionMap::register_action (RefPtr<ActionGroup> group,
935 const char* name, const char* label, sigc::slot<void> sl)
939 RefPtr<Action> act = Action::create (name, label);
941 fullpath = group->get_name();
945 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
946 group->add (act, sl);
950 /* already registered */
951 return RefPtr<Action>();
955 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
956 Gtk::RadioAction::Group& rgroup,
957 const char* name, const char* label,
962 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
963 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
965 fullpath = group->get_name();
969 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
970 group->add (act, sl);
974 /* already registered */
975 return RefPtr<Action>();
979 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
980 Gtk::RadioAction::Group& rgroup,
981 const char* name, const char* label,
982 sigc::slot<void,GtkAction*> sl,
987 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
988 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
989 ract->property_value() = value;
991 fullpath = group->get_name();
995 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
996 group->add (act, sigc::bind (sl, act->gobj()));
1000 /* already registered */
1002 return RefPtr<Action>();
1006 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1007 const char* name, const char* label, sigc::slot<void> sl)
1011 fullpath = group->get_name();
1015 RefPtr<Action> act = ToggleAction::create (name, label);
1017 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1018 group->add (act, sl);
1022 /* already registered */
1023 return RefPtr<Action>();
1027 ActionMap::get_all_actions (std::vector<std::string>& paths,
1028 std::vector<std::string>& labels,
1029 std::vector<std::string>& tooltips,
1030 std::vector<std::string>& keys,
1031 std::vector<RefPtr<Action> >& actions)
1033 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1035 ActionMap::Actions these_actions;
1036 (*map)->get_actions (these_actions);
1038 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1040 paths.push_back ((*act)->get_accel_path());
1041 labels.push_back ((*act)->get_label());
1042 tooltips.push_back ((*act)->get_tooltip());
1043 actions.push_back (*act);
1045 Bindings* bindings = (*map)->bindings();
1050 Bindings::Operation op;
1052 key = bindings->get_binding_for_action (*act, op);
1054 if (key == KeyboardKey::null_key()) {
1055 keys.push_back (string());
1057 keys.push_back (key.display_label());
1060 keys.push_back (string());
1064 these_actions.clear ();
1068 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1069 char const *gdk_name = gdk_keyval_name (k.key());
1070 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;