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 /* since all key events keycodes are changed to lower case before
248 * looking them up, make sure we only store lower case here. The Shift
249 * part will be stored in the modifier part of the KeyboardKey.
251 * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
256 if (lastmod == string::npos) {
257 lower = PBD::downcase (str);
259 lower = PBD::downcase (str.substr (lastmod+1));
262 keyval = gdk_keyval_from_name (lower.c_str());
264 if (keyval == GDK_VoidSymbol || keyval == 0) {
268 k = KeyboardKey (s, keyval);
273 Bindings::Bindings (std::string const& name)
277 bindings.push_back (this);
280 Bindings::~Bindings()
282 bindings.remove (this);
286 Bindings::ardour_action_name (RefPtr<Action> action)
288 /* Skip "<Actions>/" */
289 return action->get_accel_path ().substr (10);
293 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
295 const string action_name = ardour_action_name (action);
297 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
299 /* option one: action has already been associated with the
303 if (k->second.action == action) {
307 /* option two: action name matches, so lookup the action,
308 * setup the association while we're here, and return the binding.
311 if (_action_map && k->second.action_name == action_name) {
312 k->second.action = _action_map->find_action (action_name);
318 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
320 /* option one: action has already been associated with the
324 if (k->second.action == action) {
328 /* option two: action name matches, so lookup the action,
329 * setup the association while we're here, and return the binding.
332 if (_action_map && k->second.action_name == action_name) {
333 k->second.action = _action_map->find_action (action_name);
339 return KeyboardKey::null_key();
343 Bindings::set_action_map (ActionMap& actions)
346 _action_map->set_bindings (0);
349 _action_map = &actions;
350 _action_map->set_bindings (this);
357 Bindings::empty_keys() const
359 return press_bindings.empty() && release_bindings.empty();
363 Bindings::empty_mouse () const
365 return button_press_bindings.empty() && button_release_bindings.empty();
369 Bindings::empty() const
371 return empty_keys() && empty_mouse ();
375 Bindings::activate (KeyboardKey kb, Operation op)
377 KeybindingMap* kbm = 0;
381 kbm = &press_bindings;
384 kbm = &release_bindings;
388 /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
389 Our bindings all use the lower case character/keyname, so switch
390 to the lower case before doing the lookup.
393 KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
395 KeybindingMap::iterator k = kbm->find (unshifted);
397 if (k == kbm->end()) {
398 /* no entry for this key in the state map */
399 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
403 RefPtr<Action> action;
405 if (k->second.action) {
406 action = k->second.action;
409 action = _action_map->find_action (k->second.action_name);
415 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
419 /* return true even if the action could not be found */
425 Bindings::associate ()
427 KeybindingMap::iterator k;
433 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
434 k->second.action = _action_map->find_action (k->second.action_name);
435 if (k->second.action) {
436 push_to_gtk (k->first, k->second.action);
438 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
442 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
443 k->second.action = _action_map->find_action (k->second.action_name);
444 /* no working support in GTK for release bindings */
447 MouseButtonBindingMap::iterator b;
449 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
450 b->second.action = _action_map->find_action (b->second.action_name);
453 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
454 b->second.action = _action_map->find_action (b->second.action_name);
459 Bindings::dissociate ()
461 KeybindingMap::iterator k;
463 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
464 k->second.action.clear ();
466 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
467 k->second.action.clear ();
472 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
474 /* GTK has the useful feature of showing key bindings for actions in
475 * menus. As of August 2015, we have no interest in trying to
476 * reimplement this functionality, so we will use it even though we no
477 * longer use GTK accelerators for handling key events. To do this, we
478 * need to make sure that there is a fully populated GTK AccelMap set
479 * up with all bindings/actions.
482 Gtk::AccelKey gtk_key;
483 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
487 /* there is a trick happening here. It turns out that
488 * gtk_accel_map_add_entry() performs no validation checks on
489 * the accelerator keyval. This means we can use it to define
490 * ANY accelerator, even if they violate GTK's rules
491 * (e.g. about not using navigation keys). This works ONLY when
492 * the entry in the GTK accelerator map has not already been
493 * added. The entries will be added by the GTK UIManager when
494 * building menus, so this code must be called before that
498 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) kb.state());
503 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
509 /* We have to search the existing binding map by both action and
510 * keybinding, because the following are possible:
512 * - key is already used for a different action
513 * - action has a different binding
515 * - action is not bound
518 RefPtr<Action> action = _action_map->find_action (action_name);
524 KeybindingMap* kbm = 0;
528 kbm = &press_bindings;
531 kbm = &release_bindings;
535 KeybindingMap::iterator k = kbm->find (kb);
537 if (k != kbm->end()) {
541 /* now linear search by action */
543 for (k = kbm->begin(); k != kbm->end(); ++k) {
544 if (k->second.action_name == action_name) {
550 add (kb, op, action_name, can_save);
552 /* for now, this never fails */
558 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
560 KeybindingMap* kbm = 0;
564 kbm = &press_bindings;
567 kbm = &release_bindings;
571 KeybindingMap::iterator k = kbm->find (kb);
573 if (k != kbm->end()) {
576 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
578 kbm->insert (new_pair).first;
581 Keyboard::keybindings_changed ();
584 BindingsChanged (this); /* EMIT SIGNAL */
588 Bindings::remove (KeyboardKey kb, Operation op, bool can_save)
590 KeybindingMap* kbm = 0;
594 kbm = &press_bindings;
597 kbm = &release_bindings;
601 KeybindingMap::iterator k = kbm->find (kb);
603 if (k != kbm->end()) {
608 Keyboard::keybindings_changed ();
611 BindingsChanged (this); /* EMIT SIGNAL */
615 Bindings::remove (RefPtr<Action> action, Operation op, bool can_save)
617 KeybindingMap* kbm = 0;
621 kbm = &press_bindings;
624 kbm = &release_bindings;
628 for (KeybindingMap::iterator k = kbm->begin(); k != kbm->end(); ++k) {
629 if (k->second.action == action) {
636 Keyboard::keybindings_changed ();
639 BindingsChanged (this); /* EMIT SIGNAL */
643 Bindings::activate (MouseButton bb, Operation op)
645 MouseButtonBindingMap* bbm = 0;
649 bbm = &button_press_bindings;
652 bbm = &button_release_bindings;
656 MouseButtonBindingMap::iterator b = bbm->find (bb);
658 if (b == bbm->end()) {
659 /* no entry for this key in the state map */
663 RefPtr<Action> action;
665 if (b->second.action) {
666 action = b->second.action;
669 action = _action_map->find_action (b->second.action_name);
675 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
679 /* return true even if the action could not be found */
685 Bindings::add (MouseButton bb, Operation op, string const& action_name)
687 MouseButtonBindingMap* bbm = 0;
691 bbm = &button_press_bindings;
694 bbm = &button_release_bindings;
698 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
699 bbm->insert (newpair);
703 Bindings::remove (MouseButton bb, Operation op)
705 MouseButtonBindingMap* bbm = 0;
709 bbm = &button_press_bindings;
712 bbm = &button_release_bindings;
716 MouseButtonBindingMap::iterator b = bbm->find (bb);
718 if (b != bbm->end()) {
724 Bindings::save (XMLNode& root)
726 XMLNode* presses = new XMLNode (X_("Press"));
728 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
731 if (k->first.name().empty()) {
735 child = new XMLNode (X_("Binding"));
736 child->add_property (X_("key"), k->first.name());
737 child->add_property (X_("action"), k->second.action_name);
738 presses->add_child_nocopy (*child);
741 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
743 child = new XMLNode (X_("Binding"));
744 child->add_property (X_("button"), k->first.name());
745 child->add_property (X_("action"), k->second.action_name);
746 presses->add_child_nocopy (*child);
749 XMLNode* releases = new XMLNode (X_("Release"));
751 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
754 if (k->first.name().empty()) {
758 child = new XMLNode (X_("Binding"));
759 child->add_property (X_("key"), k->first.name());
760 child->add_property (X_("action"), k->second.action_name);
761 releases->add_child_nocopy (*child);
764 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
766 child = new XMLNode (X_("Binding"));
767 child->add_property (X_("button"), k->first.name());
768 child->add_property (X_("action"), k->second.action_name);
769 releases->add_child_nocopy (*child);
772 root.add_child_nocopy (*presses);
773 root.add_child_nocopy (*releases);
777 Bindings::load (XMLNode const& node)
779 const XMLNodeList& children (node.children());
781 press_bindings.clear ();
782 release_bindings.clear ();
784 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
785 /* each node could be Press or Release */
786 load_operation (**i);
793 Bindings::load_operation (XMLNode const& node)
795 if (node.name() == X_("Press") || node.name() == X_("Release")) {
799 if (node.name() == X_("Press")) {
805 const XMLNodeList& children (node.children());
807 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
813 ap = (*p)->property ("action");
814 kp = (*p)->property ("key");
815 bp = (*p)->property ("button");
817 if (!ap || (!kp && !bp)) {
823 if (!KeyboardKey::make_key (kp->value(), k)) {
826 add (k, op, ap->value());
829 if (!MouseButton::make_button (bp->value(), b)) {
832 add (b, op, ap->value());
839 Bindings::get_all_actions (std::vector<std::string>& paths,
840 std::vector<std::string>& labels,
841 std::vector<std::string>& tooltips,
842 std::vector<std::string>& keys,
843 std::vector<RefPtr<Action> >& actions)
849 /* build a reverse map from actions to bindings */
851 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
854 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
855 rmap.insert (make_pair (k->second.action, k->first));
858 /* get a list of all actions */
860 ActionMap::Actions all_actions;
861 _action_map->get_actions (all_actions);
863 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
865 paths.push_back ((*act)->get_accel_path());
866 labels.push_back ((*act)->get_label());
867 tooltips.push_back ((*act)->get_tooltip());
869 ReverseMap::iterator r = rmap.find (*act);
871 if (r != rmap.end()) {
872 keys.push_back (r->second.display_label());
874 keys.push_back (string());
877 actions.push_back (*act);
882 Bindings::get_bindings (string const& name, ActionMap& map)
884 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
885 if ((*b)->name() == name) {
886 (*b)->set_action_map (map);
895 Bindings::associate_all ()
897 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
902 /*==========================================ACTION MAP =========================================*/
904 ActionMap::ActionMap (string const & name)
908 action_maps.push_back (this);
911 ActionMap::~ActionMap ()
913 action_maps.remove (this);
917 ActionMap::set_bindings (Bindings* b)
923 ActionMap::get_actions (ActionMap::Actions& acts)
925 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
926 acts.push_back (a->second);
931 ActionMap::find_action (const string& name)
933 _ActionMap::iterator a = _actions.find (name);
935 if (a != _actions.end()) {
939 return RefPtr<Action>();
943 ActionMap::create_action_group (const string& name)
945 RefPtr<ActionGroup> g = ActionGroup::create (name);
947 /* this is one of the places where our own Action management code
948 has to touch the GTK one, because we want the GtkUIManager to
949 be able to create widgets (particularly Menus) from our actions.
951 This is a a necessary step for that to happen.
955 ActionManager::ui_manager->insert_action_group (g);
962 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
966 RefPtr<Action> act = Action::create (name, label);
968 fullpath = group->get_name();
972 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
977 /* already registered */
978 return RefPtr<Action> ();
982 ActionMap::register_action (RefPtr<ActionGroup> group,
983 const char* name, const char* label, sigc::slot<void> sl)
987 RefPtr<Action> act = Action::create (name, label);
989 fullpath = group->get_name();
993 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
994 group->add (act, sl);
998 /* already registered */
999 return RefPtr<Action>();
1003 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1004 Gtk::RadioAction::Group& rgroup,
1005 const char* name, const char* label,
1006 sigc::slot<void> sl)
1010 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1011 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1013 fullpath = group->get_name();
1017 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1018 group->add (act, sl);
1022 /* already registered */
1023 return RefPtr<Action>();
1027 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1028 Gtk::RadioAction::Group& rgroup,
1029 const char* name, const char* label,
1030 sigc::slot<void,GtkAction*> sl,
1035 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1036 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1037 ract->property_value() = value;
1039 fullpath = group->get_name();
1043 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1044 group->add (act, sigc::bind (sl, act->gobj()));
1048 /* already registered */
1050 return RefPtr<Action>();
1054 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1055 const char* name, const char* label, sigc::slot<void> sl)
1059 fullpath = group->get_name();
1063 RefPtr<Action> act = ToggleAction::create (name, label);
1065 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1066 group->add (act, sl);
1070 /* already registered */
1071 return RefPtr<Action>();
1075 ActionMap::get_all_actions (std::vector<std::string>& paths,
1076 std::vector<std::string>& labels,
1077 std::vector<std::string>& tooltips,
1078 std::vector<std::string>& keys,
1079 std::vector<RefPtr<Action> >& actions)
1081 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1083 ActionMap::Actions these_actions;
1084 (*map)->get_actions (these_actions);
1086 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1088 paths.push_back ((*act)->get_accel_path());
1089 labels.push_back ((*act)->get_label());
1090 tooltips.push_back ((*act)->get_tooltip());
1091 actions.push_back (*act);
1093 Bindings* bindings = (*map)->bindings();
1098 Bindings::Operation op;
1100 key = bindings->get_binding_for_action (*act, op);
1102 if (key == KeyboardKey::null_key()) {
1103 keys.push_back (string());
1105 keys.push_back (key.display_label());
1108 keys.push_back (string());
1112 these_actions.clear ();
1116 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1117 char const *gdk_name = gdk_keyval_name (k.key());
1118 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;