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.
185 if (mod & GDK_MOD2_MASK) {
186 mod &= ~GDK_MOD2_MASK;
190 return gtk_accelerator_get_label (key(), (GdkModifierType) mod);
194 KeyboardKey::name () const
200 if (s & Keyboard::PrimaryModifier) {
203 if (s & Keyboard::SecondaryModifier) {
209 if (s & Keyboard::TertiaryModifier) {
215 if (s & Keyboard::Level4Modifier) {
226 char const *gdk_name = gdk_keyval_name (key());
239 KeyboardKey::native_name () const
245 if (s & Keyboard::PrimaryModifier) {
246 str += Keyboard::primary_modifier_name ();
248 if (s & Keyboard::SecondaryModifier) {
252 str += Keyboard::secondary_modifier_name ();
254 if (s & Keyboard::TertiaryModifier) {
258 str += Keyboard::tertiary_modifier_name ();
260 if (s & Keyboard::Level4Modifier) {
264 str += Keyboard::level4_modifier_name ();
271 char const *gdk_name = gdk_keyval_name (key());
284 KeyboardKey::native_short_name () const
290 if (s & Keyboard::PrimaryModifier) {
291 str += Keyboard::primary_modifier_short_name ();
293 if (s & Keyboard::SecondaryModifier) {
297 str += Keyboard::secondary_modifier_short_name ();
299 if (s & Keyboard::TertiaryModifier) {
303 str += Keyboard::tertiary_modifier_short_name ();
305 if (s & Keyboard::Level4Modifier) {
309 str += Keyboard::level4_modifier_short_name ();
316 char const *gdk_name = gdk_keyval_name (key());
329 KeyboardKey::make_key (const string& str, KeyboardKey& k)
333 if (str.find ("Primary") != string::npos) {
334 s |= Keyboard::PrimaryModifier;
337 if (str.find ("Secondary") != string::npos) {
338 s |= Keyboard::SecondaryModifier;
341 if (str.find ("Tertiary") != string::npos) {
342 s |= Keyboard::TertiaryModifier;
345 if (str.find ("Level4") != string::npos) {
346 s |= Keyboard::Level4Modifier;
349 /* since all SINGLE key events keycodes are changed to lower case
350 * before looking them up, make sure we only store lower case here. The
351 * Shift part will be stored in the modifier part of the KeyboardKey.
353 * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
358 string::size_type lastmod = str.find_last_of ('-');
360 if (lastmod != string::npos) {
361 actual = str.substr (lastmod+1);
367 if (actual.size() == 1) {
368 actual = PBD::downcase (actual);
372 keyval = gdk_keyval_from_name (actual.c_str());
374 if (keyval == GDK_VoidSymbol || keyval == 0) {
378 k = KeyboardKey (s, keyval);
383 /*================================= Bindings =================================*/
384 Bindings::Bindings (std::string const& name)
388 bindings.push_back (this);
391 Bindings::~Bindings()
393 bindings.remove (this);
397 Bindings::ardour_action_name (RefPtr<Action> action)
399 /* Skip "<Actions>/" */
400 return action->get_accel_path ().substr (10);
404 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
406 const string action_name = ardour_action_name (action);
408 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
410 /* option one: action has already been associated with the
414 if (k->second.action == action) {
418 /* option two: action name matches, so lookup the action,
419 * setup the association while we're here, and return the binding.
422 if (_action_map && k->second.action_name == action_name) {
423 k->second.action = _action_map->find_action (action_name);
429 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
431 /* option one: action has already been associated with the
435 if (k->second.action == action) {
439 /* option two: action name matches, so lookup the action,
440 * setup the association while we're here, and return the binding.
443 if (_action_map && k->second.action_name == action_name) {
444 k->second.action = _action_map->find_action (action_name);
450 return KeyboardKey::null_key();
454 Bindings::set_action_map (ActionMap& actions)
457 _action_map->set_bindings (0);
460 _action_map = &actions;
461 _action_map->set_bindings (this);
468 Bindings::empty_keys() const
470 return press_bindings.empty() && release_bindings.empty();
474 Bindings::empty_mouse () const
476 return button_press_bindings.empty() && button_release_bindings.empty();
480 Bindings::empty() const
482 return empty_keys() && empty_mouse ();
486 Bindings::activate (KeyboardKey kb, Operation op)
488 KeybindingMap& kbm = get_keymap (op);
490 /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
491 Our bindings all use the lower case character/keyname, so switch
492 to the lower case before doing the lookup.
495 KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
497 KeybindingMap::iterator k = kbm.find (unshifted);
499 if (k == kbm.end()) {
500 /* no entry for this key in the state map */
501 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
505 RefPtr<Action> action;
507 if (k->second.action) {
508 action = k->second.action;
511 action = _action_map->find_action (k->second.action_name);
517 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
521 /* return true even if the action could not be found */
527 Bindings::associate ()
529 KeybindingMap::iterator k;
535 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
536 k->second.action = _action_map->find_action (k->second.action_name);
537 if (k->second.action) {
538 push_to_gtk (k->first, k->second.action);
540 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
544 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
545 k->second.action = _action_map->find_action (k->second.action_name);
546 /* no working support in GTK for release bindings */
549 MouseButtonBindingMap::iterator b;
551 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
552 b->second.action = _action_map->find_action (b->second.action_name);
555 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
556 b->second.action = _action_map->find_action (b->second.action_name);
561 Bindings::dissociate ()
563 KeybindingMap::iterator k;
565 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
566 k->second.action.clear ();
568 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
569 k->second.action.clear ();
574 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
576 /* GTK has the useful feature of showing key bindings for actions in
577 * menus. As of August 2015, we have no interest in trying to
578 * reimplement this functionality, so we will use it even though we no
579 * longer use GTK accelerators for handling key events. To do this, we
580 * need to make sure that there is a fully populated GTK AccelMap set
581 * up with all bindings/actions.
584 Gtk::AccelKey gtk_key;
585 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
589 /* there is a trick happening here. It turns out that
590 * gtk_accel_map_add_entry() performs no validation checks on
591 * the accelerator keyval. This means we can use it to define
592 * ANY accelerator, even if they violate GTK's rules
593 * (e.g. about not using navigation keys). This works ONLY when
594 * the entry in the GTK accelerator map has not already been
595 * added. The entries will be added by the GTK UIManager when
596 * building menus, so this code must be called before that
600 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) kb.state());
605 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
611 if (is_registered(op, action_name)) {
612 remove(op, action_name, can_save);
614 add (kb, op, action_name, can_save);
619 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
621 if (is_registered (op, action_name)) {
625 KeybindingMap& kbm = get_keymap (op);
627 KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
628 kbm.insert (new_pair).first;
631 Keyboard::keybindings_changed ();
634 BindingsChanged (this); /* EMIT SIGNAL */
639 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
641 bool erased_action = false;
642 KeybindingMap& kbm = get_keymap (op);
643 for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
644 if (k->second.action_name == action_name) {
646 erased_action = true;
651 if (!erased_action) {
652 return erased_action;
656 Keyboard::keybindings_changed ();
659 BindingsChanged (this); /* EMIT SIGNAL */
660 return erased_action;
665 Bindings::activate (MouseButton bb, Operation op)
667 MouseButtonBindingMap& bbm = get_mousemap(op);
669 MouseButtonBindingMap::iterator b = bbm.find (bb);
671 if (b == bbm.end()) {
672 /* no entry for this key in the state map */
676 RefPtr<Action> action;
678 if (b->second.action) {
679 action = b->second.action;
682 action = _action_map->find_action (b->second.action_name);
688 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
692 /* return true even if the action could not be found */
698 Bindings::add (MouseButton bb, Operation op, string const& action_name)
700 MouseButtonBindingMap& bbm = get_mousemap(op);
702 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
703 bbm.insert (newpair);
707 Bindings::remove (MouseButton bb, Operation op)
709 MouseButtonBindingMap& bbm = get_mousemap(op);
710 MouseButtonBindingMap::iterator b = bbm.find (bb);
712 if (b != bbm.end()) {
718 Bindings::save (XMLNode& root)
720 XMLNode* presses = new XMLNode (X_("Press"));
722 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
725 if (k->first.name().empty()) {
729 child = new XMLNode (X_("Binding"));
730 child->add_property (X_("key"), k->first.name());
731 child->add_property (X_("action"), k->second.action_name);
732 presses->add_child_nocopy (*child);
735 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
737 child = new XMLNode (X_("Binding"));
738 child->add_property (X_("button"), k->first.name());
739 child->add_property (X_("action"), k->second.action_name);
740 presses->add_child_nocopy (*child);
743 XMLNode* releases = new XMLNode (X_("Release"));
745 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
748 if (k->first.name().empty()) {
752 child = new XMLNode (X_("Binding"));
753 child->add_property (X_("key"), k->first.name());
754 child->add_property (X_("action"), k->second.action_name);
755 releases->add_child_nocopy (*child);
758 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
760 child = new XMLNode (X_("Binding"));
761 child->add_property (X_("button"), k->first.name());
762 child->add_property (X_("action"), k->second.action_name);
763 releases->add_child_nocopy (*child);
766 root.add_child_nocopy (*presses);
767 root.add_child_nocopy (*releases);
771 Bindings::save_all_bindings_as_html (ostream& ostr)
773 if (bindings.empty()) {
778 ostr << "<html>\n<head>\n<title>";
779 ostr << PROGRAM_NAME;
780 ostr << "</title>\n";
785 .key-name-even, .key-name-odd\n\
787 font-weight: bold;\n\
790 .key-action-odd, .key-action-even\n\
792 font-weight: normal;\n\
793 font-style: italic;\n\
795 ostr << "</style>\n";
797 ostr << "</head>\n<body>\n";
799 ostr << "<div class=\"container\">\n";
801 for (list<Bindings*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
802 (*b)->save_as_html (ostr);
811 Bindings::save_as_html (ostream& ostr) const
814 if (!press_bindings.empty()) {
816 ostr << "<div><h1 class=\"binding-set-name\">\n";
818 ostr << "</h1><table><tr><th>Shortcut</th><th>Operation</th></tr>\n";
822 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
823 if (k->first.name().empty()) {
827 RefPtr<Action> action;
829 if (k->second.action) {
830 action = k->second.action;
833 action = _action_map->find_action (k->second.action_name);
841 string key_name = k->first.native_short_name ();
842 replace_all (key_name, X_("KP_"), X_("Numpad "));
844 string::size_type pos;
846 char const *targets[] = { X_("Separator"), X_("Add"), X_("Subtract"), X_("Decimal"), X_("Divide"),
847 X_("grave"), X_("comma"), X_("period"), X_("asterisk"), X_("backslash"),
848 X_("apostrophe"), X_("minus"), X_("plus"), X_("slash"), X_("semicolon"),
849 X_("colon"), X_("equal"), X_("bracketleft"), X_("bracketright"),
850 X_("ampersand"), X_("numbersign"), X_("parenleft"), X_("parenright"),
851 X_("quoteright"), X_("quoteleft"), X_("exclam"), X_("quotedbl"),
855 char const *replacements[] = { X_("-"), X_("+"), X_("-"), X_("."), X_("/"),
856 X_("`"), X_(","), X_("."), X_("*"), X_("\\"),
857 X_("'"), X_("-"), X_("+"), X_("/"), X_(";"),
858 X_(":"), X_("="), X_("{"), X_("{"),
859 X_("&"), X_("#"), X_("("), X_(")"),
860 X_("`"), X_("'"), X_("!"), X_("\""),
863 for (size_t n = 0; targets[n]; ++n) {
864 if ((pos = key_name.find (targets[n])) != string::npos) {
865 key_name.replace (pos, strlen (targets[n]), replacements[n]);
870 ostr << "<tr><td class=\"key-name-odd\">";
872 ostr << "<tr><td class=\"key-name-even\">";
876 ostr << "</td><td class=\"key-action-odd\">";
878 ostr << "</td><td class=\"key-action-even\">";
880 ostr << action->get_label();
881 ostr << "</td></tr>\n";
885 ostr << "</table></div>\n";
890 Bindings::load (XMLNode const& node)
892 const XMLNodeList& children (node.children());
894 press_bindings.clear ();
895 release_bindings.clear ();
897 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
898 /* each node could be Press or Release */
899 load_operation (**i);
906 Bindings::load_operation (XMLNode const& node)
908 if (node.name() == X_("Press") || node.name() == X_("Release")) {
912 if (node.name() == X_("Press")) {
918 const XMLNodeList& children (node.children());
920 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
922 XMLProperty const * ap;
923 XMLProperty const * kp;
924 XMLProperty const * bp;
925 XMLNode const * child = *p;
927 ap = child->property ("action");
928 kp = child->property ("key");
929 bp = child->property ("button");
931 if (!ap || (!kp && !bp)) {
937 if (!KeyboardKey::make_key (kp->value(), k)) {
940 add (k, op, ap->value());
943 if (!MouseButton::make_button (bp->value(), b)) {
946 add (b, op, ap->value());
953 Bindings::get_all_actions (std::vector<std::string>& paths,
954 std::vector<std::string>& labels,
955 std::vector<std::string>& tooltips,
956 std::vector<std::string>& keys,
957 std::vector<RefPtr<Action> >& actions)
963 /* build a reverse map from actions to bindings */
965 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
968 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
969 rmap.insert (make_pair (k->second.action, k->first));
972 /* get a list of all actions */
974 ActionMap::Actions all_actions;
975 _action_map->get_actions (all_actions);
977 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
979 paths.push_back ((*act)->get_accel_path());
980 labels.push_back ((*act)->get_label());
981 tooltips.push_back ((*act)->get_tooltip());
983 ReverseMap::iterator r = rmap.find (*act);
985 if (r != rmap.end()) {
986 keys.push_back (r->second.display_label());
988 keys.push_back (string());
991 actions.push_back (*act);
996 Bindings::get_bindings (string const& name, ActionMap& map)
998 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
999 if ((*b)->name() == name) {
1000 (*b)->set_action_map (map);
1009 Bindings::associate_all ()
1011 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
1017 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
1019 const KeybindingMap& km = get_keymap(op);
1020 return km.find(kb) != km.end();
1024 Bindings::is_registered (Operation op, std::string const& action_name) const
1026 const KeybindingMap& km = get_keymap(op);
1027 return std::find_if(km.begin(), km.end(), ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
1030 Bindings::KeybindingMap&
1031 Bindings::get_keymap (Operation op)
1035 return press_bindings;
1038 return release_bindings;
1042 const Bindings::KeybindingMap&
1043 Bindings::get_keymap (Operation op) const
1047 return press_bindings;
1050 return release_bindings;
1054 Bindings::MouseButtonBindingMap&
1055 Bindings::get_mousemap (Operation op)
1059 return button_press_bindings;
1062 return button_release_bindings;
1066 /*==========================================ACTION MAP =========================================*/
1068 ActionMap::ActionMap (string const & name)
1072 action_maps.push_back (this);
1075 ActionMap::~ActionMap ()
1077 action_maps.remove (this);
1081 ActionMap::set_bindings (Bindings* b)
1087 ActionMap::get_actions (ActionMap::Actions& acts)
1089 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
1090 acts.push_back (a->second);
1095 ActionMap::find_action (const string& name)
1097 _ActionMap::iterator a = _actions.find (name);
1099 if (a != _actions.end()) {
1103 return RefPtr<Action>();
1107 ActionMap::create_action_group (const string& name)
1109 RefPtr<ActionGroup> g = ActionGroup::create (name);
1111 /* this is one of the places where our own Action management code
1112 has to touch the GTK one, because we want the GtkUIManager to
1113 be able to create widgets (particularly Menus) from our actions.
1115 This is a a necessary step for that to happen.
1119 ActionManager::ui_manager->insert_action_group (g);
1126 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
1130 RefPtr<Action> act = Action::create (name, label);
1132 fullpath = group->get_name();
1136 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1141 /* already registered */
1142 return RefPtr<Action> ();
1146 ActionMap::register_action (RefPtr<ActionGroup> group,
1147 const char* name, const char* label, sigc::slot<void> sl)
1151 RefPtr<Action> act = Action::create (name, label);
1153 fullpath = group->get_name();
1157 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1158 group->add (act, sl);
1162 /* already registered */
1163 return RefPtr<Action>();
1167 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1168 Gtk::RadioAction::Group& rgroup,
1169 const char* name, const char* label,
1170 sigc::slot<void> sl)
1174 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1175 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1177 fullpath = group->get_name();
1181 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1182 group->add (act, sl);
1186 /* already registered */
1187 return RefPtr<Action>();
1191 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1192 Gtk::RadioAction::Group& rgroup,
1193 const char* name, const char* label,
1194 sigc::slot<void,GtkAction*> sl,
1199 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1200 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1201 ract->property_value() = value;
1203 fullpath = group->get_name();
1207 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1208 group->add (act, sigc::bind (sl, act->gobj()));
1212 /* already registered */
1214 return RefPtr<Action>();
1218 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1219 const char* name, const char* label, sigc::slot<void> sl)
1223 fullpath = group->get_name();
1227 RefPtr<Action> act = ToggleAction::create (name, label);
1229 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1230 group->add (act, sl);
1234 /* already registered */
1235 return RefPtr<Action>();
1239 ActionMap::get_all_actions (std::vector<std::string>& paths,
1240 std::vector<std::string>& labels,
1241 std::vector<std::string>& tooltips,
1242 std::vector<std::string>& keys,
1243 std::vector<RefPtr<Action> >& actions)
1245 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1247 ActionMap::Actions these_actions;
1248 (*map)->get_actions (these_actions);
1250 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1252 paths.push_back ((*act)->get_accel_path());
1253 labels.push_back ((*act)->get_label());
1254 tooltips.push_back ((*act)->get_tooltip());
1255 actions.push_back (*act);
1257 Bindings* bindings = (*map)->bindings();
1262 Bindings::Operation op;
1264 key = bindings->get_binding_for_action (*act, op);
1266 if (key == KeyboardKey::null_key()) {
1267 keys.push_back (string());
1269 keys.push_back (key.display_label());
1272 keys.push_back (string());
1276 these_actions.clear ();
1280 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1281 char const *gdk_name = gdk_keyval_name (k.key());
1282 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;