inability to save non single character key binding with modifier eg. Ctrl-F5
[ardour.git] / libs / gtkmm2ext / bindings.cc
1 /*
2   Copyright (C) 2012 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <iostream>
21
22 #include "pbd/gstdio_compat.h"
23 #include <gtkmm/accelmap.h>
24 #include <gtkmm/uimanager.h>
25
26 #include "pbd/convert.h"
27 #include "pbd/debug.h"
28 #include "pbd/error.h"
29 #include "pbd/xml++.h"
30
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"
36
37 #include "i18n.h"
38
39 using namespace std;
40 using namespace Glib;
41 using namespace Gtk;
42 using namespace Gtkmm2ext;
43 using namespace PBD;
44
45 list<Bindings*> Bindings::bindings; /* global. Gulp */
46 list<ActionMap*> ActionMap::action_maps; /* global. Gulp */
47 PBD::Signal1<void,Bindings*> Bindings::BindingsChanged;
48
49 template <typename IteratorValueType>
50 struct ActionNameRegistered
51 {
52         ActionNameRegistered(std::string const& name)
53                 : action_name(name)
54         {}
55
56         bool operator()(IteratorValueType elem) const {
57                 return elem.second.action_name == action_name;
58         }
59         std::string const& action_name;
60 };
61
62 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
63 {
64         uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
65
66         /* this is a slightly wierd test that relies on
67          * gdk_keyval_is_{upper,lower}() returning true for keys that have no
68          * case-sensitivity. This covers mostly non-alphanumeric keys.
69          */
70
71         if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
72                 /* key is not subject to case, so ignore SHIFT
73                  */
74                 ignore |= GDK_SHIFT_MASK;
75         }
76
77         _val = (state & ~ignore);
78         _val <<= 32;
79         _val |= keycode;
80 };
81
82 bool
83 MouseButton::make_button (const string& str, MouseButton& b)
84 {
85         int s = 0;
86
87         if (str.find ("Primary") != string::npos) {
88                 s |= Keyboard::PrimaryModifier;
89         }
90
91         if (str.find ("Secondary") != string::npos) {
92                 s |= Keyboard::SecondaryModifier;
93         }
94
95         if (str.find ("Tertiary") != string::npos) {
96                 s |= Keyboard::TertiaryModifier;
97         }
98
99         if (str.find ("Level4") != string::npos) {
100                 s |= Keyboard::Level4Modifier;
101         }
102
103         string::size_type lastmod = str.find_last_of ('-');
104         uint32_t button_number;
105
106         if (lastmod == string::npos) {
107                 button_number = PBD::atoi (str);
108         } else {
109                 button_number = PBD::atoi (str.substr (lastmod+1));
110         }
111
112         b = MouseButton (s, button_number);
113         return true;
114 }
115
116 string
117 MouseButton::name () const
118 {
119         int s = state();
120
121         string str;
122
123         if (s & Keyboard::PrimaryModifier) {
124                 str += "Primary";
125         }
126         if (s & Keyboard::SecondaryModifier) {
127                 if (!str.empty()) {
128                         str += '-';
129                 }
130                 str += "Secondary";
131         }
132         if (s & Keyboard::TertiaryModifier) {
133                 if (!str.empty()) {
134                         str += '-';
135                 }
136                 str += "Tertiary";
137         }
138         if (s & Keyboard::Level4Modifier) {
139                 if (!str.empty()) {
140                         str += '-';
141                 }
142                 str += "Level4";
143         }
144
145         if (!str.empty()) {
146                 str += '-';
147         }
148
149         char buf[16];
150         snprintf (buf, sizeof (buf), "%u", button());
151         str += buf;
152
153         return str;
154 }
155
156 /*================================ KeyboardKey ================================*/
157 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
158 {
159         uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
160
161         _val = (state & ~ignore);
162         _val <<= 32;
163         _val |= keycode;
164 }
165
166 string
167 KeyboardKey::display_label () const
168 {
169         if (key() == 0) {
170                 return string();
171         }
172
173         /* This magically returns a string that will display the right thing
174          *  on all platforms, notably the command key on OS X.
175          */
176
177         uint32_t mod = state();
178
179 #ifdef __APPLE__
180         /* We use both bits (MOD2|META) for Primary on OS X,
181          * but we don't want MOD2 showing up in listings.
182          */
183
184         if (mod & GDK_MOD2_MASK) {
185                 mod &= ~GDK_MOD2_MASK;
186         }
187 #endif
188
189         return gtk_accelerator_get_label (key(), (GdkModifierType) mod);
190 }
191
192 string
193 KeyboardKey::name () const
194 {
195         int s = state();
196
197         string str;
198
199         if (s & Keyboard::PrimaryModifier) {
200                 str += "Primary";
201         }
202         if (s & Keyboard::SecondaryModifier) {
203                 if (!str.empty()) {
204                         str += '-';
205                 }
206                 str += "Secondary";
207         }
208         if (s & Keyboard::TertiaryModifier) {
209                 if (!str.empty()) {
210                         str += '-';
211                 }
212                 str += "Tertiary";
213         }
214         if (s & Keyboard::Level4Modifier) {
215                 if (!str.empty()) {
216                         str += '-';
217                 }
218                 str += "Level4";
219         }
220
221         if (!str.empty()) {
222                 str += '-';
223         }
224
225         char const *gdk_name = gdk_keyval_name (key());
226
227         if (gdk_name) {
228                 str += gdk_name;
229         } else {
230                 /* fail! */
231                 return string();
232         }
233
234         return str;
235 }
236
237 bool
238 KeyboardKey::make_key (const string& str, KeyboardKey& k)
239 {
240         int s = 0;
241
242         if (str.find ("Primary") != string::npos) {
243                 s |= Keyboard::PrimaryModifier;
244         }
245
246         if (str.find ("Secondary") != string::npos) {
247                 s |= Keyboard::SecondaryModifier;
248         }
249
250         if (str.find ("Tertiary") != string::npos) {
251                 s |= Keyboard::TertiaryModifier;
252         }
253
254         if (str.find ("Level4") != string::npos) {
255                 s |= Keyboard::Level4Modifier;
256         }
257
258         /* since all SINGLE key events keycodes are changed to lower case
259          * before looking them up, make sure we only store lower case here. The
260          * Shift part will be stored in the modifier part of the KeyboardKey.
261          *
262          * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
263          */
264
265         string actual;
266
267         string::size_type lastmod = str.find_last_of ('-');
268
269         if (lastmod != string::npos) {
270                 actual = str.substr (lastmod+1);
271         }
272         else {
273                 actual = str;
274         }
275
276         if (actual.size() == 1) {
277                 actual = PBD::downcase (actual);
278         }
279
280         guint keyval;
281         keyval = gdk_keyval_from_name (actual.c_str());
282
283         if (keyval == GDK_VoidSymbol || keyval == 0) {
284                 return false;
285         }
286
287         k = KeyboardKey (s, keyval);
288
289         return true;
290 }
291
292 /*================================= Bindings =================================*/
293 Bindings::Bindings (std::string const& name)
294         : _name (name)
295         , _action_map (0)
296 {
297         bindings.push_back (this);
298 }
299
300 Bindings::~Bindings()
301 {
302         bindings.remove (this);
303 }
304
305 string
306 Bindings::ardour_action_name (RefPtr<Action> action)
307 {
308         /* Skip "<Actions>/" */
309         return action->get_accel_path ().substr (10);
310 }
311
312 KeyboardKey
313 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
314 {
315         const string action_name = ardour_action_name (action);
316
317         for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
318
319                 /* option one: action has already been associated with the
320                  * binding
321                  */
322
323                 if (k->second.action == action) {
324                         return k->first;
325                 }
326
327                 /* option two: action name matches, so lookup the action,
328                  * setup the association while we're here, and return the binding.
329                  */
330
331                 if (_action_map && k->second.action_name == action_name) {
332                         k->second.action = _action_map->find_action (action_name);
333                         return k->first;
334                 }
335
336         }
337
338         for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
339
340                 /* option one: action has already been associated with the
341                  * binding
342                  */
343
344                 if (k->second.action == action) {
345                         return k->first;
346                 }
347
348                 /* option two: action name matches, so lookup the action,
349                  * setup the association while we're here, and return the binding.
350                  */
351
352                 if (_action_map && k->second.action_name == action_name) {
353                         k->second.action = _action_map->find_action (action_name);
354                         return k->first;
355                 }
356
357         }
358
359         return KeyboardKey::null_key();
360 }
361
362 void
363 Bindings::set_action_map (ActionMap& actions)
364 {
365         if (_action_map) {
366                 _action_map->set_bindings (0);
367         }
368
369         _action_map = &actions;
370         _action_map->set_bindings (this);
371
372         dissociate ();
373         associate ();
374 }
375
376 bool
377 Bindings::empty_keys() const
378 {
379         return press_bindings.empty() && release_bindings.empty();
380 }
381
382 bool
383 Bindings::empty_mouse () const
384 {
385         return button_press_bindings.empty() && button_release_bindings.empty();
386 }
387
388 bool
389 Bindings::empty() const
390 {
391         return empty_keys() && empty_mouse ();
392 }
393
394 bool
395 Bindings::activate (KeyboardKey kb, Operation op)
396 {
397         KeybindingMap& kbm = get_keymap (op);
398
399         /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
400            Our bindings all use the lower case character/keyname, so switch
401            to the lower case before doing the lookup.
402         */
403
404         KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
405
406         KeybindingMap::iterator k = kbm.find (unshifted);
407
408         if (k == kbm.end()) {
409                 /* no entry for this key in the state map */
410                 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
411                 return false;
412         }
413
414         RefPtr<Action> action;
415
416         if (k->second.action) {
417                 action = k->second.action;
418         } else {
419                 if (_action_map) {
420                         action = _action_map->find_action (k->second.action_name);
421                 }
422         }
423
424         if (action) {
425                 /* lets do it ... */
426                 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
427                 action->activate ();
428         }
429
430         /* return true even if the action could not be found */
431
432         return true;
433 }
434
435 void
436 Bindings::associate ()
437 {
438         KeybindingMap::iterator k;
439
440         if (!_action_map) {
441                 return;
442         }
443
444         for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
445                 k->second.action = _action_map->find_action (k->second.action_name);
446                 if (k->second.action) {
447                         push_to_gtk (k->first, k->second.action);
448                 } else {
449                         cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
450                 }
451         }
452
453         for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
454                 k->second.action = _action_map->find_action (k->second.action_name);
455                 /* no working support in GTK for release bindings */
456         }
457
458         MouseButtonBindingMap::iterator b;
459
460         for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
461                 b->second.action = _action_map->find_action (b->second.action_name);
462         }
463
464         for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
465                 b->second.action = _action_map->find_action (b->second.action_name);
466         }
467 }
468
469 void
470 Bindings::dissociate ()
471 {
472         KeybindingMap::iterator k;
473
474         for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
475                 k->second.action.clear ();
476         }
477         for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
478                 k->second.action.clear ();
479         }
480 }
481
482 void
483 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
484 {
485         /* GTK has the useful feature of showing key bindings for actions in
486          * menus. As of August 2015, we have no interest in trying to
487          * reimplement this functionality, so we will use it even though we no
488          * longer use GTK accelerators for handling key events. To do this, we
489          * need to make sure that there is a fully populated GTK AccelMap set
490          * up with all bindings/actions.
491          */
492
493         Gtk::AccelKey gtk_key;
494         bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
495
496         if (!entry_exists) {
497
498                 /* there is a trick happening here. It turns out that
499                  * gtk_accel_map_add_entry() performs no validation checks on
500                  * the accelerator keyval. This means we can use it to define
501                  * ANY accelerator, even if they violate GTK's rules
502                  * (e.g. about not using navigation keys). This works ONLY when
503                  * the entry in the GTK accelerator map has not already been
504                  * added. The entries will be added by the GTK UIManager when
505                  * building menus, so this code must be called before that
506                  * happens.
507                  */
508
509                 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) kb.state());
510         }
511 }
512
513 bool
514 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
515 {
516         if (!_action_map) {
517                 return false;
518         }
519
520         if (is_registered(op, action_name)) {
521                 remove(op, action_name, can_save);
522         }
523         add (kb, op, action_name, can_save);
524         return true;
525 }
526
527 bool
528 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
529 {
530         if (is_registered(op, action_name)) {
531                 return false;
532         }
533
534         KeybindingMap& kbm = get_keymap (op);
535
536         KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
537         kbm.insert (new_pair).first;
538
539         if (can_save) {
540                 Keyboard::keybindings_changed ();
541         }
542
543         BindingsChanged (this); /* EMIT SIGNAL */
544         return true;
545 }
546
547 bool
548 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
549 {
550         bool erased_action = false;
551         KeybindingMap& kbm = get_keymap (op);
552         for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
553                 if (k->second.action_name == action_name) {
554                         kbm.erase (k);
555                         erased_action = true;
556                         break;
557                 }
558         }
559
560         if (!erased_action) {
561                 return erased_action;
562         }
563
564         if (can_save) {
565                 Keyboard::keybindings_changed ();
566         }
567
568         BindingsChanged (this); /* EMIT SIGNAL */
569         return erased_action;
570 }
571
572
573 bool
574 Bindings::activate (MouseButton bb, Operation op)
575 {
576         MouseButtonBindingMap& bbm = get_mousemap(op);
577
578         MouseButtonBindingMap::iterator b = bbm.find (bb);
579
580         if (b == bbm.end()) {
581                 /* no entry for this key in the state map */
582                 return false;
583         }
584
585         RefPtr<Action> action;
586
587         if (b->second.action) {
588                 action = b->second.action;
589         } else {
590                 if (_action_map) {
591                         action = _action_map->find_action (b->second.action_name);
592                 }
593         }
594
595         if (action) {
596                 /* lets do it ... */
597                 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
598                 action->activate ();
599         }
600
601         /* return true even if the action could not be found */
602
603         return true;
604 }
605
606 void
607 Bindings::add (MouseButton bb, Operation op, string const& action_name)
608 {
609         MouseButtonBindingMap& bbm = get_mousemap(op);
610
611         MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
612         bbm.insert (newpair);
613 }
614
615 void
616 Bindings::remove (MouseButton bb, Operation op)
617 {
618         MouseButtonBindingMap& bbm = get_mousemap(op);
619         MouseButtonBindingMap::iterator b = bbm.find (bb);
620
621         if (b != bbm.end()) {
622                 bbm.erase (b);
623         }
624 }
625
626 void
627 Bindings::save (XMLNode& root)
628 {
629         XMLNode* presses = new XMLNode (X_("Press"));
630
631         for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
632                 XMLNode* child;
633
634                 if (k->first.name().empty()) {
635                         continue;
636                 }
637
638                 child = new XMLNode (X_("Binding"));
639                 child->add_property (X_("key"), k->first.name());
640                 child->add_property (X_("action"), k->second.action_name);
641                 presses->add_child_nocopy (*child);
642         }
643
644         for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
645                 XMLNode* child;
646                 child = new XMLNode (X_("Binding"));
647                 child->add_property (X_("button"), k->first.name());
648                 child->add_property (X_("action"), k->second.action_name);
649                 presses->add_child_nocopy (*child);
650         }
651
652         XMLNode* releases = new XMLNode (X_("Release"));
653
654         for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
655                 XMLNode* child;
656
657                 if (k->first.name().empty()) {
658                         continue;
659                 }
660
661                 child = new XMLNode (X_("Binding"));
662                 child->add_property (X_("key"), k->first.name());
663                 child->add_property (X_("action"), k->second.action_name);
664                 releases->add_child_nocopy (*child);
665         }
666
667         for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
668                 XMLNode* child;
669                 child = new XMLNode (X_("Binding"));
670                 child->add_property (X_("button"), k->first.name());
671                 child->add_property (X_("action"), k->second.action_name);
672                 releases->add_child_nocopy (*child);
673         }
674
675         root.add_child_nocopy (*presses);
676         root.add_child_nocopy (*releases);
677 }
678
679 bool
680 Bindings::load (XMLNode const& node)
681 {
682         const XMLNodeList& children (node.children());
683
684         press_bindings.clear ();
685         release_bindings.clear ();
686
687         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
688                 /* each node could be Press or Release */
689                 load_operation (**i);
690         }
691
692         return true;
693 }
694
695 void
696 Bindings::load_operation (XMLNode const& node)
697 {
698         if (node.name() == X_("Press") || node.name() == X_("Release")) {
699
700                 Operation op;
701
702                 if (node.name() == X_("Press")) {
703                         op = Press;
704                 } else {
705                         op = Release;
706                 }
707
708                 const XMLNodeList& children (node.children());
709
710                 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
711
712                         XMLProperty* ap;
713                         XMLProperty* kp;
714                         XMLProperty* bp;
715
716                         ap = (*p)->property ("action");
717                         kp = (*p)->property ("key");
718                         bp = (*p)->property ("button");
719
720                         if (!ap || (!kp && !bp)) {
721                                 continue;
722                         }
723
724                         if (kp) {
725                                 KeyboardKey k;
726                                 if (!KeyboardKey::make_key (kp->value(), k)) {
727                                         continue;
728                                 }
729                                 add (k, op, ap->value());
730                         } else {
731                                 MouseButton b;
732                                 if (!MouseButton::make_button (bp->value(), b)) {
733                                         continue;
734                                 }
735                                 add (b, op, ap->value());
736                         }
737                 }
738         }
739 }
740
741 void
742 Bindings::get_all_actions (std::vector<std::string>& paths,
743                            std::vector<std::string>& labels,
744                            std::vector<std::string>& tooltips,
745                            std::vector<std::string>& keys,
746                            std::vector<RefPtr<Action> >& actions)
747 {
748         if (!_action_map) {
749                 return;
750         }
751
752         /* build a reverse map from actions to bindings */
753
754         typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
755         ReverseMap rmap;
756
757         for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
758                 rmap.insert (make_pair (k->second.action, k->first));
759         }
760
761         /* get a list of all actions */
762
763         ActionMap::Actions all_actions;
764         _action_map->get_actions (all_actions);
765
766         for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
767
768                 paths.push_back ((*act)->get_accel_path());
769                 labels.push_back ((*act)->get_label());
770                 tooltips.push_back ((*act)->get_tooltip());
771
772                 ReverseMap::iterator r = rmap.find (*act);
773
774                 if (r != rmap.end()) {
775                         keys.push_back (r->second.display_label());
776                 } else {
777                         keys.push_back (string());
778                 }
779
780                 actions.push_back (*act);
781         }
782 }
783
784 Bindings*
785 Bindings::get_bindings (string const& name, ActionMap& map)
786 {
787         for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
788                 if ((*b)->name() == name) {
789                         (*b)->set_action_map (map);
790                         return *b;
791                 }
792         }
793
794         return 0;
795 }
796
797 void
798 Bindings::associate_all ()
799 {
800         for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
801                 (*b)->associate ();
802         }
803 }
804
805 bool
806 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
807 {
808         const KeybindingMap& km = get_keymap(op);
809         return km.find(kb) != km.end();
810 }
811
812 bool
813 Bindings::is_registered (Operation op, std::string const& action_name) const
814 {
815         const KeybindingMap& km = get_keymap(op);
816         return std::find_if(km.begin(),  km.end(),  ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
817 }
818
819 Bindings::KeybindingMap&
820 Bindings::get_keymap (Operation op)
821 {
822         switch (op) {
823         case Press:
824                 return press_bindings;
825         case Release:
826         default:
827                 return release_bindings;
828         }
829 }
830
831 const Bindings::KeybindingMap&
832 Bindings::get_keymap (Operation op) const
833 {
834         switch (op) {
835         case Press:
836                 return press_bindings;
837         case Release:
838         default:
839                 return release_bindings;
840         }
841 }
842
843 Bindings::MouseButtonBindingMap&
844 Bindings::get_mousemap (Operation op)
845 {
846         switch (op) {
847         case Press:
848                 return button_press_bindings;
849         case Release:
850         default:
851                 return button_release_bindings;
852         }
853 }
854
855 /*==========================================ACTION MAP =========================================*/
856
857 ActionMap::ActionMap (string const & name)
858         : _name (name)
859         , _bindings (0)
860 {
861         action_maps.push_back (this);
862 }
863
864 ActionMap::~ActionMap ()
865 {
866         action_maps.remove (this);
867 }
868
869 void
870 ActionMap::set_bindings (Bindings* b)
871 {
872         _bindings = b;
873 }
874
875 void
876 ActionMap::get_actions (ActionMap::Actions& acts)
877 {
878         for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
879                 acts.push_back (a->second);
880         }
881 }
882
883 RefPtr<Action>
884 ActionMap::find_action (const string& name)
885 {
886         _ActionMap::iterator a = _actions.find (name);
887
888         if (a != _actions.end()) {
889                 return a->second;
890         }
891
892         return RefPtr<Action>();
893 }
894
895 RefPtr<ActionGroup>
896 ActionMap::create_action_group (const string& name)
897 {
898         RefPtr<ActionGroup> g = ActionGroup::create (name);
899
900         /* this is one of the places where our own Action management code
901            has to touch the GTK one, because we want the GtkUIManager to
902            be able to create widgets (particularly Menus) from our actions.
903
904            This is a a necessary step for that to happen.
905         */
906
907         if (g) {
908                 ActionManager::ui_manager->insert_action_group (g);
909         }
910
911         return g;
912 }
913
914 RefPtr<Action>
915 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
916 {
917         string fullpath;
918
919         RefPtr<Action> act = Action::create (name, label);
920
921         fullpath = group->get_name();
922         fullpath += '/';
923         fullpath += name;
924
925         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
926                 group->add (act);
927                 return act;
928         }
929
930         /* already registered */
931         return RefPtr<Action> ();
932 }
933
934 RefPtr<Action>
935 ActionMap::register_action (RefPtr<ActionGroup> group,
936                             const char* name, const char* label, sigc::slot<void> sl)
937 {
938         string fullpath;
939
940         RefPtr<Action> act = Action::create (name, label);
941
942         fullpath = group->get_name();
943         fullpath += '/';
944         fullpath += name;
945
946         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
947                 group->add (act, sl);
948                 return act;
949         }
950
951         /* already registered */
952         return RefPtr<Action>();
953 }
954
955 RefPtr<Action>
956 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
957                                   Gtk::RadioAction::Group& rgroup,
958                                   const char* name, const char* label,
959                                   sigc::slot<void> sl)
960 {
961         string fullpath;
962
963         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
964         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
965
966         fullpath = group->get_name();
967         fullpath += '/';
968         fullpath += name;
969
970         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
971                 group->add (act, sl);
972                 return act;
973         }
974
975         /* already registered */
976         return RefPtr<Action>();
977 }
978
979 RefPtr<Action>
980 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
981                                   Gtk::RadioAction::Group& rgroup,
982                                   const char* name, const char* label,
983                                   sigc::slot<void,GtkAction*> sl,
984                                   int value)
985 {
986         string fullpath;
987
988         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
989         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
990         ract->property_value() = value;
991
992         fullpath = group->get_name();
993         fullpath += '/';
994         fullpath += name;
995
996         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
997                 group->add (act, sigc::bind (sl, act->gobj()));
998                 return act;
999         }
1000
1001         /* already registered */
1002
1003         return RefPtr<Action>();
1004 }
1005
1006 RefPtr<Action>
1007 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1008                                    const char* name, const char* label, sigc::slot<void> sl)
1009 {
1010         string fullpath;
1011
1012         fullpath = group->get_name();
1013         fullpath += '/';
1014         fullpath += name;
1015
1016         RefPtr<Action> act = ToggleAction::create (name, label);
1017
1018         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1019                 group->add (act, sl);
1020                 return act;
1021         }
1022
1023         /* already registered */
1024         return RefPtr<Action>();
1025 }
1026
1027 void
1028 ActionMap::get_all_actions (std::vector<std::string>& paths,
1029                             std::vector<std::string>& labels,
1030                             std::vector<std::string>& tooltips,
1031                             std::vector<std::string>& keys,
1032                             std::vector<RefPtr<Action> >& actions)
1033 {
1034         for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1035
1036                 ActionMap::Actions these_actions;
1037                 (*map)->get_actions (these_actions);
1038
1039                 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1040
1041                         paths.push_back ((*act)->get_accel_path());
1042                         labels.push_back ((*act)->get_label());
1043                         tooltips.push_back ((*act)->get_tooltip());
1044                         actions.push_back (*act);
1045
1046                         Bindings* bindings = (*map)->bindings();
1047
1048                         if (bindings) {
1049
1050                                 KeyboardKey key;
1051                                 Bindings::Operation op;
1052
1053                                 key = bindings->get_binding_for_action (*act, op);
1054
1055                                 if (key == KeyboardKey::null_key()) {
1056                                         keys.push_back (string());
1057                                 } else {
1058                                         keys.push_back (key.display_label());
1059                                 }
1060                         } else {
1061                                 keys.push_back (string());
1062                         }
1063                 }
1064
1065                 these_actions.clear ();
1066         }
1067 }
1068
1069 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1070         char const *gdk_name = gdk_keyval_name (k.key());
1071         return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;
1072 }