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();
180 return gtk_accelerator_get_label (key(), (GdkModifierType) mod);
184 KeyboardKey::name () const
190 if (s & Keyboard::PrimaryModifier) {
193 if (s & Keyboard::SecondaryModifier) {
199 if (s & Keyboard::TertiaryModifier) {
205 if (s & Keyboard::Level4Modifier) {
216 char const *gdk_name = gdk_keyval_name (key());
229 KeyboardKey::native_name () const
235 if (s & Keyboard::PrimaryModifier) {
236 str += Keyboard::primary_modifier_name ();
238 if (s & Keyboard::SecondaryModifier) {
242 str += Keyboard::secondary_modifier_name ();
244 if (s & Keyboard::TertiaryModifier) {
248 str += Keyboard::tertiary_modifier_name ();
250 if (s & Keyboard::Level4Modifier) {
254 str += Keyboard::level4_modifier_name ();
261 char const *gdk_name = gdk_keyval_name (key());
274 KeyboardKey::native_short_name () const
280 if (s & Keyboard::PrimaryModifier) {
281 str += Keyboard::primary_modifier_short_name ();
283 if (s & Keyboard::SecondaryModifier) {
287 str += Keyboard::secondary_modifier_short_name ();
289 if (s & Keyboard::TertiaryModifier) {
293 str += Keyboard::tertiary_modifier_short_name ();
295 if (s & Keyboard::Level4Modifier) {
299 str += Keyboard::level4_modifier_short_name ();
306 char const *gdk_name = gdk_keyval_name (key());
319 KeyboardKey::make_key (const string& str, KeyboardKey& k)
323 if (str.find ("Primary") != string::npos) {
324 s |= Keyboard::PrimaryModifier;
327 if (str.find ("Secondary") != string::npos) {
328 s |= Keyboard::SecondaryModifier;
331 if (str.find ("Tertiary") != string::npos) {
332 s |= Keyboard::TertiaryModifier;
335 if (str.find ("Level4") != string::npos) {
336 s |= Keyboard::Level4Modifier;
339 /* since all SINGLE key events keycodes are changed to lower case
340 * before looking them up, make sure we only store lower case here. The
341 * Shift part will be stored in the modifier part of the KeyboardKey.
343 * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
348 string::size_type lastmod = str.find_last_of ('-');
350 if (lastmod != string::npos) {
351 actual = str.substr (lastmod+1);
357 if (actual.size() == 1) {
358 actual = PBD::downcase (actual);
362 keyval = gdk_keyval_from_name (actual.c_str());
364 if (keyval == GDK_VoidSymbol || keyval == 0) {
368 k = KeyboardKey (s, keyval);
373 /*================================= Bindings =================================*/
374 Bindings::Bindings (std::string const& name)
378 bindings.push_back (this);
381 Bindings::~Bindings()
383 bindings.remove (this);
387 Bindings::ardour_action_name (RefPtr<Action> action)
389 /* Skip "<Actions>/" */
390 return action->get_accel_path ().substr (10);
394 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
396 const string action_name = ardour_action_name (action);
398 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
400 /* option one: action has already been associated with the
404 if (k->second.action == action) {
408 /* option two: action name matches, so lookup the action,
409 * setup the association while we're here, and return the binding.
412 if (_action_map && k->second.action_name == action_name) {
413 k->second.action = _action_map->find_action (action_name);
419 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
421 /* option one: action has already been associated with the
425 if (k->second.action == action) {
429 /* option two: action name matches, so lookup the action,
430 * setup the association while we're here, and return the binding.
433 if (_action_map && k->second.action_name == action_name) {
434 k->second.action = _action_map->find_action (action_name);
440 return KeyboardKey::null_key();
444 Bindings::set_action_map (ActionMap& actions)
447 _action_map->set_bindings (0);
450 _action_map = &actions;
451 _action_map->set_bindings (this);
458 Bindings::empty_keys() const
460 return press_bindings.empty() && release_bindings.empty();
464 Bindings::empty_mouse () const
466 return button_press_bindings.empty() && button_release_bindings.empty();
470 Bindings::empty() const
472 return empty_keys() && empty_mouse ();
476 Bindings::activate (KeyboardKey kb, Operation op)
478 KeybindingMap& kbm = get_keymap (op);
480 /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
481 Our bindings all use the lower case character/keyname, so switch
482 to the lower case before doing the lookup.
485 KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
487 KeybindingMap::iterator k = kbm.find (unshifted);
489 if (k == kbm.end()) {
490 /* no entry for this key in the state map */
491 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
495 RefPtr<Action> action;
497 if (k->second.action) {
498 action = k->second.action;
501 action = _action_map->find_action (k->second.action_name);
507 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
511 /* return true even if the action could not be found */
517 Bindings::associate ()
519 KeybindingMap::iterator k;
525 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
526 k->second.action = _action_map->find_action (k->second.action_name);
527 if (k->second.action) {
528 push_to_gtk (k->first, k->second.action);
530 cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
534 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
535 k->second.action = _action_map->find_action (k->second.action_name);
536 /* no working support in GTK for release bindings */
539 MouseButtonBindingMap::iterator b;
541 for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
542 b->second.action = _action_map->find_action (b->second.action_name);
545 for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
546 b->second.action = _action_map->find_action (b->second.action_name);
551 Bindings::dissociate ()
553 KeybindingMap::iterator k;
555 for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
556 k->second.action.clear ();
558 for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
559 k->second.action.clear ();
564 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
566 /* GTK has the useful feature of showing key bindings for actions in
567 * menus. As of August 2015, we have no interest in trying to
568 * reimplement this functionality, so we will use it even though we no
569 * longer use GTK accelerators for handling key events. To do this, we
570 * need to make sure that there is a fully populated GTK AccelMap set
571 * up with all bindings/actions.
574 Gtk::AccelKey gtk_key;
575 bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
579 /* there is a trick happening here. It turns out that
580 * gtk_accel_map_add_entry() performs no validation checks on
581 * the accelerator keyval. This means we can use it to define
582 * ANY accelerator, even if they violate GTK's rules
583 * (e.g. about not using navigation keys). This works ONLY when
584 * the entry in the GTK accelerator map has not already been
585 * added. The entries will be added by the GTK UIManager when
586 * building menus, so this code must be called before that
591 int mod = kb.state();
593 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) mod);
598 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
604 if (is_registered(op, action_name)) {
605 remove (op, action_name, can_save);
608 /* XXX need a way to get the old group name */
609 add (kb, op, action_name, 0, can_save);
615 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, XMLProperty const* group, bool can_save)
617 if (is_registered (op, action_name)) {
621 KeybindingMap& kbm = get_keymap (op);
623 KeybindingMap::value_type new_pair = make_pair (kb, ActionInfo (action_name, group->value()));
624 (void) kbm.insert (new_pair).first;
626 KeybindingMap::value_type new_pair = make_pair (kb, ActionInfo (action_name));
627 (void) kbm.insert (new_pair).first;
630 DEBUG_TRACE (DEBUG::Bindings, string_compose ("add binding between %1 and %2, group [%3]\n",
631 kb, action_name, (group ? group->value() : string())));
634 Keyboard::keybindings_changed ();
637 BindingsChanged (this); /* EMIT SIGNAL */
642 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
644 bool erased_action = false;
645 KeybindingMap& kbm = get_keymap (op);
646 for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
647 if (k->second.action_name == action_name) {
649 erased_action = true;
654 if (!erased_action) {
655 return erased_action;
659 Keyboard::keybindings_changed ();
662 BindingsChanged (this); /* EMIT SIGNAL */
663 return erased_action;
668 Bindings::activate (MouseButton bb, Operation op)
670 MouseButtonBindingMap& bbm = get_mousemap(op);
672 MouseButtonBindingMap::iterator b = bbm.find (bb);
674 if (b == bbm.end()) {
675 /* no entry for this key in the state map */
679 RefPtr<Action> action;
681 if (b->second.action) {
682 action = b->second.action;
685 action = _action_map->find_action (b->second.action_name);
691 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
695 /* return true even if the action could not be found */
701 Bindings::add (MouseButton bb, Operation op, string const& action_name, XMLProperty const* /*group*/)
703 MouseButtonBindingMap& bbm = get_mousemap(op);
705 MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
706 bbm.insert (newpair);
710 Bindings::remove (MouseButton bb, Operation op)
712 MouseButtonBindingMap& bbm = get_mousemap(op);
713 MouseButtonBindingMap::iterator b = bbm.find (bb);
715 if (b != bbm.end()) {
721 Bindings::save (XMLNode& root)
723 XMLNode* presses = new XMLNode (X_("Press"));
725 for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
728 if (k->first.name().empty()) {
732 child = new XMLNode (X_("Binding"));
733 child->set_property (X_("key"), k->first.name());
734 child->set_property (X_("action"), k->second.action_name);
735 presses->add_child_nocopy (*child);
738 for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
740 child = new XMLNode (X_("Binding"));
741 child->set_property (X_("button"), k->first.name());
742 child->set_property (X_("action"), k->second.action_name);
743 presses->add_child_nocopy (*child);
746 XMLNode* releases = new XMLNode (X_("Release"));
748 for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
751 if (k->first.name().empty()) {
755 child = new XMLNode (X_("Binding"));
756 child->set_property (X_("key"), k->first.name());
757 child->set_property (X_("action"), k->second.action_name);
758 releases->add_child_nocopy (*child);
761 for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
763 child = new XMLNode (X_("Binding"));
764 child->set_property (X_("button"), k->first.name());
765 child->set_property (X_("action"), k->second.action_name);
766 releases->add_child_nocopy (*child);
769 root.add_child_nocopy (*presses);
770 root.add_child_nocopy (*releases);
774 Bindings::save_all_bindings_as_html (ostream& ostr)
776 if (bindings.empty()) {
781 ostr << "<html>\n<head>\n<title>";
782 ostr << PROGRAM_NAME;
783 ostr << "</title>\n";
784 ostr << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n";
786 ostr << "</head>\n<body>\n";
788 ostr << "<table border=\"2\" cellpadding=\"6\"><tbody>\n\n";
791 /* first column: separate by group */
793 for (list<Bindings*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
794 (*b)->save_as_html (ostr, true);
799 ostr << "<td style=\"vertical-align:top\">\n\n";
800 for (list<Bindings*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
801 (*b)->save_as_html (ostr, false);
807 ostr << "</tbody></table>\n\n";
809 ostr << "</br></br>\n\n";
810 ostr << "<table border=\"2\" cellpadding=\"6\"><tbody>\n\n";
813 ostr << "<h2><u> Partial List of Available Actions { => with current shortcut, where applicable } </u></h2>\n\n";
815 vector<string> paths;
816 vector<string> labels;
817 vector<string> tooltips;
819 vector<Glib::RefPtr<Gtk::Action> > actions;
821 Gtkmm2ext::ActionMap::get_all_actions (paths, labels, tooltips, keys, actions);
823 vector<string>::iterator k;
824 vector<string>::iterator p;
825 vector<string>::iterator l;
827 for (p = paths.begin(), k = keys.begin(), l = labels.begin(); p != paths.end(); ++k, ++p, ++l) {
829 string print_path = *p;
830 /* strip <Actions>/ from the start */
831 print_path = print_path.substr (10);
834 ostr << print_path << " ( " << *l << " ) " << "</br>" << endl;
836 ostr << print_path << " ( " << *l << " ) " << " => " << *k << "</br>" << endl;
842 ostr << "</tbody></table>\n\n";
849 Bindings::save_as_html (ostream& ostr, bool categorize) const
852 if (!press_bindings.empty()) {
856 ostr << _("Window") << ": " << name() << _(" (Categorized)");
858 ostr << _("Window") << ": " << name() << _(" (Alphabetical)");
859 ostr << "</u></h2>\n\n";
861 typedef std::map<std::string, std::vector<KeybindingMap::const_iterator> > GroupMap;
864 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
866 if (k->first.name().empty()) {
871 if (categorize && !k->second.group_name.empty()) {
872 group_name = k->second.group_name;
874 group_name = _("Uncategorized");
877 GroupMap::iterator gm = group_map.find (group_name);
878 if (gm == group_map.end()) {
879 std::vector<KeybindingMap::const_iterator> li;
881 group_map.insert (make_pair (group_name,li));
883 gm->second.push_back (k);
888 for (GroupMap::const_iterator gm = group_map.begin(); gm != group_map.end(); ++gm) {
891 ostr << "<h3>" << gm->first << "</h3>\n";
894 for (vector<KeybindingMap::const_iterator>::const_iterator k = gm->second.begin(); k != gm->second.end(); ++k) {
896 if ((*k)->first.name().empty()) {
900 RefPtr<Action> action;
902 if ((*k)->second.action) {
903 action = (*k)->second.action;
906 action = _action_map->find_action ((*k)->second.action_name);
914 string key_name = (*k)->first.native_short_name ();
915 replace_all (key_name, X_("KP_"), X_("Numpad "));
916 replace_all (key_name, X_("nabla"), X_("Tab"));
918 string::size_type pos;
920 char const *targets[] = { X_("Separator"), X_("Add"), X_("Subtract"), X_("Decimal"), X_("Divide"),
921 X_("grave"), X_("comma"), X_("period"), X_("asterisk"), X_("backslash"),
922 X_("apostrophe"), X_("minus"), X_("plus"), X_("slash"), X_("semicolon"),
923 X_("colon"), X_("equal"), X_("bracketleft"), X_("bracketright"),
924 X_("ampersand"), X_("numbersign"), X_("parenleft"), X_("parenright"),
925 X_("quoteright"), X_("quoteleft"), X_("exclam"), X_("quotedbl"),
929 char const *replacements[] = { X_("-"), X_("+"), X_("-"), X_("."), X_("/"),
930 X_("`"), X_(","), X_("."), X_("*"), X_("\\"),
931 X_("'"), X_("-"), X_("+"), X_("/"), X_(";"),
932 X_(":"), X_("="), X_("{"), X_("{"),
933 X_("&"), X_("#"), X_("("), X_(")"),
934 X_("`"), X_("'"), X_("!"), X_("\""),
937 for (size_t n = 0; targets[n]; ++n) {
938 if ((pos = key_name.find (targets[n])) != string::npos) {
939 key_name.replace (pos, strlen (targets[n]), replacements[n]);
943 key_name.append(" ");
945 while (key_name.length()<28)
946 key_name.append("-");
948 ostr << "<span style=\"font-family:monospace;\">" << key_name;
949 ostr << "<i>" << action->get_label() << "</i></span></br>\n";
960 Bindings::load (XMLNode const& node)
962 const XMLNodeList& children (node.children());
964 press_bindings.clear ();
965 release_bindings.clear ();
967 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
968 /* each node could be Press or Release */
969 load_operation (**i);
976 Bindings::load_operation (XMLNode const& node)
978 if (node.name() == X_("Press") || node.name() == X_("Release")) {
982 if (node.name() == X_("Press")) {
988 const XMLNodeList& children (node.children());
990 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
992 XMLProperty const * ap;
993 XMLProperty const * kp;
994 XMLProperty const * bp;
995 XMLProperty const * gp;
996 XMLNode const * child = *p;
998 ap = child->property ("action");
999 kp = child->property ("key");
1000 bp = child->property ("button");
1001 gp = child->property ("group");
1003 if (!ap || (!kp && !bp)) {
1009 if (!KeyboardKey::make_key (kp->value(), k)) {
1012 add (k, op, ap->value(), gp);
1015 if (!MouseButton::make_button (bp->value(), b)) {
1018 add (b, op, ap->value(), gp);
1025 Bindings::get_all_actions (std::vector<std::string>& paths,
1026 std::vector<std::string>& labels,
1027 std::vector<std::string>& tooltips,
1028 std::vector<std::string>& keys,
1029 std::vector<RefPtr<Action> >& actions)
1035 /* build a reverse map from actions to bindings */
1037 typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
1040 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
1041 rmap.insert (make_pair (k->second.action, k->first));
1044 /* get a list of all actions */
1046 ActionMap::Actions all_actions;
1047 _action_map->get_actions (all_actions);
1049 for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
1051 paths.push_back ((*act)->get_accel_path());
1052 labels.push_back ((*act)->get_label());
1053 tooltips.push_back ((*act)->get_tooltip());
1055 ReverseMap::iterator r = rmap.find (*act);
1057 if (r != rmap.end()) {
1058 keys.push_back (r->second.display_label());
1060 keys.push_back (string());
1063 actions.push_back (*act);
1068 Bindings::get_bindings (string const& name, ActionMap& map)
1070 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
1071 if ((*b)->name() == name) {
1072 (*b)->set_action_map (map);
1081 Bindings::associate_all ()
1083 for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
1089 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
1091 const KeybindingMap& km = get_keymap(op);
1092 return km.find(kb) != km.end();
1096 Bindings::bound_name (KeyboardKey const& kb, Operation op) const
1098 const KeybindingMap& km = get_keymap(op);
1099 KeybindingMap::const_iterator b = km.find(kb);
1100 if (b == km.end()) {
1103 return b->second.action_name;
1107 Bindings::is_registered (Operation op, std::string const& action_name) const
1109 const KeybindingMap& km = get_keymap(op);
1110 return std::find_if(km.begin(), km.end(), ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
1113 Bindings::KeybindingMap&
1114 Bindings::get_keymap (Operation op)
1118 return press_bindings;
1121 return release_bindings;
1125 const Bindings::KeybindingMap&
1126 Bindings::get_keymap (Operation op) const
1130 return press_bindings;
1133 return release_bindings;
1137 Bindings::MouseButtonBindingMap&
1138 Bindings::get_mousemap (Operation op)
1142 return button_press_bindings;
1145 return button_release_bindings;
1149 /*==========================================ACTION MAP =========================================*/
1151 ActionMap::ActionMap (string const & name)
1155 action_maps.push_back (this);
1158 ActionMap::~ActionMap ()
1160 action_maps.remove (this);
1164 ActionMap::set_bindings (Bindings* b)
1170 ActionMap::get_actions (ActionMap::Actions& acts)
1172 for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
1173 acts.push_back (a->second);
1178 ActionMap::find_action (const string& name)
1180 _ActionMap::iterator a = _actions.find (name);
1182 if (a != _actions.end()) {
1186 return RefPtr<Action>();
1190 ActionMap::create_action_group (const string& name)
1192 Glib::ListHandle<Glib::RefPtr<ActionGroup> > agl = ActionManager::ui_manager->get_action_groups ();
1193 for (Glib::ListHandle<Glib::RefPtr<ActionGroup> >::iterator i = agl.begin (); i != agl.end (); ++i) {
1194 if ((*i)->get_name () == name) {
1199 RefPtr<ActionGroup> g = ActionGroup::create (name);
1201 /* this is one of the places where our own Action management code
1202 has to touch the GTK one, because we want the GtkUIManager to
1203 be able to create widgets (particularly Menus) from our actions.
1205 This is a a necessary step for that to happen.
1209 ActionManager::ui_manager->insert_action_group (g);
1216 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
1220 RefPtr<Action> act = Action::create (name, label);
1222 fullpath = group->get_name();
1226 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1231 /* already registered */
1232 return RefPtr<Action> ();
1236 ActionMap::register_action (RefPtr<ActionGroup> group,
1237 const char* name, const char* label, sigc::slot<void> sl)
1241 RefPtr<Action> act = Action::create (name, label);
1243 fullpath = group->get_name();
1247 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1248 group->add (act, sl);
1252 /* already registered */
1253 return RefPtr<Action>();
1257 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1258 Gtk::RadioAction::Group& rgroup,
1259 const char* name, const char* label,
1260 sigc::slot<void> sl)
1264 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1265 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1267 fullpath = group->get_name();
1271 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1272 group->add (act, sl);
1276 /* already registered */
1277 return RefPtr<Action>();
1281 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1282 Gtk::RadioAction::Group& rgroup,
1283 const char* name, const char* label,
1284 sigc::slot<void,GtkAction*> sl,
1289 RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1290 RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1291 ract->property_value() = value;
1293 fullpath = group->get_name();
1297 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1298 group->add (act, sigc::bind (sl, act->gobj()));
1302 /* already registered */
1304 return RefPtr<Action>();
1308 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1309 const char* name, const char* label, sigc::slot<void> sl)
1313 fullpath = group->get_name();
1317 RefPtr<Action> act = ToggleAction::create (name, label);
1319 if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1320 group->add (act, sl);
1324 /* already registered */
1325 return RefPtr<Action>();
1329 ActionMap::get_all_actions (std::vector<std::string>& paths,
1330 std::vector<std::string>& labels,
1331 std::vector<std::string>& tooltips,
1332 std::vector<std::string>& keys,
1333 std::vector<RefPtr<Action> >& actions)
1335 for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1337 ActionMap::Actions these_actions;
1338 (*map)->get_actions (these_actions);
1340 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1342 paths.push_back ((*act)->get_accel_path());
1343 labels.push_back ((*act)->get_label());
1344 tooltips.push_back ((*act)->get_tooltip());
1345 actions.push_back (*act);
1347 Bindings* bindings = (*map)->bindings();
1352 Bindings::Operation op;
1354 key = bindings->get_binding_for_action (*act, op);
1356 if (key == KeyboardKey::null_key()) {
1357 keys.push_back (string());
1359 keys.push_back (key.display_label());
1362 keys.push_back (string());
1366 these_actions.clear ();
1370 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1371 char const *gdk_name = gdk_keyval_name (k.key());
1372 return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state "
1373 << hex << k.state() << dec << ' ' << show_gdk_event_state (k.state());