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)
318 _action_map = &actions;
324 Bindings::empty_keys() const
326 return press_bindings.empty() && release_bindings.empty();
330 Bindings::empty_mouse () const
332 return button_press_bindings.empty() && button_release_bindings.empty();
336 Bindings::empty() const
338 return empty_keys() && empty_mouse ();
342 Bindings::activate (KeyboardKey kb, Operation op)
344 KeybindingMap* kbm = 0;
348 kbm = &press_bindings;
351 kbm = &release_bindings;
355 KeybindingMap::iterator k = kbm->find (kb);
357 if (k == kbm->end()) {
358 /* no entry for this key in the state map */
359 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", kb));
363 RefPtr<Action> action;
365 if (k->second.action) {
366 action = k->second.action;
369 action = _action_map->find_action (k->second.action_name);
375 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", kb, k->second.action_name));
379 /* return true even if the action could not be found */
385 Bindings::associate ()
387 KeybindingMap::iterator k;
393 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
394 k->second.action = _action_map->find_action (k->second.action_name);
395 if (k->second.action) {
396 cerr << "push to GTK " << k->first << ' ' << k->second.action_name << endl;
397 push_to_gtk (k->first, k->second.action);
399 cerr << "didn't find " << k->second.action_name << endl;
403 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
404 k->second.action = _action_map->find_action (k->second.action_name);
405 /* no working support in GTK for release bindings */
408 MouseButtonBindingMap::iterator b;
410 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
411 b->second.action = _action_map->find_action (b->second.action_name);
414 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
415 b->second.action = _action_map->find_action (b->second.action_name);
420 Bindings::dissociate ()
422 KeybindingMap::iterator k;
424 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
425 k->second.action.clear ();
427 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
428 k->second.action.clear ();
433 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
435 /* GTK has the useful feature of showing key bindings for actions in
436 * menus. As of August 2015, we have no interest in trying to
437 * reimplement this functionality, so we will use it even though we no
438 * longer use GTK accelerators for handling key events. To do this, we
439 * need to make sure that there is a fully populated GTK AccelMap set
440 * up with all bindings/actions.
443 uint32_t gtk_legal_keyval = kb.key();
444 possibly_translate_keyval_to_make_legal_accelerator (gtk_legal_keyval);
445 KeyboardKey gtk_binding (kb.state(), gtk_legal_keyval);
446 Gtk::AccelKey gtk_key;
448 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
452 /* there is a trick happening here. It turns out that
453 * gtk_accel_map_add_entry() performs no validation checks on
454 * the accelerator keyval. This means we can use it to define
455 * ANY accelerator, even if they violate GTK's rules
456 * (e.g. about not using navigation keys). This works ONLY when
457 * the entry in the GTK accelerator map has not already been
458 * added. The entries will be added by the GTK UIManager when
459 * building menus, so this code must be called before that
463 Gtk::AccelMap::add_entry (what->get_accel_path(), gtk_binding.key(), (Gdk::ModifierType) gtk_binding.state());
468 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
474 /* We have to search the existing binding map by both action and
475 * keybinding, because the following are possible:
477 * - key is already used for a different action
478 * - action has a different binding
480 * - action is not bound
483 RefPtr<Action> action = _action_map->find_action (action_name);
489 KeybindingMap* kbm = 0;
493 kbm = &press_bindings;
496 kbm = &release_bindings;
500 KeybindingMap::iterator k = kbm->find (kb);
502 if (k != kbm->end()) {
506 /* now linear search by action */
508 for (k = kbm->begin(); k != kbm->end(); ++k) {
509 if (k->second.action_name == action_name) {
515 add (kb, op, action_name, can_save);
517 /* for now, this never fails */
523 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
525 KeybindingMap* kbm = 0;
529 kbm = &press_bindings;
532 kbm = &release_bindings;
536 KeybindingMap::iterator k = kbm->find (kb);
538 if (k != kbm->end()) {
541 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
543 kbm->insert (new_pair).first;
546 Keyboard::keybindings_changed ();
549 BindingsChanged (this); /* EMIT SIGNAL */
553 Bindings::remove (KeyboardKey kb, Operation op, bool can_save)
555 KeybindingMap* kbm = 0;
559 kbm = &press_bindings;
562 kbm = &release_bindings;
566 KeybindingMap::iterator k = kbm->find (kb);
568 if (k != kbm->end()) {
573 Keyboard::keybindings_changed ();
576 BindingsChanged (this); /* EMIT SIGNAL */
580 Bindings::remove (RefPtr<Action> action, Operation op, bool can_save)
582 KeybindingMap* kbm = 0;
586 kbm = &press_bindings;
589 kbm = &release_bindings;
593 for (KeybindingMap::iterator k = kbm->begin(); k != kbm->end(); ++k) {
594 if (k->second.action == action) {
601 Keyboard::keybindings_changed ();
604 BindingsChanged (this); /* EMIT SIGNAL */
608 Bindings::activate (MouseButton bb, Operation op)
610 MouseButtonBindingMap* bbm = 0;
614 bbm = &button_press_bindings;
617 bbm = &button_release_bindings;
621 MouseButtonBindingMap::iterator b = bbm->find (bb);
623 if (b == bbm->end()) {
624 /* no entry for this key in the state map */
628 RefPtr<Action> action;
630 if (b->second.action) {
631 action = b->second.action;
634 action = _action_map->find_action (b->second.action_name);
640 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
644 /* return true even if the action could not be found */
650 Bindings::add (MouseButton bb, Operation op, string const& action_name)
652 MouseButtonBindingMap* bbm = 0;
656 bbm = &button_press_bindings;
659 bbm = &button_release_bindings;
663 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
664 bbm->insert (newpair);
668 Bindings::remove (MouseButton bb, Operation op)
670 MouseButtonBindingMap* bbm = 0;
674 bbm = &button_press_bindings;
677 bbm = &button_release_bindings;
681 MouseButtonBindingMap::iterator b = bbm->find (bb);
683 if (b != bbm->end()) {
689 Bindings::save (XMLNode& root)
691 XMLNode* presses = new XMLNode (X_("Press"));
693 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
696 if (k->first.name().empty()) {
700 child = new XMLNode (X_("Binding"));
701 child->add_property (X_("key"), k->first.name());
702 child->add_property (X_("action"), k->second.action_name);
703 presses->add_child_nocopy (*child);
706 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
708 child = new XMLNode (X_("Binding"));
709 child->add_property (X_("button"), k->first.name());
710 child->add_property (X_("action"), k->second.action_name);
711 presses->add_child_nocopy (*child);
714 XMLNode* releases = new XMLNode (X_("Release"));
716 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
719 if (k->first.name().empty()) {
723 child = new XMLNode (X_("Binding"));
724 child->add_property (X_("key"), k->first.name());
725 child->add_property (X_("action"), k->second.action_name);
726 releases->add_child_nocopy (*child);
729 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
731 child = new XMLNode (X_("Binding"));
732 child->add_property (X_("button"), k->first.name());
733 child->add_property (X_("action"), k->second.action_name);
734 releases->add_child_nocopy (*child);
737 root.add_child_nocopy (*presses);
738 root.add_child_nocopy (*releases);
742 Bindings::load (XMLNode const& node)
744 const XMLNodeList& children (node.children());
746 press_bindings.clear ();
747 release_bindings.clear ();
749 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
750 /* each node could be Press or Release */
751 load_operation (**i);
758 Bindings::load_operation (XMLNode const& node)
760 if (node.name() == X_("Press") || node.name() == X_("Release")) {
764 if (node.name() == X_("Press")) {
770 const XMLNodeList& children (node.children());
772 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
778 ap = (*p)->property ("action");
779 kp = (*p)->property ("key");
780 bp = (*p)->property ("button");
782 if (!ap || (!kp && !bp)) {
788 if (!KeyboardKey::make_key (kp->value(), k)) {
791 add (k, op, ap->value());
794 if (!MouseButton::make_button (bp->value(), b)) {
797 add (b, op, ap->value());
804 Bindings::get_all_actions (std::vector<std::string>& paths,
805 std::vector<std::string>& labels,
806 std::vector<std::string>& tooltips,
807 std::vector<std::string>& keys,
808 std::vector<RefPtr<Action> >& actions)
814 /* build a reverse map from actions to bindings */
816 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
819 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
820 rmap.insert (make_pair (k->second.action, k->first));
823 /* get a list of all actions */
825 ActionMap::Actions all_actions;
826 _action_map->get_actions (all_actions);
828 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
830 paths.push_back ((*act)->get_accel_path());
831 labels.push_back ((*act)->get_label());
832 tooltips.push_back ((*act)->get_tooltip());
834 ReverseMap::iterator r = rmap.find (*act);
836 if (r != rmap.end()) {
837 keys.push_back (r->second.display_label());
839 keys.push_back (string());
842 actions.push_back (*act);
847 Bindings::get_all_actions (std::vector<std::string>& names,
848 std::vector<std::string>& paths,
849 std::vector<std::string>& keys)
855 /* build a reverse map from actions to bindings */
857 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
860 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
861 rmap.insert (make_pair (k->second.action, k->first));
864 /* get a list of all actions */
866 ActionMap::Actions all_actions;
867 _action_map->get_actions (all_actions);
869 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
871 names.push_back ((*act)->get_name());
872 paths.push_back ((*act)->get_accel_path());
874 ReverseMap::iterator r = rmap.find (*act);
875 if (r != rmap.end()) {
876 keys.push_back (r->second.display_label());
878 keys.push_back (string());
884 Bindings::get_bindings (string const& name, ActionMap& map)
886 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
887 if ((*b)->name() == name) {
888 (*b)->set_action_map (map);
897 Bindings::associate_all ()
899 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
904 /*==========================================ACTION MAP =========================================*/
907 ActionMap::get_actions (ActionMap::Actions& acts)
909 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
910 acts.push_back (a->second);
915 ActionMap::find_action (const string& name)
917 _ActionMap::iterator a = _actions.find (name);
919 if (a != _actions.end()) {
923 return RefPtr<Action>();
927 ActionMap::create_action_group (const string& name)
929 RefPtr<ActionGroup> g = ActionGroup::create (name);
931 /* this is one of the places where our own Action management code
932 has to touch the GTK one, because we want the GtkUIManager to
933 be able to create widgets (particularly Menus) from our actions.
935 This is a a necessary step for that to happen.
939 ActionManager::ui_manager->insert_action_group (g);
946 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
950 RefPtr<Action> act = Action::create (name, label);
952 fullpath = group->get_name();
956 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
961 /* already registered */
962 return RefPtr<Action> ();
966 ActionMap::register_action (RefPtr<ActionGroup> group,
967 const char* name, const char* label, sigc::slot<void> sl)
971 RefPtr<Action> act = Action::create (name, label);
973 fullpath = group->get_name();
977 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
978 group->add (act, sl);
982 /* already registered */
983 return RefPtr<Action>();
987 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
988 Gtk::RadioAction::Group& rgroup,
989 const char* name, const char* label,
994 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
995 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
997 fullpath = group->get_name();
1001 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1002 group->add (act, sl);
1006 /* already registered */
1007 return RefPtr<Action>();
1011 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1012 Gtk::RadioAction::Group& rgroup,
1013 const char* name, const char* label,
1014 sigc::slot<void,GtkAction*> sl,
1019 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1020 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1021 ract->property_value() = value;
1023 fullpath = group->get_name();
1027 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1028 group->add (act, sigc::bind (sl, act->gobj()));
1032 /* already registered */
1034 return RefPtr<Action>();
1038 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1039 const char* name, const char* label, sigc::slot<void> sl)
1043 fullpath = group->get_name();
1047 RefPtr<Action> act = ToggleAction::create (name, label);
1049 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1050 group->add (act, sl);
1054 /* already registered */
1055 return RefPtr<Action>();
1058 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1059 char const *gdk_name = gdk_keyval_name (k.key());
1060 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << k.state();