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_all_actions (std::vector<std::string>& names,
853 std::vector<std::string>& paths,
854 std::vector<std::string>& keys)
860 /* build a reverse map from actions to bindings */
862 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
865 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
866 rmap.insert (make_pair (k->second.action, k->first));
869 /* get a list of all actions */
871 ActionMap::Actions all_actions;
872 _action_map->get_actions (all_actions);
874 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
876 names.push_back ((*act)->get_name());
877 paths.push_back ((*act)->get_accel_path());
879 ReverseMap::iterator r = rmap.find (*act);
880 if (r != rmap.end()) {
881 keys.push_back (r->second.display_label());
883 keys.push_back (string());
889 Bindings::get_bindings (string const& name, ActionMap& map)
891 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
892 if ((*b)->name() == name) {
893 (*b)->set_action_map (map);
902 Bindings::associate_all ()
904 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
909 /*==========================================ACTION MAP =========================================*/
911 ActionMap::ActionMap (string const & name)
915 action_maps.push_back (this);
918 ActionMap::~ActionMap ()
920 action_maps.remove (this);
924 ActionMap::set_bindings (Bindings* b)
930 ActionMap::get_actions (ActionMap::Actions& acts)
932 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
933 acts.push_back (a->second);
938 ActionMap::find_action (const string& name)
940 _ActionMap::iterator a = _actions.find (name);
942 if (a != _actions.end()) {
946 return RefPtr<Action>();
950 ActionMap::create_action_group (const string& name)
952 RefPtr<ActionGroup> g = ActionGroup::create (name);
954 /* this is one of the places where our own Action management code
955 has to touch the GTK one, because we want the GtkUIManager to
956 be able to create widgets (particularly Menus) from our actions.
958 This is a a necessary step for that to happen.
962 ActionManager::ui_manager->insert_action_group (g);
969 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
973 RefPtr<Action> act = Action::create (name, label);
975 fullpath = group->get_name();
979 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
984 /* already registered */
985 return RefPtr<Action> ();
989 ActionMap::register_action (RefPtr<ActionGroup> group,
990 const char* name, const char* label, sigc::slot<void> sl)
994 RefPtr<Action> act = Action::create (name, label);
996 fullpath = group->get_name();
1000 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1001 group->add (act, sl);
1005 /* already registered */
1006 return RefPtr<Action>();
1010 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1011 Gtk::RadioAction::Group& rgroup,
1012 const char* name, const char* label,
1013 sigc::slot<void> sl)
1017 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1018 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1020 fullpath = group->get_name();
1024 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1025 group->add (act, sl);
1029 /* already registered */
1030 return RefPtr<Action>();
1034 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1035 Gtk::RadioAction::Group& rgroup,
1036 const char* name, const char* label,
1037 sigc::slot<void,GtkAction*> sl,
1042 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1043 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1044 ract->property_value() = value;
1046 fullpath = group->get_name();
1050 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1051 group->add (act, sigc::bind (sl, act->gobj()));
1055 /* already registered */
1057 return RefPtr<Action>();
1061 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1062 const char* name, const char* label, sigc::slot<void> sl)
1066 fullpath = group->get_name();
1070 RefPtr<Action> act = ToggleAction::create (name, label);
1072 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1073 group->add (act, sl);
1077 /* already registered */
1078 return RefPtr<Action>();
1081 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1082 char const *gdk_name = gdk_keyval_name (k.key());
1083 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << k.state();