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 /* since all SINGLE key events keycodes are changed to lower case
262 * before looking them up, make sure we only store lower case here. The
263 * Shift part will be stored in the modifier part of the KeyboardKey.
265 * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
270 if (str.size() == 1) {
271 actual = PBD::downcase (str);
276 string::size_type lastmod = actual.find_last_of ('-');
279 if (lastmod != string::npos) {
280 actual = PBD::downcase (str.substr (lastmod+1));
283 keyval = gdk_keyval_from_name (actual.c_str());
285 if (keyval == GDK_VoidSymbol || keyval == 0) {
289 k = KeyboardKey (s, keyval);
294 /*================================= Bindings =================================*/
295 Bindings::Bindings (std::string const& name)
299 bindings.push_back (this);
302 Bindings::~Bindings()
304 bindings.remove (this);
308 Bindings::ardour_action_name (RefPtr<Action> action)
310 /* Skip "<Actions>/" */
311 return action->get_accel_path ().substr (10);
315 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
317 const string action_name = ardour_action_name (action);
319 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
321 /* option one: action has already been associated with the
325 if (k->second.action == action) {
329 /* option two: action name matches, so lookup the action,
330 * setup the association while we're here, and return the binding.
333 if (_action_map && k->second.action_name == action_name) {
334 k->second.action = _action_map->find_action (action_name);
340 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
342 /* option one: action has already been associated with the
346 if (k->second.action == action) {
350 /* option two: action name matches, so lookup the action,
351 * setup the association while we're here, and return the binding.
354 if (_action_map && k->second.action_name == action_name) {
355 k->second.action = _action_map->find_action (action_name);
361 return KeyboardKey::null_key();
365 Bindings::set_action_map (ActionMap& actions)
368 _action_map->set_bindings (0);
371 _action_map = &actions;
372 _action_map->set_bindings (this);
379 Bindings::empty_keys() const
381 return press_bindings.empty() && release_bindings.empty();
385 Bindings::empty_mouse () const
387 return button_press_bindings.empty() && button_release_bindings.empty();
391 Bindings::empty() const
393 return empty_keys() && empty_mouse ();
397 Bindings::activate (KeyboardKey kb, Operation op)
399 KeybindingMap& kbm = get_keymap (op);
401 /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
402 Our bindings all use the lower case character/keyname, so switch
403 to the lower case before doing the lookup.
406 KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
408 KeybindingMap::iterator k = kbm.find (unshifted);
410 if (k == kbm.end()) {
411 /* no entry for this key in the state map */
412 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
416 RefPtr<Action> action;
418 if (k->second.action) {
419 action = k->second.action;
422 action = _action_map->find_action (k->second.action_name);
428 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
432 /* return true even if the action could not be found */
438 Bindings::associate ()
440 KeybindingMap::iterator k;
446 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
447 k->second.action = _action_map->find_action (k->second.action_name);
448 if (k->second.action) {
449 push_to_gtk (k->first, k->second.action);
451 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
455 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
456 k->second.action = _action_map->find_action (k->second.action_name);
457 /* no working support in GTK for release bindings */
460 MouseButtonBindingMap::iterator b;
462 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
463 b->second.action = _action_map->find_action (b->second.action_name);
466 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
467 b->second.action = _action_map->find_action (b->second.action_name);
472 Bindings::dissociate ()
474 KeybindingMap::iterator k;
476 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
477 k->second.action.clear ();
479 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
480 k->second.action.clear ();
485 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
487 /* GTK has the useful feature of showing key bindings for actions in
488 * menus. As of August 2015, we have no interest in trying to
489 * reimplement this functionality, so we will use it even though we no
490 * longer use GTK accelerators for handling key events. To do this, we
491 * need to make sure that there is a fully populated GTK AccelMap set
492 * up with all bindings/actions.
495 Gtk::AccelKey gtk_key;
496 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
500 /* there is a trick happening here. It turns out that
501 * gtk_accel_map_add_entry() performs no validation checks on
502 * the accelerator keyval. This means we can use it to define
503 * ANY accelerator, even if they violate GTK's rules
504 * (e.g. about not using navigation keys). This works ONLY when
505 * the entry in the GTK accelerator map has not already been
506 * added. The entries will be added by the GTK UIManager when
507 * building menus, so this code must be called before that
511 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) kb.state());
516 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
522 if (is_registered(op, action_name)) {
523 remove(op, action_name, can_save);
525 add (kb, op, action_name, can_save);
530 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
532 if (is_registered(op, action_name)) {
536 KeybindingMap& kbm = get_keymap (op);
538 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
539 kbm.insert (new_pair).first;
542 Keyboard::keybindings_changed ();
545 BindingsChanged (this); /* EMIT SIGNAL */
550 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
552 bool erased_action = false;
553 KeybindingMap& kbm = get_keymap (op);
554 for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
555 if (k->second.action_name == action_name) {
557 erased_action = true;
562 if (!erased_action) {
563 return erased_action;
567 Keyboard::keybindings_changed ();
570 BindingsChanged (this); /* EMIT SIGNAL */
571 return erased_action;
576 Bindings::activate (MouseButton bb, Operation op)
578 MouseButtonBindingMap& bbm = get_mousemap(op);
580 MouseButtonBindingMap::iterator b = bbm.find (bb);
582 if (b == bbm.end()) {
583 /* no entry for this key in the state map */
587 RefPtr<Action> action;
589 if (b->second.action) {
590 action = b->second.action;
593 action = _action_map->find_action (b->second.action_name);
599 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
603 /* return true even if the action could not be found */
609 Bindings::add (MouseButton bb, Operation op, string const& action_name)
611 MouseButtonBindingMap& bbm = get_mousemap(op);
613 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
614 bbm.insert (newpair);
618 Bindings::remove (MouseButton bb, Operation op)
620 MouseButtonBindingMap& bbm = get_mousemap(op);
621 MouseButtonBindingMap::iterator b = bbm.find (bb);
623 if (b != bbm.end()) {
629 Bindings::save (XMLNode& root)
631 XMLNode* presses = new XMLNode (X_("Press"));
633 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
636 if (k->first.name().empty()) {
640 child = new XMLNode (X_("Binding"));
641 child->add_property (X_("key"), k->first.name());
642 child->add_property (X_("action"), k->second.action_name);
643 presses->add_child_nocopy (*child);
646 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
648 child = new XMLNode (X_("Binding"));
649 child->add_property (X_("button"), k->first.name());
650 child->add_property (X_("action"), k->second.action_name);
651 presses->add_child_nocopy (*child);
654 XMLNode* releases = new XMLNode (X_("Release"));
656 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
659 if (k->first.name().empty()) {
663 child = new XMLNode (X_("Binding"));
664 child->add_property (X_("key"), k->first.name());
665 child->add_property (X_("action"), k->second.action_name);
666 releases->add_child_nocopy (*child);
669 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
671 child = new XMLNode (X_("Binding"));
672 child->add_property (X_("button"), k->first.name());
673 child->add_property (X_("action"), k->second.action_name);
674 releases->add_child_nocopy (*child);
677 root.add_child_nocopy (*presses);
678 root.add_child_nocopy (*releases);
682 Bindings::load (XMLNode const& node)
684 const XMLNodeList& children (node.children());
686 press_bindings.clear ();
687 release_bindings.clear ();
689 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
690 /* each node could be Press or Release */
691 load_operation (**i);
698 Bindings::load_operation (XMLNode const& node)
700 if (node.name() == X_("Press") || node.name() == X_("Release")) {
704 if (node.name() == X_("Press")) {
710 const XMLNodeList& children (node.children());
712 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
718 ap = (*p)->property ("action");
719 kp = (*p)->property ("key");
720 bp = (*p)->property ("button");
722 if (!ap || (!kp && !bp)) {
728 if (!KeyboardKey::make_key (kp->value(), k)) {
731 add (k, op, ap->value());
734 if (!MouseButton::make_button (bp->value(), b)) {
737 add (b, op, ap->value());
744 Bindings::get_all_actions (std::vector<std::string>& paths,
745 std::vector<std::string>& labels,
746 std::vector<std::string>& tooltips,
747 std::vector<std::string>& keys,
748 std::vector<RefPtr<Action> >& actions)
754 /* build a reverse map from actions to bindings */
756 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
759 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
760 rmap.insert (make_pair (k->second.action, k->first));
763 /* get a list of all actions */
765 ActionMap::Actions all_actions;
766 _action_map->get_actions (all_actions);
768 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
770 paths.push_back ((*act)->get_accel_path());
771 labels.push_back ((*act)->get_label());
772 tooltips.push_back ((*act)->get_tooltip());
774 ReverseMap::iterator r = rmap.find (*act);
776 if (r != rmap.end()) {
777 keys.push_back (r->second.display_label());
779 keys.push_back (string());
782 actions.push_back (*act);
787 Bindings::get_bindings (string const& name, ActionMap& map)
789 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
790 if ((*b)->name() == name) {
791 (*b)->set_action_map (map);
800 Bindings::associate_all ()
802 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
808 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
810 const KeybindingMap& km = get_keymap(op);
811 return km.find(kb) != km.end();
815 Bindings::is_registered (Operation op, std::string const& action_name) const
817 const KeybindingMap& km = get_keymap(op);
818 return std::find_if(km.begin(), km.end(), ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
821 Bindings::KeybindingMap&
822 Bindings::get_keymap (Operation op)
826 return press_bindings;
829 return release_bindings;
833 const Bindings::KeybindingMap&
834 Bindings::get_keymap (Operation op) const
838 return press_bindings;
841 return release_bindings;
845 Bindings::MouseButtonBindingMap&
846 Bindings::get_mousemap (Operation op)
850 return button_press_bindings;
853 return button_release_bindings;
857 /*==========================================ACTION MAP =========================================*/
859 ActionMap::ActionMap (string const & name)
863 action_maps.push_back (this);
866 ActionMap::~ActionMap ()
868 action_maps.remove (this);
872 ActionMap::set_bindings (Bindings* b)
878 ActionMap::get_actions (ActionMap::Actions& acts)
880 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
881 acts.push_back (a->second);
886 ActionMap::find_action (const string& name)
888 _ActionMap::iterator a = _actions.find (name);
890 if (a != _actions.end()) {
894 return RefPtr<Action>();
898 ActionMap::create_action_group (const string& name)
900 RefPtr<ActionGroup> g = ActionGroup::create (name);
902 /* this is one of the places where our own Action management code
903 has to touch the GTK one, because we want the GtkUIManager to
904 be able to create widgets (particularly Menus) from our actions.
906 This is a a necessary step for that to happen.
910 ActionManager::ui_manager->insert_action_group (g);
917 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
921 RefPtr<Action> act = Action::create (name, label);
923 fullpath = group->get_name();
927 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
932 /* already registered */
933 return RefPtr<Action> ();
937 ActionMap::register_action (RefPtr<ActionGroup> group,
938 const char* name, const char* label, sigc::slot<void> sl)
942 RefPtr<Action> act = Action::create (name, label);
944 fullpath = group->get_name();
948 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
949 group->add (act, sl);
953 /* already registered */
954 return RefPtr<Action>();
958 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
959 Gtk::RadioAction::Group& rgroup,
960 const char* name, const char* label,
965 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
966 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
968 fullpath = group->get_name();
972 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
973 group->add (act, sl);
977 /* already registered */
978 return RefPtr<Action>();
982 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
983 Gtk::RadioAction::Group& rgroup,
984 const char* name, const char* label,
985 sigc::slot<void,GtkAction*> sl,
990 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
991 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
992 ract->property_value() = value;
994 fullpath = group->get_name();
998 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
999 group->add (act, sigc::bind (sl, act->gobj()));
1003 /* already registered */
1005 return RefPtr<Action>();
1009 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1010 const char* name, const char* label, sigc::slot<void> sl)
1014 fullpath = group->get_name();
1018 RefPtr<Action> act = ToggleAction::create (name, label);
1020 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1021 group->add (act, sl);
1025 /* already registered */
1026 return RefPtr<Action>();
1030 ActionMap::get_all_actions (std::vector<std::string>& paths,
1031 std::vector<std::string>& labels,
1032 std::vector<std::string>& tooltips,
1033 std::vector<std::string>& keys,
1034 std::vector<RefPtr<Action> >& actions)
1036 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1038 ActionMap::Actions these_actions;
1039 (*map)->get_actions (these_actions);
1041 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1043 paths.push_back ((*act)->get_accel_path());
1044 labels.push_back ((*act)->get_label());
1045 tooltips.push_back ((*act)->get_tooltip());
1046 actions.push_back (*act);
1048 Bindings* bindings = (*map)->bindings();
1053 Bindings::Operation op;
1055 key = bindings->get_binding_for_action (*act, op);
1057 if (key == KeyboardKey::null_key()) {
1058 keys.push_back (string());
1060 keys.push_back (key.display_label());
1063 keys.push_back (string());
1067 these_actions.clear ();
1071 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1072 char const *gdk_name = gdk_keyval_name (k.key());
1073 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;