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