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 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
51 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
53 /* this is a slightly wierd test that relies on
54 * gdk_keyval_is_{upper,lower}() returning true for keys that have no
55 * case-sensitivity. This covers mostly non-alphanumeric keys.
58 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
59 /* key is not subject to case, so ignore SHIFT
61 ignore |= GDK_SHIFT_MASK;
64 _val = (state & ~ignore);
70 MouseButton::make_button (const string& str, MouseButton& b)
74 if (str.find ("Primary") != string::npos) {
75 s |= Keyboard::PrimaryModifier;
78 if (str.find ("Secondary") != string::npos) {
79 s |= Keyboard::SecondaryModifier;
82 if (str.find ("Tertiary") != string::npos) {
83 s |= Keyboard::TertiaryModifier;
86 if (str.find ("Level4") != string::npos) {
87 s |= Keyboard::Level4Modifier;
90 string::size_type lastmod = str.find_last_of ('-');
91 uint32_t button_number;
93 if (lastmod == string::npos) {
94 button_number = PBD::atoi (str);
96 button_number = PBD::atoi (str.substr (lastmod+1));
99 b = MouseButton (s, button_number);
104 MouseButton::name () const
110 if (s & Keyboard::PrimaryModifier) {
113 if (s & Keyboard::SecondaryModifier) {
119 if (s & Keyboard::TertiaryModifier) {
125 if (s & Keyboard::Level4Modifier) {
137 snprintf (buf, sizeof (buf), "%u", button());
143 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
145 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
147 _val = (state & ~ignore);
153 KeyboardKey::display_label () const
159 /* This magically returns a string that will display the right thing
160 * on all platforms, notably the command key on OS X.
163 uint32_t mod = state();
166 /* We use both bits (MOD2|META) for Primary on OS X,
167 * but we don't want MOD2 showing up in listings.
170 if (mod & GDK_MOD2_MASK) {
171 mod &= ~GDK_MOD2_MASK;
175 return gtk_accelerator_get_label (key(), (GdkModifierType) mod);
179 KeyboardKey::name () const
185 if (s & Keyboard::PrimaryModifier) {
188 if (s & Keyboard::SecondaryModifier) {
194 if (s & Keyboard::TertiaryModifier) {
200 if (s & Keyboard::Level4Modifier) {
211 char const *gdk_name = gdk_keyval_name (key());
224 KeyboardKey::make_key (const string& str, KeyboardKey& k)
228 if (str.find ("Primary") != string::npos) {
229 s |= Keyboard::PrimaryModifier;
232 if (str.find ("Secondary") != string::npos) {
233 s |= Keyboard::SecondaryModifier;
236 if (str.find ("Tertiary") != string::npos) {
237 s |= Keyboard::TertiaryModifier;
240 if (str.find ("Level4") != string::npos) {
241 s |= Keyboard::Level4Modifier;
244 string::size_type lastmod = str.find_last_of ('-');
247 if (lastmod == string::npos) {
248 keyval = gdk_keyval_from_name (str.c_str());
250 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
253 if (keyval == GDK_VoidSymbol || keyval == 0) {
257 k = KeyboardKey (s, keyval);
262 Bindings::Bindings (std::string const& name)
266 bindings.push_back (this);
269 Bindings::~Bindings()
271 bindings.remove (this);
275 Bindings::ardour_action_name (RefPtr<Action> action)
277 /* Skip "<Actions>/" */
278 return action->get_accel_path ().substr (10);
282 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
284 const string action_name = ardour_action_name (action);
286 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
288 /* option one: action has already been associated with the
292 if (k->second.action == action) {
296 /* option two: action name matches, so lookup the action,
297 * setup the association while we're here, and return the binding.
300 if (_action_map && k->second.action_name == action_name) {
301 k->second.action = _action_map->find_action (action_name);
307 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
309 /* option one: action has already been associated with the
313 if (k->second.action == action) {
317 /* option two: action name matches, so lookup the action,
318 * setup the association while we're here, and return the binding.
321 if (_action_map && k->second.action_name == action_name) {
322 k->second.action = _action_map->find_action (action_name);
328 return KeyboardKey::null_key();
332 Bindings::set_action_map (ActionMap& actions)
335 _action_map->set_bindings (0);
338 _action_map = &actions;
339 _action_map->set_bindings (this);
346 Bindings::empty_keys() const
348 return press_bindings.empty() && release_bindings.empty();
352 Bindings::empty_mouse () const
354 return button_press_bindings.empty() && button_release_bindings.empty();
358 Bindings::empty() const
360 return empty_keys() && empty_mouse ();
364 Bindings::activate (KeyboardKey kb, Operation op)
366 KeybindingMap* kbm = 0;
370 kbm = &press_bindings;
373 kbm = &release_bindings;
377 /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
378 Our bindings all use the lower case character/keyname, so switch
379 to the lower case before doing the lookup.
382 KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
384 KeybindingMap::iterator k = kbm->find (unshifted);
386 if (k == kbm->end()) {
387 /* no entry for this key in the state map */
388 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
392 RefPtr<Action> action;
394 if (k->second.action) {
395 action = k->second.action;
398 action = _action_map->find_action (k->second.action_name);
404 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
408 /* return true even if the action could not be found */
414 Bindings::associate ()
416 KeybindingMap::iterator k;
422 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
423 k->second.action = _action_map->find_action (k->second.action_name);
424 if (k->second.action) {
425 push_to_gtk (k->first, k->second.action);
427 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
431 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
432 k->second.action = _action_map->find_action (k->second.action_name);
433 /* no working support in GTK for release bindings */
436 MouseButtonBindingMap::iterator b;
438 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
439 b->second.action = _action_map->find_action (b->second.action_name);
442 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
443 b->second.action = _action_map->find_action (b->second.action_name);
448 Bindings::dissociate ()
450 KeybindingMap::iterator k;
452 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
453 k->second.action.clear ();
455 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
456 k->second.action.clear ();
461 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
463 /* GTK has the useful feature of showing key bindings for actions in
464 * menus. As of August 2015, we have no interest in trying to
465 * reimplement this functionality, so we will use it even though we no
466 * longer use GTK accelerators for handling key events. To do this, we
467 * need to make sure that there is a fully populated GTK AccelMap set
468 * up with all bindings/actions.
471 Gtk::AccelKey gtk_key;
472 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
476 /* there is a trick happening here. It turns out that
477 * gtk_accel_map_add_entry() performs no validation checks on
478 * the accelerator keyval. This means we can use it to define
479 * ANY accelerator, even if they violate GTK's rules
480 * (e.g. about not using navigation keys). This works ONLY when
481 * the entry in the GTK accelerator map has not already been
482 * added. The entries will be added by the GTK UIManager when
483 * building menus, so this code must be called before that
487 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) kb.state());
492 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
498 /* We have to search the existing binding map by both action and
499 * keybinding, because the following are possible:
501 * - key is already used for a different action
502 * - action has a different binding
504 * - action is not bound
507 RefPtr<Action> action = _action_map->find_action (action_name);
513 KeybindingMap* kbm = 0;
517 kbm = &press_bindings;
520 kbm = &release_bindings;
524 KeybindingMap::iterator k = kbm->find (kb);
526 if (k != kbm->end()) {
530 /* now linear search by action */
532 for (k = kbm->begin(); k != kbm->end(); ++k) {
533 if (k->second.action_name == action_name) {
539 add (kb, op, action_name, can_save);
541 /* for now, this never fails */
547 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
549 KeybindingMap* kbm = 0;
553 kbm = &press_bindings;
556 kbm = &release_bindings;
560 KeybindingMap::iterator k = kbm->find (kb);
562 if (k != kbm->end()) {
565 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
567 kbm->insert (new_pair).first;
570 Keyboard::keybindings_changed ();
573 BindingsChanged (this); /* EMIT SIGNAL */
577 Bindings::remove (KeyboardKey kb, Operation op, bool can_save)
579 KeybindingMap* kbm = 0;
583 kbm = &press_bindings;
586 kbm = &release_bindings;
590 KeybindingMap::iterator k = kbm->find (kb);
592 if (k != kbm->end()) {
597 Keyboard::keybindings_changed ();
600 BindingsChanged (this); /* EMIT SIGNAL */
604 Bindings::remove (RefPtr<Action> action, Operation op, bool can_save)
606 KeybindingMap* kbm = 0;
610 kbm = &press_bindings;
613 kbm = &release_bindings;
617 for (KeybindingMap::iterator k = kbm->begin(); k != kbm->end(); ++k) {
618 if (k->second.action == action) {
625 Keyboard::keybindings_changed ();
628 BindingsChanged (this); /* EMIT SIGNAL */
632 Bindings::activate (MouseButton bb, Operation op)
634 MouseButtonBindingMap* bbm = 0;
638 bbm = &button_press_bindings;
641 bbm = &button_release_bindings;
645 MouseButtonBindingMap::iterator b = bbm->find (bb);
647 if (b == bbm->end()) {
648 /* no entry for this key in the state map */
652 RefPtr<Action> action;
654 if (b->second.action) {
655 action = b->second.action;
658 action = _action_map->find_action (b->second.action_name);
664 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
668 /* return true even if the action could not be found */
674 Bindings::add (MouseButton bb, Operation op, string const& action_name)
676 MouseButtonBindingMap* bbm = 0;
680 bbm = &button_press_bindings;
683 bbm = &button_release_bindings;
687 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
688 bbm->insert (newpair);
692 Bindings::remove (MouseButton bb, Operation op)
694 MouseButtonBindingMap* bbm = 0;
698 bbm = &button_press_bindings;
701 bbm = &button_release_bindings;
705 MouseButtonBindingMap::iterator b = bbm->find (bb);
707 if (b != bbm->end()) {
713 Bindings::save (XMLNode& root)
715 XMLNode* presses = new XMLNode (X_("Press"));
717 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
720 if (k->first.name().empty()) {
724 child = new XMLNode (X_("Binding"));
725 child->add_property (X_("key"), k->first.name());
726 child->add_property (X_("action"), k->second.action_name);
727 presses->add_child_nocopy (*child);
730 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
732 child = new XMLNode (X_("Binding"));
733 child->add_property (X_("button"), k->first.name());
734 child->add_property (X_("action"), k->second.action_name);
735 presses->add_child_nocopy (*child);
738 XMLNode* releases = new XMLNode (X_("Release"));
740 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
743 if (k->first.name().empty()) {
747 child = new XMLNode (X_("Binding"));
748 child->add_property (X_("key"), k->first.name());
749 child->add_property (X_("action"), k->second.action_name);
750 releases->add_child_nocopy (*child);
753 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
755 child = new XMLNode (X_("Binding"));
756 child->add_property (X_("button"), k->first.name());
757 child->add_property (X_("action"), k->second.action_name);
758 releases->add_child_nocopy (*child);
761 root.add_child_nocopy (*presses);
762 root.add_child_nocopy (*releases);
766 Bindings::load (XMLNode const& node)
768 const XMLNodeList& children (node.children());
770 press_bindings.clear ();
771 release_bindings.clear ();
773 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
774 /* each node could be Press or Release */
775 load_operation (**i);
782 Bindings::load_operation (XMLNode const& node)
784 if (node.name() == X_("Press") || node.name() == X_("Release")) {
788 if (node.name() == X_("Press")) {
794 const XMLNodeList& children (node.children());
796 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
802 ap = (*p)->property ("action");
803 kp = (*p)->property ("key");
804 bp = (*p)->property ("button");
806 if (!ap || (!kp && !bp)) {
812 if (!KeyboardKey::make_key (kp->value(), k)) {
815 add (k, op, ap->value());
818 if (!MouseButton::make_button (bp->value(), b)) {
821 add (b, op, ap->value());
828 Bindings::get_all_actions (std::vector<std::string>& paths,
829 std::vector<std::string>& labels,
830 std::vector<std::string>& tooltips,
831 std::vector<std::string>& keys,
832 std::vector<RefPtr<Action> >& actions)
838 /* build a reverse map from actions to bindings */
840 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
843 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
844 rmap.insert (make_pair (k->second.action, k->first));
847 /* get a list of all actions */
849 ActionMap::Actions all_actions;
850 _action_map->get_actions (all_actions);
852 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
854 paths.push_back ((*act)->get_accel_path());
855 labels.push_back ((*act)->get_label());
856 tooltips.push_back ((*act)->get_tooltip());
858 ReverseMap::iterator r = rmap.find (*act);
860 if (r != rmap.end()) {
861 keys.push_back (r->second.display_label());
863 keys.push_back (string());
866 actions.push_back (*act);
871 Bindings::get_bindings (string const& name, ActionMap& map)
873 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
874 if ((*b)->name() == name) {
875 (*b)->set_action_map (map);
884 Bindings::associate_all ()
886 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
891 /*==========================================ACTION MAP =========================================*/
893 ActionMap::ActionMap (string const & name)
897 action_maps.push_back (this);
900 ActionMap::~ActionMap ()
902 action_maps.remove (this);
906 ActionMap::set_bindings (Bindings* b)
912 ActionMap::get_actions (ActionMap::Actions& acts)
914 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
915 acts.push_back (a->second);
920 ActionMap::find_action (const string& name)
922 _ActionMap::iterator a = _actions.find (name);
924 if (a != _actions.end()) {
928 return RefPtr<Action>();
932 ActionMap::create_action_group (const string& name)
934 RefPtr<ActionGroup> g = ActionGroup::create (name);
936 /* this is one of the places where our own Action management code
937 has to touch the GTK one, because we want the GtkUIManager to
938 be able to create widgets (particularly Menus) from our actions.
940 This is a a necessary step for that to happen.
944 ActionManager::ui_manager->insert_action_group (g);
951 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
955 RefPtr<Action> act = Action::create (name, label);
957 fullpath = group->get_name();
961 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
966 /* already registered */
967 return RefPtr<Action> ();
971 ActionMap::register_action (RefPtr<ActionGroup> group,
972 const char* name, const char* label, sigc::slot<void> sl)
976 RefPtr<Action> act = Action::create (name, label);
978 fullpath = group->get_name();
982 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
983 group->add (act, sl);
987 /* already registered */
988 return RefPtr<Action>();
992 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
993 Gtk::RadioAction::Group& rgroup,
994 const char* name, const char* label,
999 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1000 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1002 fullpath = group->get_name();
1006 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1007 group->add (act, sl);
1011 /* already registered */
1012 return RefPtr<Action>();
1016 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1017 Gtk::RadioAction::Group& rgroup,
1018 const char* name, const char* label,
1019 sigc::slot<void,GtkAction*> sl,
1024 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1025 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1026 ract->property_value() = value;
1028 fullpath = group->get_name();
1032 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1033 group->add (act, sigc::bind (sl, act->gobj()));
1037 /* already registered */
1039 return RefPtr<Action>();
1043 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1044 const char* name, const char* label, sigc::slot<void> sl)
1048 fullpath = group->get_name();
1052 RefPtr<Action> act = ToggleAction::create (name, label);
1054 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1055 group->add (act, sl);
1059 /* already registered */
1060 return RefPtr<Action>();
1064 ActionMap::get_all_actions (std::vector<std::string>& paths,
1065 std::vector<std::string>& labels,
1066 std::vector<std::string>& tooltips,
1067 std::vector<std::string>& keys,
1068 std::vector<RefPtr<Action> >& actions)
1070 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1072 ActionMap::Actions these_actions;
1073 (*map)->get_actions (these_actions);
1075 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1077 paths.push_back ((*act)->get_accel_path());
1078 labels.push_back ((*act)->get_label());
1079 tooltips.push_back ((*act)->get_tooltip());
1080 actions.push_back (*act);
1082 Bindings* bindings = (*map)->bindings();
1087 Bindings::Operation op;
1089 key = bindings->get_binding_for_action (*act, op);
1091 if (key == KeyboardKey::null_key()) {
1092 keys.push_back (string());
1094 keys.push_back (key.display_label());
1097 keys.push_back (string());
1101 these_actions.clear ();
1105 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1106 char const *gdk_name = gdk_keyval_name (k.key());
1107 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;