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 uint32_t Bindings::_ignored_state = 0;
46 map<string,Bindings*> Bindings::bindings_for_state;
48 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
50 uint32_t ignore = Bindings::ignored_state();
52 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
53 /* key is not subject to case, so ignore SHIFT
55 ignore |= GDK_SHIFT_MASK;
58 _val = (state & ~ignore);
64 MouseButton::make_button (const string& str, MouseButton& b)
68 if (str.find ("Primary") != string::npos) {
69 s |= Keyboard::PrimaryModifier;
72 if (str.find ("Secondary") != string::npos) {
73 s |= Keyboard::SecondaryModifier;
76 if (str.find ("Tertiary") != string::npos) {
77 s |= Keyboard::TertiaryModifier;
80 if (str.find ("Level4") != string::npos) {
81 s |= Keyboard::Level4Modifier;
84 string::size_type lastmod = str.find_last_of ('-');
85 uint32_t button_number;
87 if (lastmod == string::npos) {
88 button_number = PBD::atoi (str);
90 button_number = PBD::atoi (str.substr (lastmod+1));
93 b = MouseButton (s, button_number);
98 MouseButton::name () const
104 if (s & Keyboard::PrimaryModifier) {
107 if (s & Keyboard::SecondaryModifier) {
113 if (s & Keyboard::TertiaryModifier) {
119 if (s & Keyboard::Level4Modifier) {
131 snprintf (buf, sizeof (buf), "%u", button());
137 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
139 uint32_t ignore = Bindings::ignored_state();
141 _val = (state & ~ignore);
148 KeyboardKey::name () const
154 if (s & Keyboard::PrimaryModifier) {
157 if (s & Keyboard::SecondaryModifier) {
163 if (s & Keyboard::TertiaryModifier) {
169 if (s & Keyboard::Level4Modifier) {
180 char const *gdk_name = gdk_keyval_name (key());
193 KeyboardKey::make_key (const string& str, KeyboardKey& k)
197 if (str.find ("Primary") != string::npos) {
198 s |= Keyboard::PrimaryModifier;
201 if (str.find ("Secondary") != string::npos) {
202 s |= Keyboard::SecondaryModifier;
205 if (str.find ("Tertiary") != string::npos) {
206 s |= Keyboard::TertiaryModifier;
209 if (str.find ("Level4") != string::npos) {
210 s |= Keyboard::Level4Modifier;
213 string::size_type lastmod = str.find_last_of ('-');
216 if (lastmod == string::npos) {
217 keyval = gdk_keyval_from_name (str.c_str());
219 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
222 if (keyval == GDK_VoidSymbol || keyval == 0) {
226 k = KeyboardKey (s, keyval);
231 Bindings::Bindings ()
236 Bindings::~Bindings()
238 if (!_name.empty()) {
239 remove_bindings_for_state (_name, *this);
244 Bindings::empty_keys() const
246 return press_bindings.empty() && release_bindings.empty();
250 Bindings::empty_mouse () const
252 return button_press_bindings.empty() && button_release_bindings.empty();
256 Bindings::empty() const
258 return empty_keys() && empty_mouse ();
262 Bindings::set_action_map (ActionMap& am)
265 press_bindings.clear ();
266 release_bindings.clear ();
270 Bindings::activate (KeyboardKey kb, Operation op)
272 KeybindingMap* kbm = 0;
276 kbm = &press_bindings;
279 kbm = &release_bindings;
283 KeybindingMap::iterator k = kbm->find (kb);
285 if (k == kbm->end()) {
286 /* no entry for this key in the state map */
287 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", kb));
293 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", kb, k->second->get_name()));
295 k->second->activate ();
300 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
306 /* We have to search the existing binding map by both action and
307 * keybinding, because the following are possible:
309 * - key is already used for a different action
310 * - action has a different binding
312 * - action is not bound
315 RefPtr<Action> action = action_map->find_action (action_name);
321 KeybindingMap* kbm = 0;
325 kbm = &press_bindings;
328 kbm = &release_bindings;
332 KeybindingMap::iterator k = kbm->find (kb);
334 if (k != kbm->end()) {
338 /* now linear search by action */
340 for (k = kbm->begin(); k != kbm->end(); ++k) {
341 if (k->second == action) {
347 add (kb, op, action, can_save);
349 /* for now, this never fails */
355 Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what, bool can_save)
357 KeybindingMap* kbm = 0;
361 kbm = &press_bindings;
364 kbm = &release_bindings;
368 KeybindingMap::iterator k = kbm->find (kb);
370 if (k == kbm->end()) {
371 pair<KeyboardKey,RefPtr<Action> > newpair (kb, what);
372 kbm->insert (newpair);
377 /* GTK has the useful feature of showing key bindings for actions in
378 * menus. As of August 2015, we have no interest in trying to
379 * reimplement this functionality, so we will use it even though we no
380 * longer use GTK accelerators for handling key events. To do this, we
381 * need to make sure that there is a fully populated GTK AccelMap set
382 * up with all bindings/actions.
385 Gtk::AccelKey gtk_key;
387 /* tweak the modifier used in the binding so that GTK will accept it
388 * and display something acceptable. The actual keyval should display
389 * correctly even if it involves a key that GTK would not allow
393 uint32_t gtk_legal_keyval = kb.key();
394 possibly_translate_keyval_to_make_legal_accelerator (gtk_legal_keyval);
395 KeyboardKey gtk_binding (kb.state(), gtk_legal_keyval);
398 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
400 if (!entry_exists || gtk_key.get_key() == 0) {
402 /* there is a trick happening here. It turns out that
403 * gtk_accel_map_add_entry() performs no validation checks on
404 * the accelerator keyval. This means we can use it to define
405 * ANY accelerator, even if they violate GTK's rules
406 * (e.g. about not using navigation keys). This works ONLY when
407 * the entry in the GTK accelerator map has not already been
408 * added. The entries will be added by the GTK UIManager when
409 * building menus, so this code must be called before that
413 Gtk::AccelMap::add_entry (what->get_accel_path(),
415 (Gdk::ModifierType) gtk_binding.state());
417 warning << string_compose (_("There is more than one key binding defined for %1. Both will work, but only the first will be visible in menus"), what->get_accel_path()) << endmsg;
420 if (!Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key) || gtk_key.get_key() == 0) {
421 cerr << "GTK binding using " << gtk_binding << " failed for " << what->get_accel_path() << " existing = " << gtk_key.get_key() << " + " << gtk_key.get_mod() << endl;
425 Keyboard::keybindings_changed ();
430 Bindings::remove (KeyboardKey kb, Operation op, bool can_save)
432 KeybindingMap* kbm = 0;
436 kbm = &press_bindings;
439 kbm = &release_bindings;
443 KeybindingMap::iterator k = kbm->find (kb);
445 if (k != kbm->end()) {
450 Keyboard::keybindings_changed ();
455 Bindings::remove (RefPtr<Action> action, Operation op, bool can_save)
457 KeybindingMap* kbm = 0;
461 kbm = &press_bindings;
464 kbm = &release_bindings;
468 for (KeybindingMap::iterator k = kbm->begin(); k != kbm->end(); ++k) {
469 if (k->second == action) {
476 Keyboard::keybindings_changed ();
481 Bindings::activate (MouseButton bb, Operation op)
483 MouseButtonBindingMap* bbm = 0;
487 bbm = &button_press_bindings;
490 bbm = &button_release_bindings;
494 MouseButtonBindingMap::iterator b = bbm->find (bb);
496 if (b == bbm->end()) {
497 /* no entry for this key in the state map */
503 b->second->activate ();
508 Bindings::add (MouseButton bb, Operation op, RefPtr<Action> what)
510 MouseButtonBindingMap* bbm = 0;
514 bbm = &button_press_bindings;
517 bbm = &button_release_bindings;
521 MouseButtonBindingMap::iterator b = bbm->find (bb);
523 if (b == bbm->end()) {
524 pair<MouseButton,RefPtr<Action> > newpair (bb, what);
525 bbm->insert (newpair);
526 // cerr << "Bindings added mouse button " << bb.button() << " w/ " << bb.state() << " => " << what->get_name() << endl;
533 Bindings::remove (MouseButton bb, Operation op)
535 MouseButtonBindingMap* bbm = 0;
539 bbm = &button_press_bindings;
542 bbm = &button_release_bindings;
546 MouseButtonBindingMap::iterator b = bbm->find (bb);
548 if (b != bbm->end()) {
554 Bindings::save (const string& path)
557 XMLNode* root = new XMLNode (X_("Bindings"));
558 tree.set_root (root);
562 if (!tree.write (path)) {
563 ::g_unlink (path.c_str());
571 Bindings::save (XMLNode& root)
573 XMLNode* presses = new XMLNode (X_("Press"));
575 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
578 if (k->first.name().empty()) {
582 child = new XMLNode (X_("Binding"));
583 child->add_property (X_("key"), k->first.name());
584 string ap = k->second->get_accel_path();
585 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
586 presses->add_child_nocopy (*child);
589 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
591 child = new XMLNode (X_("Binding"));
592 child->add_property (X_("button"), k->first.name());
593 string ap = k->second->get_accel_path();
594 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
595 presses->add_child_nocopy (*child);
598 XMLNode* releases = new XMLNode (X_("Release"));
600 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
603 if (k->first.name().empty()) {
607 child = new XMLNode (X_("Binding"));
608 child->add_property (X_("key"), k->first.name());
609 string ap = k->second->get_accel_path();
610 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
611 releases->add_child_nocopy (*child);
614 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
616 child = new XMLNode (X_("Binding"));
617 child->add_property (X_("button"), k->first.name());
618 string ap = k->second->get_accel_path();
619 child->add_property (X_("action"), ap.substr (ap.find ('/') + 1));
620 releases->add_child_nocopy (*child);
623 root.add_child_nocopy (*presses);
624 root.add_child_nocopy (*releases);
628 Bindings::load (string const & name)
636 XMLNode const * node = Keyboard::bindings_node();
639 error << string_compose (_("No keyboard binding information when loading bindings for \"%1\""), name) << endmsg;
643 if (!_name.empty()) {
644 remove_bindings_for_state (_name, *this);
647 const XMLNodeList& children (node->children());
650 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
652 if ((*i)->name() == X_("Bindings")) {
653 XMLProperty const * prop = (*i)->property (X_("name"));
659 if (prop->value() == name) {
668 error << string_compose (_("Bindings for \"%1\" not found in keyboard binding node\n"), name) << endmsg;
672 press_bindings.clear ();
673 release_bindings.clear ();
675 const XMLNodeList& bindings (node->children());
677 for (XMLNodeList::const_iterator i = bindings.begin(); i != bindings.end(); ++i) {
678 /* each node could be Press or Release */
683 add_bindings_for_state (_name, *this);
689 Bindings::load (const XMLNode& node)
691 if (node.name() == X_("Press") || node.name() == X_("Release")) {
695 if (node.name() == X_("Press")) {
701 const XMLNodeList& children (node.children());
703 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
709 ap = (*p)->property ("action");
710 kp = (*p)->property ("key");
711 bp = (*p)->property ("button");
713 if (!ap || (!kp && !bp)) {
720 act = action_map->find_action (ap->value());
724 string::size_type slash = ap->value().find ('/');
725 if (slash != string::npos) {
726 string group = ap->value().substr (0, slash);
727 string action = ap->value().substr (slash+1);
728 act = ActionManager::get_action (group.c_str(), action.c_str());
738 if (!KeyboardKey::make_key (kp->value(), k)) {
744 if (!MouseButton::make_button (bp->value(), b)) {
754 Bindings::get_all_actions (std::vector<std::string>& paths,
755 std::vector<std::string>& labels,
756 std::vector<std::string>& tooltips,
757 std::vector<std::string>& keys,
758 std::vector<RefPtr<Action> >& actions)
764 /* build a reverse map from actions to bindings */
766 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
769 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
770 rmap.insert (make_pair (k->second, k->first));
773 /* get a list of all actions */
775 ActionMap::Actions all_actions;
776 action_map->get_actions (all_actions);
778 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
780 paths.push_back ((*act)->get_accel_path());
781 labels.push_back ((*act)->get_label());
782 tooltips.push_back ((*act)->get_tooltip());
784 ReverseMap::iterator r = rmap.find (*act);
786 if (r != rmap.end()) {
787 keys.push_back (gtk_accelerator_get_label (r->second.key(), (GdkModifierType) r->second.state()));
789 keys.push_back (string());
792 actions.push_back (*act);
797 Bindings::get_all_actions (std::vector<std::string>& names,
798 std::vector<std::string>& paths,
799 std::vector<std::string>& keys)
801 /* build a reverse map from actions to bindings */
803 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
806 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
807 rmap.insert (make_pair (k->second, k->first));
810 /* get a list of all actions */
812 ActionMap::Actions actions;
813 action_map->get_actions (actions);
815 for (ActionMap::Actions::const_iterator act = actions.begin(); act != actions.end(); ++act) {
816 names.push_back ((*act)->get_name());
817 paths.push_back ((*act)->get_accel_path());
819 ReverseMap::iterator r = rmap.find (*act);
820 if (r != rmap.end()) {
821 keys.push_back (gtk_accelerator_get_label (r->second.key(), (GdkModifierType) r->second.state()));
823 keys.push_back (string());
829 ActionMap::get_actions (ActionMap::Actions& acts)
831 for (_ActionMap::iterator a = actions.begin(); a != actions.end(); ++a) {
832 acts.push_back (a->second);
837 ActionMap::find_action (const string& name)
839 _ActionMap::iterator a = actions.find (name);
841 if (a != actions.end()) {
845 return RefPtr<Action>();
849 ActionMap::create_action_group (const string& name)
851 RefPtr<ActionGroup> g = ActionGroup::create (name);
856 ActionMap::install_action_group (RefPtr<ActionGroup> group)
858 ActionManager::ui_manager->insert_action_group (group);
862 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
866 RefPtr<Action> act = Action::create (name, label);
868 fullpath = group->get_name();
872 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
877 /* already registered */
878 return RefPtr<Action> ();
882 ActionMap::register_action (RefPtr<ActionGroup> group,
883 const char* name, const char* label, sigc::slot<void> sl)
887 RefPtr<Action> act = Action::create (name, label);
889 fullpath = group->get_name();
893 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
894 group->add (act, sl);
898 /* already registered */
899 return RefPtr<Action>();
903 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
904 Gtk::RadioAction::Group& rgroup,
905 const char* name, const char* label,
910 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
911 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
913 fullpath = group->get_name();
917 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
918 group->add (act, sl);
922 /* already registered */
923 return RefPtr<Action>();
927 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
928 Gtk::RadioAction::Group& rgroup,
929 const char* name, const char* label,
930 sigc::slot<void,GtkAction*> sl,
935 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
936 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
937 ract->property_value() = value;
939 fullpath = group->get_name();
943 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
944 group->add (act, sigc::bind (sl, act->gobj()));
948 /* already registered */
950 return RefPtr<Action>();
954 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
955 const char* name, const char* label, sigc::slot<void> sl)
959 fullpath = group->get_name();
963 RefPtr<Action> act = ToggleAction::create (name, label);
965 if (actions.insert (_ActionMap::value_type (fullpath, act)).second) {
966 group->add (act, sl);
970 /* already registered */
971 return RefPtr<Action>();
975 Bindings::add_bindings_for_state (std::string const& name, Bindings& bindings)
977 bindings_for_state.insert (make_pair (name, &bindings));
981 Bindings::remove_bindings_for_state (std::string const& name, Bindings& bindings)
983 bindings_for_state.erase (name);
986 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
987 char const *gdk_name = gdk_keyval_name (k.key());
988 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << k.state();