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 ActionMap Gtkmm2ext::Actions; /* global. Gulp */
46 list<Bindings*> Bindings::bindings; /* global. Gulp */
47 uint32_t Bindings::_ignored_state = 0;
49 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
51 uint32_t ignore = Bindings::ignored_state();
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 = Bindings::ignored_state();
142 _val = (state & ~ignore);
149 KeyboardKey::name () const
155 if (s & Keyboard::PrimaryModifier) {
158 if (s & Keyboard::SecondaryModifier) {
164 if (s & Keyboard::TertiaryModifier) {
170 if (s & Keyboard::Level4Modifier) {
181 char const *gdk_name = gdk_keyval_name (key());
194 KeyboardKey::make_key (const string& str, KeyboardKey& k)
198 if (str.find ("Primary") != string::npos) {
199 s |= Keyboard::PrimaryModifier;
202 if (str.find ("Secondary") != string::npos) {
203 s |= Keyboard::SecondaryModifier;
206 if (str.find ("Tertiary") != string::npos) {
207 s |= Keyboard::TertiaryModifier;
210 if (str.find ("Level4") != string::npos) {
211 s |= Keyboard::Level4Modifier;
214 string::size_type lastmod = str.find_last_of ('-');
217 if (lastmod == string::npos) {
218 keyval = gdk_keyval_from_name (str.c_str());
220 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
223 if (keyval == GDK_VoidSymbol || keyval == 0) {
227 k = KeyboardKey (s, keyval);
232 Bindings::Bindings (std::string const& name)
234 , _action_map (Actions)
236 bindings.push_back (this);
239 Bindings::~Bindings()
241 bindings.remove (this);
245 Bindings::set_action_map (ActionMap& actions)
247 _action_map = actions;
253 Bindings::empty_keys() const
255 return press_bindings.empty() && release_bindings.empty();
259 Bindings::empty_mouse () const
261 return button_press_bindings.empty() && button_release_bindings.empty();
265 Bindings::empty() const
267 return empty_keys() && empty_mouse ();
271 Bindings::activate (KeyboardKey kb, Operation op)
273 KeybindingMap* kbm = 0;
277 kbm = &press_bindings;
280 kbm = &release_bindings;
284 KeybindingMap::iterator k = kbm->find (kb);
286 if (k == kbm->end()) {
287 /* no entry for this key in the state map */
288 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", kb));
292 RefPtr<Action> action;
294 if (!k->second.action) {
295 action = _action_map.find_action (k->second.action_name);
300 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", kb, k->second.action_name));
304 /* return true even if the action could not be found */
310 Bindings::associate ()
312 KeybindingMap::iterator k;
314 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
315 k->second.action = _action_map.find_action (k->second.action_name);
316 if (k->second.action) {
317 push_to_gtk (k->first, k->second.action);
321 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
322 k->second.action = _action_map.find_action (k->second.action_name);
323 /* no working support in GTK for release bindings */
326 MouseButtonBindingMap::iterator b;
328 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
329 b->second.action = _action_map.find_action (b->second.action_name);
332 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
333 b->second.action = _action_map.find_action (b->second.action_name);
338 Bindings::dissociate ()
340 KeybindingMap::iterator k;
342 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
343 k->second.action.clear ();
345 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
346 k->second.action.clear ();
351 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
353 /* GTK has the useful feature of showing key bindings for actions in
354 * menus. As of August 2015, we have no interest in trying to
355 * reimplement this functionality, so we will use it even though we no
356 * longer use GTK accelerators for handling key events. To do this, we
357 * need to make sure that there is a fully populated GTK AccelMap set
358 * up with all bindings/actions.
361 Gtk::AccelKey gtk_key;
363 /* tweak the modifier used in the binding so that GTK will accept it
364 * and display something acceptable. The actual keyval should display
365 * correctly even if it involves a key that GTK would not allow
369 uint32_t gtk_legal_keyval = kb.key();
370 possibly_translate_keyval_to_make_legal_accelerator (gtk_legal_keyval);
371 KeyboardKey gtk_binding (kb.state(), gtk_legal_keyval);
374 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
376 if (!entry_exists || gtk_key.get_key() == 0) {
378 /* there is a trick happening here. It turns out that
379 * gtk_accel_map_add_entry() performs no validation checks on
380 * the accelerator keyval. This means we can use it to define
381 * ANY accelerator, even if they violate GTK's rules
382 * (e.g. about not using navigation keys). This works ONLY when
383 * the entry in the GTK accelerator map has not already been
384 * added. The entries will be added by the GTK UIManager when
385 * building menus, so this code must be called before that
389 Gtk::AccelMap::add_entry (what->get_accel_path(),
391 (Gdk::ModifierType) gtk_binding.state());
393 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;
396 if (!Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key) || gtk_key.get_key() == 0) {
397 cerr << "GTK binding using " << gtk_binding << " failed for " << what->get_accel_path() << " existing = " << gtk_key.get_key() << " + " << gtk_key.get_mod() << endl;
402 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
404 /* We have to search the existing binding map by both action and
405 * keybinding, because the following are possible:
407 * - key is already used for a different action
408 * - action has a different binding
410 * - action is not bound
413 RefPtr<Action> action = Actions.find_action (action_name);
419 KeybindingMap* kbm = 0;
423 kbm = &press_bindings;
426 kbm = &release_bindings;
430 KeybindingMap::iterator k = kbm->find (kb);
432 if (k != kbm->end()) {
436 /* now linear search by action */
438 for (k = kbm->begin(); k != kbm->end(); ++k) {
439 if (k->second.action_name == action_name) {
445 add (kb, op, action_name, can_save);
447 /* for now, this never fails */
453 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
455 KeybindingMap* kbm = 0;
459 kbm = &press_bindings;
462 kbm = &release_bindings;
466 KeybindingMap::iterator k = kbm->find (kb);
468 if (k != kbm->end()) {
471 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
473 kbm->insert (new_pair).first;
476 Keyboard::keybindings_changed ();
481 Bindings::remove (KeyboardKey kb, Operation op, bool can_save)
483 KeybindingMap* kbm = 0;
487 kbm = &press_bindings;
490 kbm = &release_bindings;
494 KeybindingMap::iterator k = kbm->find (kb);
496 if (k != kbm->end()) {
501 Keyboard::keybindings_changed ();
506 Bindings::remove (RefPtr<Action> action, Operation op, bool can_save)
508 KeybindingMap* kbm = 0;
512 kbm = &press_bindings;
515 kbm = &release_bindings;
519 for (KeybindingMap::iterator k = kbm->begin(); k != kbm->end(); ++k) {
520 if (k->second.action == action) {
527 Keyboard::keybindings_changed ();
532 Bindings::activate (MouseButton bb, Operation op)
534 MouseButtonBindingMap* bbm = 0;
538 bbm = &button_press_bindings;
541 bbm = &button_release_bindings;
545 MouseButtonBindingMap::iterator b = bbm->find (bb);
547 if (b == bbm->end()) {
548 /* no entry for this key in the state map */
552 RefPtr<Action> action;
554 if (!b->second.action) {
555 action = _action_map.find_action (b->second.action_name);
563 /* return true even if the action could not be found */
569 Bindings::add (MouseButton bb, Operation op, string const& action_name)
571 MouseButtonBindingMap* bbm = 0;
575 bbm = &button_press_bindings;
578 bbm = &button_release_bindings;
582 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
583 bbm->insert (newpair);
587 Bindings::remove (MouseButton bb, Operation op)
589 MouseButtonBindingMap* bbm = 0;
593 bbm = &button_press_bindings;
596 bbm = &button_release_bindings;
600 MouseButtonBindingMap::iterator b = bbm->find (bb);
602 if (b != bbm->end()) {
608 Bindings::save (XMLNode& root)
610 XMLNode* presses = new XMLNode (X_("Press"));
612 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
615 if (k->first.name().empty()) {
619 child = new XMLNode (X_("Binding"));
620 child->add_property (X_("key"), k->first.name());
621 child->add_property (X_("action"), k->second.action_name);
622 presses->add_child_nocopy (*child);
625 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
627 child = new XMLNode (X_("Binding"));
628 child->add_property (X_("button"), k->first.name());
629 child->add_property (X_("action"), k->second.action_name);
630 presses->add_child_nocopy (*child);
633 XMLNode* releases = new XMLNode (X_("Release"));
635 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
638 if (k->first.name().empty()) {
642 child = new XMLNode (X_("Binding"));
643 child->add_property (X_("key"), k->first.name());
644 child->add_property (X_("action"), k->second.action_name);
645 releases->add_child_nocopy (*child);
648 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
650 child = new XMLNode (X_("Binding"));
651 child->add_property (X_("button"), k->first.name());
652 child->add_property (X_("action"), k->second.action_name);
653 releases->add_child_nocopy (*child);
656 root.add_child_nocopy (*presses);
657 root.add_child_nocopy (*releases);
661 Bindings::load (XMLNode const& node)
663 const XMLNodeList& children (node.children());
665 press_bindings.clear ();
666 release_bindings.clear ();
668 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
669 /* each node could be Press or Release */
670 load_operation (**i);
677 Bindings::load_operation (XMLNode const& node)
679 if (node.name() == X_("Press") || node.name() == X_("Release")) {
683 if (node.name() == X_("Press")) {
689 const XMLNodeList& children (node.children());
691 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
697 ap = (*p)->property ("action");
698 kp = (*p)->property ("key");
699 bp = (*p)->property ("button");
701 if (!ap || (!kp && !bp)) {
707 if (!KeyboardKey::make_key (kp->value(), k)) {
710 add (k, op, ap->value());
713 if (!MouseButton::make_button (bp->value(), b)) {
716 add (b, op, ap->value());
723 Bindings::get_all_actions (std::vector<std::string>& paths,
724 std::vector<std::string>& labels,
725 std::vector<std::string>& tooltips,
726 std::vector<std::string>& keys,
727 std::vector<RefPtr<Action> >& actions)
729 /* build a reverse map from actions to bindings */
731 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
734 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
735 rmap.insert (make_pair (k->second.action, k->first));
738 /* get a list of all actions */
740 ActionMap::Actions all_actions;
741 _action_map.get_actions (all_actions);
743 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
745 paths.push_back ((*act)->get_accel_path());
746 labels.push_back ((*act)->get_label());
747 tooltips.push_back ((*act)->get_tooltip());
749 ReverseMap::iterator r = rmap.find (*act);
751 if (r != rmap.end()) {
752 keys.push_back (gtk_accelerator_get_label (r->second.key(), (GdkModifierType) r->second.state()));
754 keys.push_back (string());
757 actions.push_back (*act);
762 Bindings::get_all_actions (std::vector<std::string>& names,
763 std::vector<std::string>& paths,
764 std::vector<std::string>& keys)
766 /* build a reverse map from actions to bindings */
768 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
771 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
772 rmap.insert (make_pair (k->second.action, k->first));
775 /* get a list of all actions */
777 ActionMap::Actions all_actions;
778 _action_map.get_actions (all_actions);
780 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
782 names.push_back ((*act)->get_name());
783 paths.push_back ((*act)->get_accel_path());
785 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());
795 Bindings::get_bindings (string const& name)
797 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
798 if ((*b)->name() == name) {
807 Bindings::associate_all ()
809 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
814 /*==========================================ACTION MAP =========================================*/
817 ActionMap::get_actions (ActionMap::Actions& acts)
819 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
820 acts.push_back (a->second);
825 ActionMap::find_action (const string& name)
827 _ActionMap::iterator a = _actions.find (name);
829 if (a != _actions.end()) {
833 return RefPtr<Action>();
837 ActionMap::create_action_group (const string& name)
839 RefPtr<ActionGroup> g = ActionGroup::create (name);
841 /* this is one of the places where our own Action management code
842 has to touch the GTK one, because we want the GtkUIManager to
843 be able to create widgets (particularly Menus) from our actions.
845 This is a a necessary step for that to happen.
849 ActionManager::ui_manager->insert_action_group (g);
856 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
860 RefPtr<Action> act = Action::create (name, label);
862 fullpath = group->get_name();
866 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
871 /* already registered */
872 return RefPtr<Action> ();
876 ActionMap::register_action (RefPtr<ActionGroup> group,
877 const char* name, const char* label, sigc::slot<void> sl)
881 RefPtr<Action> act = Action::create (name, label);
883 fullpath = group->get_name();
887 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
888 group->add (act, sl);
892 /* already registered */
893 return RefPtr<Action>();
897 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
898 Gtk::RadioAction::Group& rgroup,
899 const char* name, const char* label,
904 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
905 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
907 fullpath = group->get_name();
911 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
912 group->add (act, sl);
916 /* already registered */
917 return RefPtr<Action>();
921 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
922 Gtk::RadioAction::Group& rgroup,
923 const char* name, const char* label,
924 sigc::slot<void,GtkAction*> sl,
929 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
930 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
931 ract->property_value() = value;
933 fullpath = group->get_name();
937 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
938 group->add (act, sigc::bind (sl, act->gobj()));
942 /* already registered */
944 return RefPtr<Action>();
948 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
949 const char* name, const char* label, sigc::slot<void> sl)
953 fullpath = group->get_name();
957 RefPtr<Action> act = ToggleAction::create (name, label);
959 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
960 group->add (act, sl);
964 /* already registered */
965 return RefPtr<Action>();
968 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
969 char const *gdk_name = gdk_keyval_name (k.key());
970 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << k.state();