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