prepare strict-i/o configuration.
[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         if (str.size() == 1) {
268                 actual = PBD::downcase (str);
269         } else {
270                 actual = str;
271         }
272
273         string::size_type lastmod = actual.find_last_of ('-');
274         guint keyval;
275
276         if (lastmod != string::npos) {
277                 actual = PBD::downcase (str.substr (lastmod+1));
278         }
279
280         keyval = gdk_keyval_from_name (actual.c_str());
281
282         if (keyval == GDK_VoidSymbol || keyval == 0) {
283                 return false;
284         }
285
286         k = KeyboardKey (s, keyval);
287
288         return true;
289 }
290
291 /*================================= Bindings =================================*/
292 Bindings::Bindings (std::string const& name)
293         : _name (name)
294         , _action_map (0)
295 {
296         bindings.push_back (this);
297 }
298
299 Bindings::~Bindings()
300 {
301         bindings.remove (this);
302 }
303
304 string
305 Bindings::ardour_action_name (RefPtr<Action> action)
306 {
307         /* Skip "<Actions>/" */
308         return action->get_accel_path ().substr (10);
309 }
310
311 KeyboardKey
312 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
313 {
314         const string action_name = ardour_action_name (action);
315
316         for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
317
318                 /* option one: action has already been associated with the
319                  * binding
320                  */
321
322                 if (k->second.action == action) {
323                         return k->first;
324                 }
325
326                 /* option two: action name matches, so lookup the action,
327                  * setup the association while we're here, and return the binding.
328                  */
329
330                 if (_action_map && k->second.action_name == action_name) {
331                         k->second.action = _action_map->find_action (action_name);
332                         return k->first;
333                 }
334
335         }
336
337         for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
338
339                 /* option one: action has already been associated with the
340                  * binding
341                  */
342
343                 if (k->second.action == action) {
344                         return k->first;
345                 }
346
347                 /* option two: action name matches, so lookup the action,
348                  * setup the association while we're here, and return the binding.
349                  */
350
351                 if (_action_map && k->second.action_name == action_name) {
352                         k->second.action = _action_map->find_action (action_name);
353                         return k->first;
354                 }
355
356         }
357
358         return KeyboardKey::null_key();
359 }
360
361 void
362 Bindings::set_action_map (ActionMap& actions)
363 {
364         if (_action_map) {
365                 _action_map->set_bindings (0);
366         }
367
368         _action_map = &actions;
369         _action_map->set_bindings (this);
370
371         dissociate ();
372         associate ();
373 }
374
375 bool
376 Bindings::empty_keys() const
377 {
378         return press_bindings.empty() && release_bindings.empty();
379 }
380
381 bool
382 Bindings::empty_mouse () const
383 {
384         return button_press_bindings.empty() && button_release_bindings.empty();
385 }
386
387 bool
388 Bindings::empty() const
389 {
390         return empty_keys() && empty_mouse ();
391 }
392
393 bool
394 Bindings::activate (KeyboardKey kb, Operation op)
395 {
396         KeybindingMap& kbm = get_keymap (op);
397
398         /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'.
399            Our bindings all use the lower case character/keyname, so switch
400            to the lower case before doing the lookup.
401         */
402
403         KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key()));
404
405         KeybindingMap::iterator k = kbm.find (unshifted);
406
407         if (k == kbm.end()) {
408                 /* no entry for this key in the state map */
409                 DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted));
410                 return false;
411         }
412
413         RefPtr<Action> action;
414
415         if (k->second.action) {
416                 action = k->second.action;
417         } else {
418                 if (_action_map) {
419                         action = _action_map->find_action (k->second.action_name);
420                 }
421         }
422
423         if (action) {
424                 /* lets do it ... */
425                 DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name));
426                 action->activate ();
427         }
428
429         /* return true even if the action could not be found */
430
431         return true;
432 }
433
434 void
435 Bindings::associate ()
436 {
437         KeybindingMap::iterator k;
438
439         if (!_action_map) {
440                 return;
441         }
442
443         for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
444                 k->second.action = _action_map->find_action (k->second.action_name);
445                 if (k->second.action) {
446                         push_to_gtk (k->first, k->second.action);
447                 } else {
448                         cerr << _name << " didn't find " << k->second.action_name << " in " << _action_map->name() << endl;
449                 }
450         }
451
452         for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
453                 k->second.action = _action_map->find_action (k->second.action_name);
454                 /* no working support in GTK for release bindings */
455         }
456
457         MouseButtonBindingMap::iterator b;
458
459         for (b = button_press_bindings.begin(); b != button_press_bindings.end(); ++b) {
460                 b->second.action = _action_map->find_action (b->second.action_name);
461         }
462
463         for (b = button_release_bindings.begin(); b != button_release_bindings.end(); ++b) {
464                 b->second.action = _action_map->find_action (b->second.action_name);
465         }
466 }
467
468 void
469 Bindings::dissociate ()
470 {
471         KeybindingMap::iterator k;
472
473         for (k = press_bindings.begin(); k != press_bindings.end(); ++k) {
474                 k->second.action.clear ();
475         }
476         for (k = release_bindings.begin(); k != release_bindings.end(); ++k) {
477                 k->second.action.clear ();
478         }
479 }
480
481 void
482 Bindings::push_to_gtk (KeyboardKey kb, RefPtr<Action> what)
483 {
484         /* GTK has the useful feature of showing key bindings for actions in
485          * menus. As of August 2015, we have no interest in trying to
486          * reimplement this functionality, so we will use it even though we no
487          * longer use GTK accelerators for handling key events. To do this, we
488          * need to make sure that there is a fully populated GTK AccelMap set
489          * up with all bindings/actions.
490          */
491
492         Gtk::AccelKey gtk_key;
493         bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key);
494
495         if (!entry_exists) {
496
497                 /* there is a trick happening here. It turns out that
498                  * gtk_accel_map_add_entry() performs no validation checks on
499                  * the accelerator keyval. This means we can use it to define
500                  * ANY accelerator, even if they violate GTK's rules
501                  * (e.g. about not using navigation keys). This works ONLY when
502                  * the entry in the GTK accelerator map has not already been
503                  * added. The entries will be added by the GTK UIManager when
504                  * building menus, so this code must be called before that
505                  * happens.
506                  */
507
508                 Gtk::AccelMap::add_entry (what->get_accel_path(), kb.key(), (Gdk::ModifierType) kb.state());
509         }
510 }
511
512 bool
513 Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save)
514 {
515         if (!_action_map) {
516                 return false;
517         }
518
519         if (is_registered(op, action_name)) {
520                 remove(op, action_name, can_save);
521         }
522         add (kb, op, action_name, can_save);
523         return true;
524 }
525
526 bool
527 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
528 {
529         if (is_registered(op, action_name)) {
530                 return false;
531         }
532
533         KeybindingMap& kbm = get_keymap (op);
534
535         KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
536         kbm.insert (new_pair).first;
537
538         if (can_save) {
539                 Keyboard::keybindings_changed ();
540         }
541
542         BindingsChanged (this); /* EMIT SIGNAL */
543         return true;
544 }
545
546 bool
547 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
548 {
549         bool erased_action = false;
550         KeybindingMap& kbm = get_keymap (op);
551         for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
552                 if (k->second.action_name == action_name) {
553                         kbm.erase (k);
554                         erased_action = true;
555                         break;
556                 }
557         }
558
559         if (!erased_action) {
560                 return erased_action;
561         }
562
563         if (can_save) {
564                 Keyboard::keybindings_changed ();
565         }
566
567         BindingsChanged (this); /* EMIT SIGNAL */
568         return erased_action;
569 }
570
571
572 bool
573 Bindings::activate (MouseButton bb, Operation op)
574 {
575         MouseButtonBindingMap& bbm = get_mousemap(op);
576
577         MouseButtonBindingMap::iterator b = bbm.find (bb);
578
579         if (b == bbm.end()) {
580                 /* no entry for this key in the state map */
581                 return false;
582         }
583
584         RefPtr<Action> action;
585
586         if (b->second.action) {
587                 action = b->second.action;
588         } else {
589                 if (_action_map) {
590                         action = _action_map->find_action (b->second.action_name);
591                 }
592         }
593
594         if (action) {
595                 /* lets do it ... */
596                 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
597                 action->activate ();
598         }
599
600         /* return true even if the action could not be found */
601
602         return true;
603 }
604
605 void
606 Bindings::add (MouseButton bb, Operation op, string const& action_name)
607 {
608         MouseButtonBindingMap& bbm = get_mousemap(op);
609
610         MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
611         bbm.insert (newpair);
612 }
613
614 void
615 Bindings::remove (MouseButton bb, Operation op)
616 {
617         MouseButtonBindingMap& bbm = get_mousemap(op);
618         MouseButtonBindingMap::iterator b = bbm.find (bb);
619
620         if (b != bbm.end()) {
621                 bbm.erase (b);
622         }
623 }
624
625 void
626 Bindings::save (XMLNode& root)
627 {
628         XMLNode* presses = new XMLNode (X_("Press"));
629
630         for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
631                 XMLNode* child;
632
633                 if (k->first.name().empty()) {
634                         continue;
635                 }
636
637                 child = new XMLNode (X_("Binding"));
638                 child->add_property (X_("key"), k->first.name());
639                 child->add_property (X_("action"), k->second.action_name);
640                 presses->add_child_nocopy (*child);
641         }
642
643         for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
644                 XMLNode* child;
645                 child = new XMLNode (X_("Binding"));
646                 child->add_property (X_("button"), k->first.name());
647                 child->add_property (X_("action"), k->second.action_name);
648                 presses->add_child_nocopy (*child);
649         }
650
651         XMLNode* releases = new XMLNode (X_("Release"));
652
653         for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
654                 XMLNode* child;
655
656                 if (k->first.name().empty()) {
657                         continue;
658                 }
659
660                 child = new XMLNode (X_("Binding"));
661                 child->add_property (X_("key"), k->first.name());
662                 child->add_property (X_("action"), k->second.action_name);
663                 releases->add_child_nocopy (*child);
664         }
665
666         for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
667                 XMLNode* child;
668                 child = new XMLNode (X_("Binding"));
669                 child->add_property (X_("button"), k->first.name());
670                 child->add_property (X_("action"), k->second.action_name);
671                 releases->add_child_nocopy (*child);
672         }
673
674         root.add_child_nocopy (*presses);
675         root.add_child_nocopy (*releases);
676 }
677
678 bool
679 Bindings::load (XMLNode const& node)
680 {
681         const XMLNodeList& children (node.children());
682
683         press_bindings.clear ();
684         release_bindings.clear ();
685
686         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
687                 /* each node could be Press or Release */
688                 load_operation (**i);
689         }
690
691         return true;
692 }
693
694 void
695 Bindings::load_operation (XMLNode const& node)
696 {
697         if (node.name() == X_("Press") || node.name() == X_("Release")) {
698
699                 Operation op;
700
701                 if (node.name() == X_("Press")) {
702                         op = Press;
703                 } else {
704                         op = Release;
705                 }
706
707                 const XMLNodeList& children (node.children());
708
709                 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
710
711                         XMLProperty* ap;
712                         XMLProperty* kp;
713                         XMLProperty* bp;
714
715                         ap = (*p)->property ("action");
716                         kp = (*p)->property ("key");
717                         bp = (*p)->property ("button");
718
719                         if (!ap || (!kp && !bp)) {
720                                 continue;
721                         }
722
723                         if (kp) {
724                                 KeyboardKey k;
725                                 if (!KeyboardKey::make_key (kp->value(), k)) {
726                                         continue;
727                                 }
728                                 add (k, op, ap->value());
729                         } else {
730                                 MouseButton b;
731                                 if (!MouseButton::make_button (bp->value(), b)) {
732                                         continue;
733                                 }
734                                 add (b, op, ap->value());
735                         }
736                 }
737         }
738 }
739
740 void
741 Bindings::get_all_actions (std::vector<std::string>& paths,
742                            std::vector<std::string>& labels,
743                            std::vector<std::string>& tooltips,
744                            std::vector<std::string>& keys,
745                            std::vector<RefPtr<Action> >& actions)
746 {
747         if (!_action_map) {
748                 return;
749         }
750
751         /* build a reverse map from actions to bindings */
752
753         typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
754         ReverseMap rmap;
755
756         for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
757                 rmap.insert (make_pair (k->second.action, k->first));
758         }
759
760         /* get a list of all actions */
761
762         ActionMap::Actions all_actions;
763         _action_map->get_actions (all_actions);
764
765         for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
766
767                 paths.push_back ((*act)->get_accel_path());
768                 labels.push_back ((*act)->get_label());
769                 tooltips.push_back ((*act)->get_tooltip());
770
771                 ReverseMap::iterator r = rmap.find (*act);
772
773                 if (r != rmap.end()) {
774                         keys.push_back (r->second.display_label());
775                 } else {
776                         keys.push_back (string());
777                 }
778
779                 actions.push_back (*act);
780         }
781 }
782
783 Bindings*
784 Bindings::get_bindings (string const& name, ActionMap& map)
785 {
786         for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
787                 if ((*b)->name() == name) {
788                         (*b)->set_action_map (map);
789                         return *b;
790                 }
791         }
792
793         return 0;
794 }
795
796 void
797 Bindings::associate_all ()
798 {
799         for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
800                 (*b)->associate ();
801         }
802 }
803
804 bool
805 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
806 {
807         const KeybindingMap& km = get_keymap(op);
808         return km.find(kb) != km.end();
809 }
810
811 bool
812 Bindings::is_registered (Operation op, std::string const& action_name) const
813 {
814         const KeybindingMap& km = get_keymap(op);
815         return std::find_if(km.begin(),  km.end(),  ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
816 }
817
818 Bindings::KeybindingMap&
819 Bindings::get_keymap (Operation op)
820 {
821         switch (op) {
822         case Press:
823                 return press_bindings;
824         case Release:
825         default:
826                 return release_bindings;
827         }
828 }
829
830 const Bindings::KeybindingMap&
831 Bindings::get_keymap (Operation op) const
832 {
833         switch (op) {
834         case Press:
835                 return press_bindings;
836         case Release:
837         default:
838                 return release_bindings;
839         }
840 }
841
842 Bindings::MouseButtonBindingMap&
843 Bindings::get_mousemap (Operation op)
844 {
845         switch (op) {
846         case Press:
847                 return button_press_bindings;
848         case Release:
849         default:
850                 return button_release_bindings;
851         }
852 }
853
854 /*==========================================ACTION MAP =========================================*/
855
856 ActionMap::ActionMap (string const & name)
857         : _name (name)
858         , _bindings (0)
859 {
860         action_maps.push_back (this);
861 }
862
863 ActionMap::~ActionMap ()
864 {
865         action_maps.remove (this);
866 }
867
868 void
869 ActionMap::set_bindings (Bindings* b)
870 {
871         _bindings = b;
872 }
873
874 void
875 ActionMap::get_actions (ActionMap::Actions& acts)
876 {
877         for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
878                 acts.push_back (a->second);
879         }
880 }
881
882 RefPtr<Action>
883 ActionMap::find_action (const string& name)
884 {
885         _ActionMap::iterator a = _actions.find (name);
886
887         if (a != _actions.end()) {
888                 return a->second;
889         }
890
891         return RefPtr<Action>();
892 }
893
894 RefPtr<ActionGroup>
895 ActionMap::create_action_group (const string& name)
896 {
897         RefPtr<ActionGroup> g = ActionGroup::create (name);
898
899         /* this is one of the places where our own Action management code
900            has to touch the GTK one, because we want the GtkUIManager to
901            be able to create widgets (particularly Menus) from our actions.
902
903            This is a a necessary step for that to happen.
904         */
905
906         if (g) {
907                 ActionManager::ui_manager->insert_action_group (g);
908         }
909
910         return g;
911 }
912
913 RefPtr<Action>
914 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
915 {
916         string fullpath;
917
918         RefPtr<Action> act = Action::create (name, label);
919
920         fullpath = group->get_name();
921         fullpath += '/';
922         fullpath += name;
923
924         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
925                 group->add (act);
926                 return act;
927         }
928
929         /* already registered */
930         return RefPtr<Action> ();
931 }
932
933 RefPtr<Action>
934 ActionMap::register_action (RefPtr<ActionGroup> group,
935                             const char* name, const char* label, sigc::slot<void> sl)
936 {
937         string fullpath;
938
939         RefPtr<Action> act = Action::create (name, label);
940
941         fullpath = group->get_name();
942         fullpath += '/';
943         fullpath += name;
944
945         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
946                 group->add (act, sl);
947                 return act;
948         }
949
950         /* already registered */
951         return RefPtr<Action>();
952 }
953
954 RefPtr<Action>
955 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
956                                   Gtk::RadioAction::Group& rgroup,
957                                   const char* name, const char* label,
958                                   sigc::slot<void> sl)
959 {
960         string fullpath;
961
962         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
963         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
964
965         fullpath = group->get_name();
966         fullpath += '/';
967         fullpath += name;
968
969         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
970                 group->add (act, sl);
971                 return act;
972         }
973
974         /* already registered */
975         return RefPtr<Action>();
976 }
977
978 RefPtr<Action>
979 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
980                                   Gtk::RadioAction::Group& rgroup,
981                                   const char* name, const char* label,
982                                   sigc::slot<void,GtkAction*> sl,
983                                   int value)
984 {
985         string fullpath;
986
987         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
988         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
989         ract->property_value() = value;
990
991         fullpath = group->get_name();
992         fullpath += '/';
993         fullpath += name;
994
995         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
996                 group->add (act, sigc::bind (sl, act->gobj()));
997                 return act;
998         }
999
1000         /* already registered */
1001
1002         return RefPtr<Action>();
1003 }
1004
1005 RefPtr<Action>
1006 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1007                                    const char* name, const char* label, sigc::slot<void> sl)
1008 {
1009         string fullpath;
1010
1011         fullpath = group->get_name();
1012         fullpath += '/';
1013         fullpath += name;
1014
1015         RefPtr<Action> act = ToggleAction::create (name, label);
1016
1017         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1018                 group->add (act, sl);
1019                 return act;
1020         }
1021
1022         /* already registered */
1023         return RefPtr<Action>();
1024 }
1025
1026 void
1027 ActionMap::get_all_actions (std::vector<std::string>& paths,
1028                             std::vector<std::string>& labels,
1029                             std::vector<std::string>& tooltips,
1030                             std::vector<std::string>& keys,
1031                             std::vector<RefPtr<Action> >& actions)
1032 {
1033         for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1034
1035                 ActionMap::Actions these_actions;
1036                 (*map)->get_actions (these_actions);
1037
1038                 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1039
1040                         paths.push_back ((*act)->get_accel_path());
1041                         labels.push_back ((*act)->get_label());
1042                         tooltips.push_back ((*act)->get_tooltip());
1043                         actions.push_back (*act);
1044
1045                         Bindings* bindings = (*map)->bindings();
1046
1047                         if (bindings) {
1048
1049                                 KeyboardKey key;
1050                                 Bindings::Operation op;
1051
1052                                 key = bindings->get_binding_for_action (*act, op);
1053
1054                                 if (key == KeyboardKey::null_key()) {
1055                                         keys.push_back (string());
1056                                 } else {
1057                                         keys.push_back (key.display_label());
1058                                 }
1059                         } else {
1060                                 keys.push_back (string());
1061                         }
1062                 }
1063
1064                 these_actions.clear ();
1065         }
1066 }
1067
1068 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1069         char const *gdk_name = gdk_keyval_name (k.key());
1070         return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;
1071 }