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/replace_all.h"
30 #include "pbd/xml++.h"
32 #include "gtkmm2ext/actions.h"
33 #include "gtkmm2ext/bindings.h"
34 #include "gtkmm2ext/debug.h"
35 #include "gtkmm2ext/keyboard.h"
36 #include "gtkmm2ext/utils.h"
43 using namespace Gtkmm2ext;
46 list<Bindings*> Bindings::bindings; /* global. Gulp */
47 list<ActionMap*> ActionMap::action_maps; /* global. Gulp */
48 PBD::Signal1<void,Bindings*> Bindings::BindingsChanged;
50 template <typename IteratorValueType>
51 struct ActionNameRegistered
53 ActionNameRegistered(std::string const& name)
57 bool operator()(IteratorValueType elem) const {
58 return elem.second.action_name == action_name;
60 std::string const& action_name;
63 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
65 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
67 /* this is a slightly wierd test that relies on
68 * gdk_keyval_is_{upper,lower}() returning true for keys that have no
69 * case-sensitivity. This covers mostly non-alphanumeric keys.
72 if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
73 /* key is not subject to case, so ignore SHIFT
75 ignore |= GDK_SHIFT_MASK;
78 _val = (state & ~ignore);
84 MouseButton::make_button (const string& str, MouseButton& b)
88 if (str.find ("Primary") != string::npos) {
89 s |= Keyboard::PrimaryModifier;
92 if (str.find ("Secondary") != string::npos) {
93 s |= Keyboard::SecondaryModifier;
96 if (str.find ("Tertiary") != string::npos) {
97 s |= Keyboard::TertiaryModifier;
100 if (str.find ("Level4") != string::npos) {
101 s |= Keyboard::Level4Modifier;
104 string::size_type lastmod = str.find_last_of ('-');
105 uint32_t button_number;
107 if (lastmod == string::npos) {
108 button_number = PBD::atoi (str);
110 button_number = PBD::atoi (str.substr (lastmod+1));
113 b = MouseButton (s, button_number);
118 MouseButton::name () const
124 if (s & Keyboard::PrimaryModifier) {
127 if (s & Keyboard::SecondaryModifier) {
133 if (s & Keyboard::TertiaryModifier) {
139 if (s & Keyboard::Level4Modifier) {
151 snprintf (buf, sizeof (buf), "%u", button());
157 /*================================ KeyboardKey ================================*/
158 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
160 uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
162 _val = (state & ~ignore);
168 KeyboardKey::display_label () const
174 /* This magically returns a string that will display the right thing
175 * on all platforms, notably the command key on OS X.
178 uint32_t mod = state();
181 /* We use both bits (MOD2|META) for Primary on OS X,
182 * but we don't want MOD2 showing up in listings. So remove
183 * it and add back META.
186 if (mod & GDK_MOD2_MASK) {
187 mod = (mod & ~GDK_MOD2_MASK) | GDK_META_MASK;
192 return gtk_accelerator_get_label (key(), (GdkModifierType) mod);
196 KeyboardKey::name () const
202 if (s & Keyboard::PrimaryModifier) {
205 if (s & Keyboard::SecondaryModifier) {
211 if (s & Keyboard::TertiaryModifier) {
217 if (s & Keyboard::Level4Modifier) {
228 char const *gdk_name = gdk_keyval_name (key());
241 KeyboardKey::native_name () const
247 if (s & Keyboard::PrimaryModifier) {
248 str += Keyboard::primary_modifier_name ();
250 if (s & Keyboard::SecondaryModifier) {
254 str += Keyboard::secondary_modifier_name ();
256 if (s & Keyboard::TertiaryModifier) {
260 str += Keyboard::tertiary_modifier_name ();
262 if (s & Keyboard::Level4Modifier) {
266 str += Keyboard::level4_modifier_name ();
273 char const *gdk_name = gdk_keyval_name (key());
286 KeyboardKey::native_short_name () const
292 if (s & Keyboard::PrimaryModifier) {
293 str += Keyboard::primary_modifier_short_name ();
295 if (s & Keyboard::SecondaryModifier) {
299 str += Keyboard::secondary_modifier_short_name ();
301 if (s & Keyboard::TertiaryModifier) {
305 str += Keyboard::tertiary_modifier_short_name ();
307 if (s & Keyboard::Level4Modifier) {
311 str += Keyboard::level4_modifier_short_name ();
318 char const *gdk_name = gdk_keyval_name (key());
331 KeyboardKey::make_key (const string& str, KeyboardKey& k)
335 if (str.find ("Primary") != string::npos) {
336 s |= Keyboard::PrimaryModifier;
339 if (str.find ("Secondary") != string::npos) {
340 s |= Keyboard::SecondaryModifier;
343 if (str.find ("Tertiary") != string::npos) {
344 s |= Keyboard::TertiaryModifier;
347 if (str.find ("Level4") != string::npos) {
348 s |= Keyboard::Level4Modifier;
351 /* since all SINGLE key events keycodes are changed to lower case
352 * before looking them up, make sure we only store lower case here. The
353 * Shift part will be stored in the modifier part of the KeyboardKey.
355 * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
360 string::size_type lastmod = str.find_last_of ('-');
362 if (lastmod != string::npos) {
363 actual = str.substr (lastmod+1);
369 if (actual.size() == 1) {
370 actual = PBD::downcase (actual);
374 keyval = gdk_keyval_from_name (actual.c_str());
376 if (keyval == GDK_VoidSymbol || keyval == 0) {
380 k = KeyboardKey (s, keyval);
385 /*================================= Bindings =================================*/
386 Bindings::Bindings (std::string const& name)
390 bindings.push_back (this);
393 Bindings::~Bindings()
395 bindings.remove (this);
399 Bindings::ardour_action_name (RefPtr<Action> action)
401 /* Skip "<Actions>/" */
402 return action->get_accel_path ().substr (10);
406 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
408 const string action_name = ardour_action_name (action);
410 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
412 /* option one: action has already been associated with the
416 if (k->second.action == action) {
420 /* option two: action name matches, so lookup the action,
421 * setup the association while we're here, and return the binding.
424 if (_action_map && k->second.action_name == action_name) {
425 k->second.action = _action_map->find_action (action_name);
431 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
433 /* option one: action has already been associated with the
437 if (k->second.action == action) {
441 /* option two: action name matches, so lookup the action,
442 * setup the association while we're here, and return the binding.
445 if (_action_map && k->second.action_name == action_name) {
446 k->second.action = _action_map->find_action (action_name);
452 return KeyboardKey::null_key();
456 Bindings::set_action_map (ActionMap& actions)
459 _action_map->set_bindings (0);
462 _action_map = &actions;
463 _action_map->set_bindings (this);
470 Bindings::empty_keys() const
472 return press_bindings.empty() && release_bindings.empty();
476 Bindings::empty_mouse () const
478 return button_press_bindings.empty() && button_release_bindings.empty();
482 Bindings::empty() const
484 return empty_keys() && empty_mouse ();
488 Bindings::activate (KeyboardKey kb, Operation op)
490 KeybindingMap& kbm = get_keymap (op);
492 /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
493 Our bindings all use the lower case character/keyname, so switch
494 to the lower case before doing the lookup.
497 KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
499 KeybindingMap::iterator k = kbm.find (unshifted);
501 if (k == kbm.end()) {
502 /* no entry for this key in the state map */
503 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
507 RefPtr<Action> action;
509 if (k->second.action) {
510 action = k->second.action;
513 action = _action_map->find_action (k->second.action_name);
519 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
523 /* return true even if the action could not be found */
529 Bindings::associate ()
531 KeybindingMap::iterator k;
537 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
538 k->second.action = _action_map->find_action (k->second.action_name);
539 if (k->second.action) {
540 push_to_gtk (k->first, k->second.action);
542 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
546 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
547 k->second.action = _action_map->find_action (k->second.action_name);
548 /* no working support in GTK for release bindings */
551 MouseButtonBindingMap::iterator b;
553 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
554 b->second.action = _action_map->find_action (b->second.action_name);
557 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
558 b->second.action = _action_map->find_action (b->second.action_name);
563 Bindings::dissociate ()
565 KeybindingMap::iterator k;
567 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
568 k->second.action.clear ();
570 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
571 k->second.action.clear ();
576 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
578 /* GTK has the useful feature of showing key bindings for actions in
579 * menus. As of August 2015, we have no interest in trying to
580 * reimplement this functionality, so we will use it even though we no
581 * longer use GTK accelerators for handling key events. To do this, we
582 * need to make sure that there is a fully populated GTK AccelMap set
583 * up with all bindings/actions.
586 Gtk::AccelKey gtk_key;
587 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
591 /* there is a trick happening here. It turns out that
592 * gtk_accel_map_add_entry() performs no validation checks on
593 * the accelerator keyval. This means we can use it to define
594 * ANY accelerator, even if they violate GTK's rules
595 * (e.g. about not using navigation keys). This works ONLY when
596 * the entry in the GTK accelerator map has not already been
597 * added. The entries will be added by the GTK UIManager when
598 * building menus, so this code must be called before that
603 int mod = kb.state();
605 /* See comments in Keyboard::Keyboard about GTK handling of MOD2, META and the Command key.
607 * If we do not do this, GTK+ won't show the correct text for shortcuts in menus.
610 if (mod & GDK_MOD2_MASK) {
611 mod = mod | GDK_META_MASK;
615 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) mod);
620 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
626 if (is_registered(op, action_name)) {
627 remove (op, action_name, can_save);
630 /* XXX need a way to get the old group name */
631 add (kb, op, action_name, 0, can_save);
637 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, XMLProperty const* group, bool can_save)
639 if (is_registered (op, action_name)) {
643 KeybindingMap& kbm = get_keymap (op);
645 KeybindingMap::value_type new_pair = make_pair (kb, ActionInfo (action_name, group->value()));
646 (void) kbm.insert (new_pair).first;
648 KeybindingMap::value_type new_pair = make_pair (kb, ActionInfo (action_name));
649 (void) kbm.insert (new_pair).first;
652 DEBUG_TRACE (DEBUG::Bindings, string_compose ("add binding between %1 and %2, group [%3]\n",
653 kb, action_name, (group ? group->value() : string())));
656 Keyboard::keybindings_changed ();
659 BindingsChanged (this); /* EMIT SIGNAL */
664 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
666 bool erased_action = false;
667 KeybindingMap& kbm = get_keymap (op);
668 for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
669 if (k->second.action_name == action_name) {
671 erased_action = true;
676 if (!erased_action) {
677 return erased_action;
681 Keyboard::keybindings_changed ();
684 BindingsChanged (this); /* EMIT SIGNAL */
685 return erased_action;
690 Bindings::activate (MouseButton bb, Operation op)
692 MouseButtonBindingMap& bbm = get_mousemap(op);
694 MouseButtonBindingMap::iterator b = bbm.find (bb);
696 if (b == bbm.end()) {
697 /* no entry for this key in the state map */
701 RefPtr<Action> action;
703 if (b->second.action) {
704 action = b->second.action;
707 action = _action_map->find_action (b->second.action_name);
713 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
717 /* return true even if the action could not be found */
723 Bindings::add (MouseButton bb, Operation op, string const& action_name, XMLProperty const* /*group*/)
725 MouseButtonBindingMap& bbm = get_mousemap(op);
727 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
728 bbm.insert (newpair);
732 Bindings::remove (MouseButton bb, Operation op)
734 MouseButtonBindingMap& bbm = get_mousemap(op);
735 MouseButtonBindingMap::iterator b = bbm.find (bb);
737 if (b != bbm.end()) {
743 Bindings::save (XMLNode& root)
745 XMLNode* presses = new XMLNode (X_("Press"));
747 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
750 if (k->first.name().empty()) {
754 child = new XMLNode (X_("Binding"));
755 child->add_property (X_("key"), k->first.name());
756 child->add_property (X_("action"), k->second.action_name);
757 presses->add_child_nocopy (*child);
760 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
762 child = new XMLNode (X_("Binding"));
763 child->add_property (X_("button"), k->first.name());
764 child->add_property (X_("action"), k->second.action_name);
765 presses->add_child_nocopy (*child);
768 XMLNode* releases = new XMLNode (X_("Release"));
770 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
773 if (k->first.name().empty()) {
777 child = new XMLNode (X_("Binding"));
778 child->add_property (X_("key"), k->first.name());
779 child->add_property (X_("action"), k->second.action_name);
780 releases->add_child_nocopy (*child);
783 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
785 child = new XMLNode (X_("Binding"));
786 child->add_property (X_("button"), k->first.name());
787 child->add_property (X_("action"), k->second.action_name);
788 releases->add_child_nocopy (*child);
791 root.add_child_nocopy (*presses);
792 root.add_child_nocopy (*releases);
796 Bindings::save_all_bindings_as_html (ostream& ostr)
798 if (bindings.empty()) {
803 ostr << "<html>\n<head>\n<title>";
804 ostr << PROGRAM_NAME;
805 ostr << "</title>\n";
810 .key-name-even, .key-name-odd\n\
812 font-weight: bold;\n\
815 .key-action-odd, .key-action-even\n\
817 font-weight: normal;\n\
818 font-style: italic;\n\
820 ostr << "</style>\n";
822 ostr << "</head>\n<body>\n";
824 ostr << "<div class=\"container\">\n";
826 for (list<Bindings*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
827 (*b)->save_as_html (ostr);
836 Bindings::save_as_html (ostream& ostr) const
839 if (!press_bindings.empty()) {
841 ostr << "<div class=\"binding-set\">\n";
846 /* first pass: separate by group */
848 typedef std::map<std::string, std::vector<KeybindingMap::const_iterator> > GroupMap;
851 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
852 if (k->first.name().empty()) {
857 if (!k->second.group_name.empty()) {
858 group_name = k->second.group_name;
860 group_name = X_("nogroup");
863 GroupMap::iterator gm = group_map.find (group_name);
864 if (gm == group_map.end()) {
865 std::vector<KeybindingMap::const_iterator> li;
867 group_map.insert (make_pair (group_name,li));
869 gm->second.push_back (k);
873 for (GroupMap::const_iterator gm = group_map.begin(); gm != group_map.end(); ++gm) {
875 ostr << "<div class=\"group\">\n";
876 ostr << "<div class=\"group-name\">" << gm->first << "</div>\n";
878 for (vector<KeybindingMap::const_iterator>::const_iterator k = gm->second.begin(); k != gm->second.end(); ++k) {
880 if ((*k)->first.name().empty()) {
884 RefPtr<Action> action;
886 if ((*k)->second.action) {
887 action = (*k)->second.action;
890 action = _action_map->find_action ((*k)->second.action_name);
898 string key_name = (*k)->first.native_short_name ();
899 replace_all (key_name, X_("KP_"), X_("Numpad "));
901 string::size_type pos;
903 char const *targets[] = { X_("Separator"), X_("Add"), X_("Subtract"), X_("Decimal"), X_("Divide"),
904 X_("grave"), X_("comma"), X_("period"), X_("asterisk"), X_("backslash"),
905 X_("apostrophe"), X_("minus"), X_("plus"), X_("slash"), X_("semicolon"),
906 X_("colon"), X_("equal"), X_("bracketleft"), X_("bracketright"),
907 X_("ampersand"), X_("numbersign"), X_("parenleft"), X_("parenright"),
908 X_("quoteright"), X_("quoteleft"), X_("exclam"), X_("quotedbl"),
912 char const *replacements[] = { X_("-"), X_("+"), X_("-"), X_("."), X_("/"),
913 X_("`"), X_(","), X_("."), X_("*"), X_("\\"),
914 X_("'"), X_("-"), X_("+"), X_("/"), X_(";"),
915 X_(":"), X_("="), X_("{"), X_("{"),
916 X_("&"), X_("#"), X_("("), X_(")"),
917 X_("`"), X_("'"), X_("!"), X_("\""),
920 for (size_t n = 0; targets[n]; ++n) {
921 if ((pos = key_name.find (targets[n])) != string::npos) {
922 key_name.replace (pos, strlen (targets[n]), replacements[n]);
926 ostr << "<div class=\"key\">" << key_name << "</div>";
927 ostr << "<div class=\"action\">" << action->get_label() << "</div>\n";
929 ostr << "</div>\n\n";
937 Bindings::load (XMLNode const& node)
939 const XMLNodeList& children (node.children());
941 press_bindings.clear ();
942 release_bindings.clear ();
944 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
945 /* each node could be Press or Release */
946 load_operation (**i);
953 Bindings::load_operation (XMLNode const& node)
955 if (node.name() == X_("Press") || node.name() == X_("Release")) {
959 if (node.name() == X_("Press")) {
965 const XMLNodeList& children (node.children());
967 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
969 XMLProperty const * ap;
970 XMLProperty const * kp;
971 XMLProperty const * bp;
972 XMLProperty const * gp;
973 XMLNode const * child = *p;
975 ap = child->property ("action");
976 kp = child->property ("key");
977 bp = child->property ("button");
978 gp = child->property ("group");
980 if (!ap || (!kp && !bp)) {
986 if (!KeyboardKey::make_key (kp->value(), k)) {
989 add (k, op, ap->value(), gp);
992 if (!MouseButton::make_button (bp->value(), b)) {
995 add (b, op, ap->value(), gp);
1002 Bindings::get_all_actions (std::vector<std::string>& paths,
1003 std::vector<std::string>& labels,
1004 std::vector<std::string>& tooltips,
1005 std::vector<std::string>& keys,
1006 std::vector<RefPtr<Action> >& actions)
1012 /* build a reverse map from actions to bindings */
1014 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
1017 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
1018 rmap.insert (make_pair (k->second.action, k->first));
1021 /* get a list of all actions */
1023 ActionMap::Actions all_actions;
1024 _action_map->get_actions (all_actions);
1026 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
1028 paths.push_back ((*act)->get_accel_path());
1029 labels.push_back ((*act)->get_label());
1030 tooltips.push_back ((*act)->get_tooltip());
1032 ReverseMap::iterator r = rmap.find (*act);
1034 if (r != rmap.end()) {
1035 keys.push_back (r->second.display_label());
1037 keys.push_back (string());
1040 actions.push_back (*act);
1045 Bindings::get_bindings (string const& name, ActionMap& map)
1047 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
1048 if ((*b)->name() == name) {
1049 (*b)->set_action_map (map);
1058 Bindings::associate_all ()
1060 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
1066 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
1068 const KeybindingMap& km = get_keymap(op);
1069 return km.find(kb) != km.end();
1073 Bindings::is_registered (Operation op, std::string const& action_name) const
1075 const KeybindingMap& km = get_keymap(op);
1076 return std::find_if(km.begin(), km.end(), ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
1079 Bindings::KeybindingMap&
1080 Bindings::get_keymap (Operation op)
1084 return press_bindings;
1087 return release_bindings;
1091 const Bindings::KeybindingMap&
1092 Bindings::get_keymap (Operation op) const
1096 return press_bindings;
1099 return release_bindings;
1103 Bindings::MouseButtonBindingMap&
1104 Bindings::get_mousemap (Operation op)
1108 return button_press_bindings;
1111 return button_release_bindings;
1115 /*==========================================ACTION MAP =========================================*/
1117 ActionMap::ActionMap (string const & name)
1121 action_maps.push_back (this);
1124 ActionMap::~ActionMap ()
1126 action_maps.remove (this);
1130 ActionMap::set_bindings (Bindings* b)
1136 ActionMap::get_actions (ActionMap::Actions& acts)
1138 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
1139 acts.push_back (a->second);
1144 ActionMap::find_action (const string& name)
1146 _ActionMap::iterator a = _actions.find (name);
1148 if (a != _actions.end()) {
1152 return RefPtr<Action>();
1156 ActionMap::create_action_group (const string& name)
1158 RefPtr<ActionGroup> g = ActionGroup::create (name);
1160 /* this is one of the places where our own Action management code
1161 has to touch the GTK one, because we want the GtkUIManager to
1162 be able to create widgets (particularly Menus) from our actions.
1164 This is a a necessary step for that to happen.
1168 ActionManager::ui_manager->insert_action_group (g);
1175 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
1179 RefPtr<Action> act = Action::create (name, label);
1181 fullpath = group->get_name();
1185 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1190 /* already registered */
1191 return RefPtr<Action> ();
1195 ActionMap::register_action (RefPtr<ActionGroup> group,
1196 const char* name, const char* label, sigc::slot<void> sl)
1200 RefPtr<Action> act = Action::create (name, label);
1202 fullpath = group->get_name();
1206 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1207 group->add (act, sl);
1211 /* already registered */
1212 return RefPtr<Action>();
1216 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1217 Gtk::RadioAction::Group& rgroup,
1218 const char* name, const char* label,
1219 sigc::slot<void> sl)
1223 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1224 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1226 fullpath = group->get_name();
1230 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1231 group->add (act, sl);
1235 /* already registered */
1236 return RefPtr<Action>();
1240 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1241 Gtk::RadioAction::Group& rgroup,
1242 const char* name, const char* label,
1243 sigc::slot<void,GtkAction*> sl,
1248 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1249 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1250 ract->property_value() = value;
1252 fullpath = group->get_name();
1256 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1257 group->add (act, sigc::bind (sl, act->gobj()));
1261 /* already registered */
1263 return RefPtr<Action>();
1267 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1268 const char* name, const char* label, sigc::slot<void> sl)
1272 fullpath = group->get_name();
1276 RefPtr<Action> act = ToggleAction::create (name, label);
1278 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1279 group->add (act, sl);
1283 /* already registered */
1284 return RefPtr<Action>();
1288 ActionMap::get_all_actions (std::vector<std::string>& paths,
1289 std::vector<std::string>& labels,
1290 std::vector<std::string>& tooltips,
1291 std::vector<std::string>& keys,
1292 std::vector<RefPtr<Action> >& actions)
1294 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1296 ActionMap::Actions these_actions;
1297 (*map)->get_actions (these_actions);
1299 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1301 paths.push_back ((*act)->get_accel_path());
1302 labels.push_back ((*act)->get_label());
1303 tooltips.push_back ((*act)->get_tooltip());
1304 actions.push_back (*act);
1306 Bindings* bindings = (*map)->bindings();
1311 Bindings::Operation op;
1313 key = bindings->get_binding_for_action (*act, op);
1315 if (key == KeyboardKey::null_key()) {
1316 keys.push_back (string());
1318 keys.push_back (key.display_label());
1321 keys.push_back (string());
1325 these_actions.clear ();
1329 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1330 char const *gdk_name = gdk_keyval_name (k.key());
1331 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state "
1332 << hex << k.state() << dec << ' ' << show_gdk_event_state (k.state());