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 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
54 /* key is not subject to case, so ignore SHIFT
56 ignore |= GDK_SHIFT_MASK;
59 _val = (state & ~ignore);
65 MouseButton::make_button (const string& str, MouseButton& b)
69 if (str.find ("Primary") != string::npos) {
70 s |= Keyboard::PrimaryModifier;
73 if (str.find ("Secondary") != string::npos) {
74 s |= Keyboard::SecondaryModifier;
77 if (str.find ("Tertiary") != string::npos) {
78 s |= Keyboard::TertiaryModifier;
81 if (str.find ("Level4") != string::npos) {
82 s |= Keyboard::Level4Modifier;
85 string::size_type lastmod = str.find_last_of ('-');
86 uint32_t button_number;
88 if (lastmod == string::npos) {
89 button_number = PBD::atoi (str);
91 button_number = PBD::atoi (str.substr (lastmod+1));
94 b = MouseButton (s, button_number);
99 MouseButton::name () const
105 if (s & Keyboard::PrimaryModifier) {
108 if (s & Keyboard::SecondaryModifier) {
114 if (s & Keyboard::TertiaryModifier) {
120 if (s & Keyboard::Level4Modifier) {
132 snprintf (buf, sizeof (buf), "%u", button());
138 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
140 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
142 _val = (state & ~ignore);
148 KeyboardKey::display_label () const
154 /* This magically returns a string that will display the right thing
155 * on all platforms, notably the command key on OS X.
158 return gtk_accelerator_get_label (key(), (GdkModifierType) state());
162 KeyboardKey::name () const
168 if (s & Keyboard::PrimaryModifier) {
171 if (s & Keyboard::SecondaryModifier) {
177 if (s & Keyboard::TertiaryModifier) {
183 if (s & Keyboard::Level4Modifier) {
194 char const *gdk_name = gdk_keyval_name (key());
207 KeyboardKey::make_key (const string& str, KeyboardKey& k)
211 if (str.find ("Primary") != string::npos) {
212 s |= Keyboard::PrimaryModifier;
215 if (str.find ("Secondary") != string::npos) {
216 s |= Keyboard::SecondaryModifier;
219 if (str.find ("Tertiary") != string::npos) {
220 s |= Keyboard::TertiaryModifier;
223 if (str.find ("Level4") != string::npos) {
224 s |= Keyboard::Level4Modifier;
227 string::size_type lastmod = str.find_last_of ('-');
230 if (lastmod == string::npos) {
231 keyval = gdk_keyval_from_name (str.c_str());
233 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
236 if (keyval == GDK_VoidSymbol || keyval == 0) {
240 k = KeyboardKey (s, keyval);
245 Bindings::Bindings (std::string const& name)
249 bindings.push_back (this);
252 Bindings::~Bindings()
254 bindings.remove (this);
258 Bindings::ardour_action_name (RefPtr<Action> action)
260 /* Skip "<Actions>/" */
261 return action->get_accel_path ().substr (10);
265 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
267 const string action_name = ardour_action_name (action);
269 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
271 /* option one: action has already been associated with the
275 if (k->second.action == action) {
279 /* option two: action name matches, so lookup the action,
280 * setup the association while we're here, and return the binding.
283 if (_action_map && k->second.action_name == action_name) {
284 k->second.action = _action_map->find_action (action_name);
290 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
292 /* option one: action has already been associated with the
296 if (k->second.action == action) {
300 /* option two: action name matches, so lookup the action,
301 * setup the association while we're here, and return the binding.
304 if (_action_map && k->second.action_name == action_name) {
305 k->second.action = _action_map->find_action (action_name);
311 return KeyboardKey::null_key();
315 Bindings::set_action_map (ActionMap& actions)
318 _action_map->set_bindings (0);
321 _action_map = &actions;
322 _action_map->set_bindings (this);
329 Bindings::empty_keys() const
331 return press_bindings.empty() && release_bindings.empty();
335 Bindings::empty_mouse () const
337 return button_press_bindings.empty() && button_release_bindings.empty();
341 Bindings::empty() const
343 return empty_keys() && empty_mouse ();
347 Bindings::activate (KeyboardKey kb, Operation op)
349 KeybindingMap* kbm = 0;
353 kbm = &press_bindings;
356 kbm = &release_bindings;
360 KeybindingMap::iterator k = kbm->find (kb);
362 if (k == kbm->end()) {
363 /* no entry for this key in the state map */
364 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", kb));
368 RefPtr<Action> action;
370 if (k->second.action) {
371 action = k->second.action;
374 action = _action_map->find_action (k->second.action_name);
380 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", kb, k->second.action_name));
384 /* return true even if the action could not be found */
390 Bindings::associate ()
392 KeybindingMap::iterator k;
398 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
399 k->second.action = _action_map->find_action (k->second.action_name);
400 if (k->second.action) {
401 push_to_gtk (k->first, k->second.action);
403 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
407 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
408 k->second.action = _action_map->find_action (k->second.action_name);
409 /* no working support in GTK for release bindings */
412 MouseButtonBindingMap::iterator b;
414 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
415 b->second.action = _action_map->find_action (b->second.action_name);
418 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
419 b->second.action = _action_map->find_action (b->second.action_name);
424 Bindings::dissociate ()
426 KeybindingMap::iterator k;
428 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
429 k->second.action.clear ();
431 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
432 k->second.action.clear ();
437 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
439 /* GTK has the useful feature of showing key bindings for actions in
440 * menus. As of August 2015, we have no interest in trying to
441 * reimplement this functionality, so we will use it even though we no
442 * longer use GTK accelerators for handling key events. To do this, we
443 * need to make sure that there is a fully populated GTK AccelMap set
444 * up with all bindings/actions.
447 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(), kb.key(), (Gdk::ModifierType) kb.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_bindings (string const& name, ActionMap& map)
849 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
850 if ((*b)->name() == name) {
851 (*b)->set_action_map (map);
860 Bindings::associate_all ()
862 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
867 /*==========================================ACTION MAP =========================================*/
869 ActionMap::ActionMap (string const & name)
873 action_maps.push_back (this);
876 ActionMap::~ActionMap ()
878 action_maps.remove (this);
882 ActionMap::set_bindings (Bindings* b)
888 ActionMap::get_actions (ActionMap::Actions& acts)
890 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
891 acts.push_back (a->second);
896 ActionMap::find_action (const string& name)
898 _ActionMap::iterator a = _actions.find (name);
900 if (a != _actions.end()) {
904 return RefPtr<Action>();
908 ActionMap::create_action_group (const string& name)
910 RefPtr<ActionGroup> g = ActionGroup::create (name);
912 /* this is one of the places where our own Action management code
913 has to touch the GTK one, because we want the GtkUIManager to
914 be able to create widgets (particularly Menus) from our actions.
916 This is a a necessary step for that to happen.
920 ActionManager::ui_manager->insert_action_group (g);
927 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
931 RefPtr<Action> act = Action::create (name, label);
933 fullpath = group->get_name();
937 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
942 /* already registered */
943 return RefPtr<Action> ();
947 ActionMap::register_action (RefPtr<ActionGroup> group,
948 const char* name, const char* label, sigc::slot<void> sl)
952 RefPtr<Action> act = Action::create (name, label);
954 fullpath = group->get_name();
958 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
959 group->add (act, sl);
963 /* already registered */
964 return RefPtr<Action>();
968 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
969 Gtk::RadioAction::Group& rgroup,
970 const char* name, const char* label,
975 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
976 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
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,
995 sigc::slot<void,GtkAction*> sl,
1000 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1001 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1002 ract->property_value() = value;
1004 fullpath = group->get_name();
1008 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1009 group->add (act, sigc::bind (sl, act->gobj()));
1013 /* already registered */
1015 return RefPtr<Action>();
1019 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1020 const char* name, const char* label, sigc::slot<void> sl)
1024 fullpath = group->get_name();
1028 RefPtr<Action> act = ToggleAction::create (name, label);
1030 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1031 group->add (act, sl);
1035 /* already registered */
1036 return RefPtr<Action>();
1040 ActionMap::get_all_actions (std::vector<std::string>& paths,
1041 std::vector<std::string>& labels,
1042 std::vector<std::string>& tooltips,
1043 std::vector<std::string>& keys,
1044 std::vector<RefPtr<Action> >& actions)
1046 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1048 ActionMap::Actions these_actions;
1049 (*map)->get_actions (these_actions);
1051 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1053 paths.push_back ((*act)->get_accel_path());
1054 labels.push_back ((*act)->get_label());
1055 tooltips.push_back ((*act)->get_tooltip());
1056 actions.push_back (*act);
1058 Bindings* bindings = (*map)->bindings();
1063 Bindings::Operation op;
1065 key = bindings->get_binding_for_action (*act, op);
1067 if (key == KeyboardKey::null_key()) {
1068 keys.push_back (string());
1070 keys.push_back (key.display_label());
1073 keys.push_back (string());
1077 these_actions.clear ();
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 " << hex << k.state() << dec;