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) {
712 XMLProperty const * ap;
713 XMLProperty const * kp;
714 XMLProperty const * bp;
715 XMLNode const * child = *p;
717 ap = child->property ("action");
718 kp = child->property ("key");
719 bp = child->property ("button");
721 if (!ap || (!kp && !bp)) {
727 if (!KeyboardKey::make_key (kp->value(), k)) {
730 add (k, op, ap->value());
733 if (!MouseButton::make_button (bp->value(), b)) {
736 add (b, op, ap->value());
743 Bindings::get_all_actions (std::vector<std::string>& paths,
744 std::vector<std::string>& labels,
745 std::vector<std::string>& tooltips,
746 std::vector<std::string>& keys,
747 std::vector<RefPtr<Action> >& actions)
753 /* build a reverse map from actions to bindings */
755 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
758 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
759 rmap.insert (make_pair (k->second.action, k->first));
762 /* get a list of all actions */
764 ActionMap::Actions all_actions;
765 _action_map->get_actions (all_actions);
767 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
769 paths.push_back ((*act)->get_accel_path());
770 labels.push_back ((*act)->get_label());
771 tooltips.push_back ((*act)->get_tooltip());
773 ReverseMap::iterator r = rmap.find (*act);
775 if (r != rmap.end()) {
776 keys.push_back (r->second.display_label());
778 keys.push_back (string());
781 actions.push_back (*act);
786 Bindings::get_bindings (string const& name, ActionMap& map)
788 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
789 if ((*b)->name() == name) {
790 (*b)->set_action_map (map);
799 Bindings::associate_all ()
801 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
807 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
809 const KeybindingMap& km = get_keymap(op);
810 return km.find(kb) != km.end();
814 Bindings::is_registered (Operation op, std::string const& action_name) const
816 const KeybindingMap& km = get_keymap(op);
817 return std::find_if(km.begin(), km.end(), ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
820 Bindings::KeybindingMap&
821 Bindings::get_keymap (Operation op)
825 return press_bindings;
828 return release_bindings;
832 const Bindings::KeybindingMap&
833 Bindings::get_keymap (Operation op) const
837 return press_bindings;
840 return release_bindings;
844 Bindings::MouseButtonBindingMap&
845 Bindings::get_mousemap (Operation op)
849 return button_press_bindings;
852 return button_release_bindings;
856 /*==========================================ACTION MAP =========================================*/
858 ActionMap::ActionMap (string const & name)
862 action_maps.push_back (this);
865 ActionMap::~ActionMap ()
867 action_maps.remove (this);
871 ActionMap::set_bindings (Bindings* b)
877 ActionMap::get_actions (ActionMap::Actions& acts)
879 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
880 acts.push_back (a->second);
885 ActionMap::find_action (const string& name)
887 _ActionMap::iterator a = _actions.find (name);
889 if (a != _actions.end()) {
893 return RefPtr<Action>();
897 ActionMap::create_action_group (const string& name)
899 RefPtr<ActionGroup> g = ActionGroup::create (name);
901 /* this is one of the places where our own Action management code
902 has to touch the GTK one, because we want the GtkUIManager to
903 be able to create widgets (particularly Menus) from our actions.
905 This is a a necessary step for that to happen.
909 ActionManager::ui_manager->insert_action_group (g);
916 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
920 RefPtr<Action> act = Action::create (name, label);
922 fullpath = group->get_name();
926 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
931 /* already registered */
932 return RefPtr<Action> ();
936 ActionMap::register_action (RefPtr<ActionGroup> group,
937 const char* name, const char* label, sigc::slot<void> sl)
941 RefPtr<Action> act = Action::create (name, label);
943 fullpath = group->get_name();
947 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
948 group->add (act, sl);
952 /* already registered */
953 return RefPtr<Action>();
957 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
958 Gtk::RadioAction::Group& rgroup,
959 const char* name, const char* label,
964 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
965 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
967 fullpath = group->get_name();
971 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
972 group->add (act, sl);
976 /* already registered */
977 return RefPtr<Action>();
981 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
982 Gtk::RadioAction::Group& rgroup,
983 const char* name, const char* label,
984 sigc::slot<void,GtkAction*> sl,
989 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
990 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
991 ract->property_value() = value;
993 fullpath = group->get_name();
997 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
998 group->add (act, sigc::bind (sl, act->gobj()));
1002 /* already registered */
1004 return RefPtr<Action>();
1008 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1009 const char* name, const char* label, sigc::slot<void> sl)
1013 fullpath = group->get_name();
1017 RefPtr<Action> act = ToggleAction::create (name, label);
1019 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1020 group->add (act, sl);
1024 /* already registered */
1025 return RefPtr<Action>();
1029 ActionMap::get_all_actions (std::vector<std::string>& paths,
1030 std::vector<std::string>& labels,
1031 std::vector<std::string>& tooltips,
1032 std::vector<std::string>& keys,
1033 std::vector<RefPtr<Action> >& actions)
1035 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1037 ActionMap::Actions these_actions;
1038 (*map)->get_actions (these_actions);
1040 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1042 paths.push_back ((*act)->get_accel_path());
1043 labels.push_back ((*act)->get_label());
1044 tooltips.push_back ((*act)->get_tooltip());
1045 actions.push_back (*act);
1047 Bindings* bindings = (*map)->bindings();
1052 Bindings::Operation op;
1054 key = bindings->get_binding_for_action (*act, op);
1056 if (key == KeyboardKey::null_key()) {
1057 keys.push_back (string());
1059 keys.push_back (key.display_label());
1062 keys.push_back (string());
1066 these_actions.clear ();
1070 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1071 char const *gdk_name = gdk_keyval_name (k.key());
1072 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;