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 uint32_t Bindings::_ignored_state = 0;
47 list<ActionMap*> ActionMap::action_maps; /* global. Gulp */
48 PBD::Signal1<void,Bindings*> Bindings::BindingsChanged;
50 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
52 uint32_t ignore = Bindings::ignored_state();
54 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
55 /* key is not subject to case, so ignore SHIFT
57 ignore |= GDK_SHIFT_MASK;
60 _val = (state & ~ignore);
66 MouseButton::make_button (const string& str, MouseButton& b)
70 if (str.find ("Primary") != string::npos) {
71 s |= Keyboard::PrimaryModifier;
74 if (str.find ("Secondary") != string::npos) {
75 s |= Keyboard::SecondaryModifier;
78 if (str.find ("Tertiary") != string::npos) {
79 s |= Keyboard::TertiaryModifier;
82 if (str.find ("Level4") != string::npos) {
83 s |= Keyboard::Level4Modifier;
86 string::size_type lastmod = str.find_last_of ('-');
87 uint32_t button_number;
89 if (lastmod == string::npos) {
90 button_number = PBD::atoi (str);
92 button_number = PBD::atoi (str.substr (lastmod+1));
95 b = MouseButton (s, button_number);
100 MouseButton::name () const
106 if (s & Keyboard::PrimaryModifier) {
109 if (s & Keyboard::SecondaryModifier) {
115 if (s & Keyboard::TertiaryModifier) {
121 if (s & Keyboard::Level4Modifier) {
133 snprintf (buf, sizeof (buf), "%u", button());
139 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
141 uint32_t ignore = Bindings::ignored_state();
143 _val = (state & ~ignore);
149 KeyboardKey::display_label () const
155 /* This magically returns a string that will display the right thing
156 * on all platforms, notably the command key on OS X.
159 return gtk_accelerator_get_label (key(), (GdkModifierType) state());
163 KeyboardKey::name () const
169 if (s & Keyboard::PrimaryModifier) {
172 if (s & Keyboard::SecondaryModifier) {
178 if (s & Keyboard::TertiaryModifier) {
184 if (s & Keyboard::Level4Modifier) {
195 char const *gdk_name = gdk_keyval_name (key());
208 KeyboardKey::make_key (const string& str, KeyboardKey& k)
212 if (str.find ("Primary") != string::npos) {
213 s |= Keyboard::PrimaryModifier;
216 if (str.find ("Secondary") != string::npos) {
217 s |= Keyboard::SecondaryModifier;
220 if (str.find ("Tertiary") != string::npos) {
221 s |= Keyboard::TertiaryModifier;
224 if (str.find ("Level4") != string::npos) {
225 s |= Keyboard::Level4Modifier;
228 string::size_type lastmod = str.find_last_of ('-');
231 if (lastmod == string::npos) {
232 keyval = gdk_keyval_from_name (str.c_str());
234 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
237 if (keyval == GDK_VoidSymbol || keyval == 0) {
241 k = KeyboardKey (s, keyval);
246 Bindings::Bindings (std::string const& name)
250 bindings.push_back (this);
253 Bindings::~Bindings()
255 bindings.remove (this);
259 Bindings::ardour_action_name (RefPtr<Action> action)
261 /* Skip "<Actions>/" */
262 return action->get_accel_path ().substr (10);
266 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
268 const string action_name = ardour_action_name (action);
270 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
272 /* option one: action has already been associated with the
276 if (k->second.action == action) {
280 /* option two: action name matches, so lookup the action,
281 * setup the association while we're here, and return the binding.
284 if (_action_map && k->second.action_name == action_name) {
285 k->second.action = _action_map->find_action (action_name);
291 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
293 /* option one: action has already been associated with the
297 if (k->second.action == action) {
301 /* option two: action name matches, so lookup the action,
302 * setup the association while we're here, and return the binding.
305 if (_action_map && k->second.action_name == action_name) {
306 k->second.action = _action_map->find_action (action_name);
312 return KeyboardKey::null_key();
316 Bindings::set_action_map (ActionMap& actions)
319 _action_map->set_bindings (0);
322 _action_map = &actions;
323 _action_map->set_bindings (this);
330 Bindings::empty_keys() const
332 return press_bindings.empty() && release_bindings.empty();
336 Bindings::empty_mouse () const
338 return button_press_bindings.empty() && button_release_bindings.empty();
342 Bindings::empty() const
344 return empty_keys() && empty_mouse ();
348 Bindings::activate (KeyboardKey kb, Operation op)
350 KeybindingMap* kbm = 0;
354 kbm = &press_bindings;
357 kbm = &release_bindings;
361 KeybindingMap::iterator k = kbm->find (kb);
363 if (k == kbm->end()) {
364 /* no entry for this key in the state map */
365 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", kb));
369 RefPtr<Action> action;
371 if (k->second.action) {
372 action = k->second.action;
375 action = _action_map->find_action (k->second.action_name);
381 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", kb, k->second.action_name));
385 /* return true even if the action could not be found */
391 Bindings::associate ()
393 KeybindingMap::iterator k;
399 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
400 k->second.action = _action_map->find_action (k->second.action_name);
401 if (k->second.action) {
402 push_to_gtk (k->first, k->second.action);
404 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
408 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
409 k->second.action = _action_map->find_action (k->second.action_name);
410 /* no working support in GTK for release bindings */
413 MouseButtonBindingMap::iterator b;
415 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
416 b->second.action = _action_map->find_action (b->second.action_name);
419 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
420 b->second.action = _action_map->find_action (b->second.action_name);
425 Bindings::dissociate ()
427 KeybindingMap::iterator k;
429 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
430 k->second.action.clear ();
432 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
433 k->second.action.clear ();
438 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
440 /* GTK has the useful feature of showing key bindings for actions in
441 * menus. As of August 2015, we have no interest in trying to
442 * reimplement this functionality, so we will use it even though we no
443 * longer use GTK accelerators for handling key events. To do this, we
444 * need to make sure that there is a fully populated GTK AccelMap set
445 * up with all bindings/actions.
448 uint32_t gtk_legal_keyval = kb.key();
449 possibly_translate_keyval_to_make_legal_accelerator (gtk_legal_keyval);
450 KeyboardKey gtk_binding (kb.state(), gtk_legal_keyval);
451 Gtk::AccelKey gtk_key;
453 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
457 /* there is a trick happening here. It turns out that
458 * gtk_accel_map_add_entry() performs no validation checks on
459 * the accelerator keyval. This means we can use it to define
460 * ANY accelerator, even if they violate GTK's rules
461 * (e.g. about not using navigation keys). This works ONLY when
462 * the entry in the GTK accelerator map has not already been
463 * added. The entries will be added by the GTK UIManager when
464 * building menus, so this code must be called before that
468 Gtk::AccelMap::add_entry (what->get_accel_path(), gtk_binding.key(), (Gdk::ModifierType) gtk_binding.state());
473 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
479 /* We have to search the existing binding map by both action and
480 * keybinding, because the following are possible:
482 * - key is already used for a different action
483 * - action has a different binding
485 * - action is not bound
488 RefPtr<Action> action = _action_map->find_action (action_name);
494 KeybindingMap* kbm = 0;
498 kbm = &press_bindings;
501 kbm = &release_bindings;
505 KeybindingMap::iterator k = kbm->find (kb);
507 if (k != kbm->end()) {
511 /* now linear search by action */
513 for (k = kbm->begin(); k != kbm->end(); ++k) {
514 if (k->second.action_name == action_name) {
520 add (kb, op, action_name, can_save);
522 /* for now, this never fails */
528 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
530 KeybindingMap* kbm = 0;
534 kbm = &press_bindings;
537 kbm = &release_bindings;
541 KeybindingMap::iterator k = kbm->find (kb);
543 if (k != kbm->end()) {
546 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
548 kbm->insert (new_pair).first;
551 Keyboard::keybindings_changed ();
554 BindingsChanged (this); /* EMIT SIGNAL */
558 Bindings::remove (KeyboardKey kb, Operation op, 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()) {
578 Keyboard::keybindings_changed ();
581 BindingsChanged (this); /* EMIT SIGNAL */
585 Bindings::remove (RefPtr<Action> action, Operation op, bool can_save)
587 KeybindingMap* kbm = 0;
591 kbm = &press_bindings;
594 kbm = &release_bindings;
598 for (KeybindingMap::iterator k = kbm->begin(); k != kbm->end(); ++k) {
599 if (k->second.action == action) {
606 Keyboard::keybindings_changed ();
609 BindingsChanged (this); /* EMIT SIGNAL */
613 Bindings::activate (MouseButton bb, Operation op)
615 MouseButtonBindingMap* bbm = 0;
619 bbm = &button_press_bindings;
622 bbm = &button_release_bindings;
626 MouseButtonBindingMap::iterator b = bbm->find (bb);
628 if (b == bbm->end()) {
629 /* no entry for this key in the state map */
633 RefPtr<Action> action;
635 if (b->second.action) {
636 action = b->second.action;
639 action = _action_map->find_action (b->second.action_name);
645 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
649 /* return true even if the action could not be found */
655 Bindings::add (MouseButton bb, Operation op, string const& action_name)
657 MouseButtonBindingMap* bbm = 0;
661 bbm = &button_press_bindings;
664 bbm = &button_release_bindings;
668 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
669 bbm->insert (newpair);
673 Bindings::remove (MouseButton bb, Operation op)
675 MouseButtonBindingMap* bbm = 0;
679 bbm = &button_press_bindings;
682 bbm = &button_release_bindings;
686 MouseButtonBindingMap::iterator b = bbm->find (bb);
688 if (b != bbm->end()) {
694 Bindings::save (XMLNode& root)
696 XMLNode* presses = new XMLNode (X_("Press"));
698 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
701 if (k->first.name().empty()) {
705 child = new XMLNode (X_("Binding"));
706 child->add_property (X_("key"), k->first.name());
707 child->add_property (X_("action"), k->second.action_name);
708 presses->add_child_nocopy (*child);
711 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
713 child = new XMLNode (X_("Binding"));
714 child->add_property (X_("button"), k->first.name());
715 child->add_property (X_("action"), k->second.action_name);
716 presses->add_child_nocopy (*child);
719 XMLNode* releases = new XMLNode (X_("Release"));
721 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
724 if (k->first.name().empty()) {
728 child = new XMLNode (X_("Binding"));
729 child->add_property (X_("key"), k->first.name());
730 child->add_property (X_("action"), k->second.action_name);
731 releases->add_child_nocopy (*child);
734 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
736 child = new XMLNode (X_("Binding"));
737 child->add_property (X_("button"), k->first.name());
738 child->add_property (X_("action"), k->second.action_name);
739 releases->add_child_nocopy (*child);
742 root.add_child_nocopy (*presses);
743 root.add_child_nocopy (*releases);
747 Bindings::load (XMLNode const& node)
749 const XMLNodeList& children (node.children());
751 press_bindings.clear ();
752 release_bindings.clear ();
754 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
755 /* each node could be Press or Release */
756 load_operation (**i);
763 Bindings::load_operation (XMLNode const& node)
765 if (node.name() == X_("Press") || node.name() == X_("Release")) {
769 if (node.name() == X_("Press")) {
775 const XMLNodeList& children (node.children());
777 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
783 ap = (*p)->property ("action");
784 kp = (*p)->property ("key");
785 bp = (*p)->property ("button");
787 if (!ap || (!kp && !bp)) {
793 if (!KeyboardKey::make_key (kp->value(), k)) {
796 add (k, op, ap->value());
799 if (!MouseButton::make_button (bp->value(), b)) {
802 add (b, op, ap->value());
809 Bindings::get_all_actions (std::vector<std::string>& paths,
810 std::vector<std::string>& labels,
811 std::vector<std::string>& tooltips,
812 std::vector<std::string>& keys,
813 std::vector<RefPtr<Action> >& actions)
819 /* build a reverse map from actions to bindings */
821 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
824 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
825 rmap.insert (make_pair (k->second.action, k->first));
828 /* get a list of all actions */
830 ActionMap::Actions all_actions;
831 _action_map->get_actions (all_actions);
833 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
835 paths.push_back ((*act)->get_accel_path());
836 labels.push_back ((*act)->get_label());
837 tooltips.push_back ((*act)->get_tooltip());
839 ReverseMap::iterator r = rmap.find (*act);
841 if (r != rmap.end()) {
842 keys.push_back (r->second.display_label());
844 keys.push_back (string());
847 actions.push_back (*act);
852 Bindings::get_bindings (string const& name, ActionMap& map)
854 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
855 if ((*b)->name() == name) {
856 (*b)->set_action_map (map);
865 Bindings::associate_all ()
867 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
872 /*==========================================ACTION MAP =========================================*/
874 ActionMap::ActionMap (string const & name)
878 action_maps.push_back (this);
881 ActionMap::~ActionMap ()
883 action_maps.remove (this);
887 ActionMap::set_bindings (Bindings* b)
893 ActionMap::get_actions (ActionMap::Actions& acts)
895 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
896 acts.push_back (a->second);
901 ActionMap::find_action (const string& name)
903 _ActionMap::iterator a = _actions.find (name);
905 if (a != _actions.end()) {
909 return RefPtr<Action>();
913 ActionMap::create_action_group (const string& name)
915 RefPtr<ActionGroup> g = ActionGroup::create (name);
917 /* this is one of the places where our own Action management code
918 has to touch the GTK one, because we want the GtkUIManager to
919 be able to create widgets (particularly Menus) from our actions.
921 This is a a necessary step for that to happen.
925 ActionManager::ui_manager->insert_action_group (g);
932 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
936 RefPtr<Action> act = Action::create (name, label);
938 fullpath = group->get_name();
942 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
947 /* already registered */
948 return RefPtr<Action> ();
952 ActionMap::register_action (RefPtr<ActionGroup> group,
953 const char* name, const char* label, sigc::slot<void> sl)
957 RefPtr<Action> act = Action::create (name, label);
959 fullpath = group->get_name();
963 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
964 group->add (act, sl);
968 /* already registered */
969 return RefPtr<Action>();
973 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
974 Gtk::RadioAction::Group& rgroup,
975 const char* name, const char* label,
980 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
981 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
983 fullpath = group->get_name();
987 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
988 group->add (act, sl);
992 /* already registered */
993 return RefPtr<Action>();
997 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
998 Gtk::RadioAction::Group& rgroup,
999 const char* name, const char* label,
1000 sigc::slot<void,GtkAction*> sl,
1005 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1006 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1007 ract->property_value() = value;
1009 fullpath = group->get_name();
1013 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1014 group->add (act, sigc::bind (sl, act->gobj()));
1018 /* already registered */
1020 return RefPtr<Action>();
1024 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1025 const char* name, const char* label, sigc::slot<void> sl)
1029 fullpath = group->get_name();
1033 RefPtr<Action> act = ToggleAction::create (name, label);
1035 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1036 group->add (act, sl);
1040 /* already registered */
1041 return RefPtr<Action>();
1045 ActionMap::get_all_actions (std::vector<std::string>& paths,
1046 std::vector<std::string>& labels,
1047 std::vector<std::string>& tooltips,
1048 std::vector<std::string>& keys,
1049 std::vector<RefPtr<Action> >& actions)
1051 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1053 ActionMap::Actions these_actions;
1054 (*map)->get_actions (these_actions);
1056 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1058 paths.push_back ((*act)->get_accel_path());
1059 labels.push_back ((*act)->get_label());
1060 tooltips.push_back ((*act)->get_tooltip());
1061 actions.push_back (*act);
1063 Bindings* bindings = (*map)->bindings();
1068 Bindings::Operation op;
1070 key = bindings->get_binding_for_action (*act, op);
1072 if (key == KeyboardKey::null_key()) {
1073 keys.push_back (string());
1075 keys.push_back (key.display_label());
1078 keys.push_back (string());
1082 these_actions.clear ();
1086 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1087 char const *gdk_name = gdk_keyval_name (k.key());
1088 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << k.state();