Small refactoring of keyboard bindings (first part)
[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
50 /*============================ ActionNameRegistered ===========================*/
51 template <typename IteratorValueType>
52 struct ActionNameRegistered
53 {
54         ActionNameRegistered(std::string const& name)
55         : action_name(name)
56         {}
57
58         bool operator()(IteratorValueType elem) const {
59                 return elem.second.action_name == action_name;
60         }
61         std::string const& action_name;
62 };
63
64 /*================================ MouseButton ================================*/
65 MouseButton::MouseButton (uint32_t state, uint32_t keycode)
66 {
67         uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
68
69         /* this is a slightly wierd test that relies on
70          * gdk_keyval_is_{upper,lower}() returning true for keys that have no
71          * case-sensitivity. This covers mostly non-alphanumeric keys.
72          */
73
74         if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
75                 /* key is not subject to case, so ignore SHIFT
76                  */
77                 ignore |= GDK_SHIFT_MASK;
78         }
79
80         _val = (state & ~ignore);
81         _val <<= 32;
82         _val |= keycode;
83 };
84
85 bool
86 MouseButton::make_button (const string& str, MouseButton& b)
87 {
88         int s = 0;
89
90         if (str.find ("Primary") != string::npos) {
91                 s |= Keyboard::PrimaryModifier;
92         }
93
94         if (str.find ("Secondary") != string::npos) {
95                 s |= Keyboard::SecondaryModifier;
96         }
97
98         if (str.find ("Tertiary") != string::npos) {
99                 s |= Keyboard::TertiaryModifier;
100         }
101
102         if (str.find ("Level4") != string::npos) {
103                 s |= Keyboard::Level4Modifier;
104         }
105
106         string::size_type lastmod = str.find_last_of ('-');
107         uint32_t button_number;
108
109         if (lastmod == string::npos) {
110                 button_number = PBD::atoi (str);
111         } else {
112                 button_number = PBD::atoi (str.substr (lastmod+1));
113         }
114
115         b = MouseButton (s, button_number);
116         return true;
117 }
118
119 string
120 MouseButton::name () const
121 {
122         int s = state();
123
124         string str;
125
126         if (s & Keyboard::PrimaryModifier) {
127                 str += "Primary";
128         }
129         if (s & Keyboard::SecondaryModifier) {
130                 if (!str.empty()) {
131                         str += '-';
132                 }
133                 str += "Secondary";
134         }
135         if (s & Keyboard::TertiaryModifier) {
136                 if (!str.empty()) {
137                         str += '-';
138                 }
139                 str += "Tertiary";
140         }
141         if (s & Keyboard::Level4Modifier) {
142                 if (!str.empty()) {
143                         str += '-';
144                 }
145                 str += "Level4";
146         }
147
148         if (!str.empty()) {
149                 str += '-';
150         }
151
152         char buf[16];
153         snprintf (buf, sizeof (buf), "%u", button());
154         str += buf;
155
156         return str;
157 }
158
159 /*================================ KeyboardKey ================================*/
160 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
161 {
162         uint32_t ignore = ~Keyboard::RelevantModifierKeyMask;
163
164         _val = (state & ~ignore);
165         _val <<= 32;
166         _val |= keycode;
167 }
168
169 string
170 KeyboardKey::display_label () const
171 {
172         if (key() == 0) {
173                 return string();
174         }
175
176         /* This magically returns a string that will display the right thing
177          *  on all platforms, notably the command key on OS X.
178          */
179
180         uint32_t mod = state();
181
182 #ifdef __APPLE__
183                 /* We use both bits (MOD2|META) for Primary on OS X,
184                  * but we don't want MOD2 showing up in listings.
185                  */
186
187                 if (mod & GDK_MOD2_MASK) {
188                         mod &= ~GDK_MOD2_MASK;
189                 }
190 #endif
191
192         return gtk_accelerator_get_label (key(), (GdkModifierType) mod);
193 }
194
195 string
196 KeyboardKey::name () const
197 {
198         int s = state();
199
200         string str;
201
202         if (s & Keyboard::PrimaryModifier) {
203                 str += "Primary";
204         }
205         if (s & Keyboard::SecondaryModifier) {
206                 if (!str.empty()) {
207                         str += '-';
208                 }
209                 str += "Secondary";
210         }
211         if (s & Keyboard::TertiaryModifier) {
212                 if (!str.empty()) {
213                         str += '-';
214                 }
215                 str += "Tertiary";
216         }
217         if (s & Keyboard::Level4Modifier) {
218                 if (!str.empty()) {
219                         str += '-';
220                 }
221                 str += "Level4";
222         }
223
224         if (!str.empty()) {
225                 str += '-';
226         }
227
228         char const *gdk_name = gdk_keyval_name (key());
229
230         if (gdk_name) {
231                 str += gdk_name;
232         } else {
233                 /* fail! */
234                 return string();
235         }
236
237         return str;
238 }
239
240 bool
241 KeyboardKey::make_key (const string& str, KeyboardKey& k)
242 {
243         int s = 0;
244
245         if (str.find ("Primary") != string::npos) {
246                 s |= Keyboard::PrimaryModifier;
247         }
248
249         if (str.find ("Secondary") != string::npos) {
250                 s |= Keyboard::SecondaryModifier;
251         }
252
253         if (str.find ("Tertiary") != string::npos) {
254                 s |= Keyboard::TertiaryModifier;
255         }
256
257         if (str.find ("Level4") != string::npos) {
258                 s |= Keyboard::Level4Modifier;
259         }
260
261         string::size_type lastmod = str.find_last_of ('-');
262         guint keyval;
263
264         /* since all key events keycodes are changed to lower case before
265          * looking them up, make sure we only store lower case here. The Shift
266          * part will be stored in the modifier part of the KeyboardKey.
267          *
268          * And yes Mildred, this doesn't cover CapsLock cases. Oh well.
269          */
270
271         string lower;
272
273         if (lastmod == string::npos) {
274                 lower = PBD::downcase (str);
275         } else {
276                 lower = PBD::downcase (str.substr (lastmod+1));
277         }
278
279         keyval = gdk_keyval_from_name (lower.c_str());
280
281         if (keyval == GDK_VoidSymbol || keyval == 0) {
282                 return false;
283         }
284
285         k = KeyboardKey (s, keyval);
286
287         return true;
288 }
289
290 /*================================= Bindings =================================*/
291 Bindings::Bindings (std::string const& name)
292         : _name (name)
293         , _action_map (0)
294 {
295         bindings.push_back (this);
296 }
297
298 Bindings::~Bindings()
299 {
300         bindings.remove (this);
301 }
302
303 string
304 Bindings::ardour_action_name (RefPtr<Action> action)
305 {
306         /* Skip "<Actions>/" */
307         return action->get_accel_path ().substr (10);
308 }
309
310 KeyboardKey
311 Bindings::get_binding_for_action (RefPtr<Action> action, Operation& op)
312 {
313         const string action_name = ardour_action_name (action);
314
315         for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
316
317                 /* option one: action has already been associated with the
318                  * binding
319                  */
320
321                 if (k->second.action == action) {
322                         return k->first;
323                 }
324
325                 /* option two: action name matches, so lookup the action,
326                  * setup the association while we're here, and return the binding.
327                  */
328
329                 if (_action_map && k->second.action_name == action_name) {
330                         k->second.action = _action_map->find_action (action_name);
331                         return k->first;
332                 }
333
334         }
335
336         for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
337
338                 /* option one: action has already been associated with the
339                  * binding
340                  */
341
342                 if (k->second.action == action) {
343                         return k->first;
344                 }
345
346                 /* option two: action name matches, so lookup the action,
347                  * setup the association while we're here, and return the binding.
348                  */
349
350                 if (_action_map && k->second.action_name == action_name) {
351                          k->second.action = _action_map->find_action (action_name);
352                          return k->first;
353                  }
354
355         }
356
357         return KeyboardKey::null_key();
358 }
359
360 void
361 Bindings::set_action_map (ActionMap& actions)
362 {
363         if (_action_map) {
364                 _action_map->set_bindings (0);
365         }
366
367         _action_map = &actions;
368         _action_map->set_bindings (this);
369
370         dissociate ();
371         associate ();
372 }
373
374 bool
375 Bindings::empty_keys() const
376 {
377         return press_bindings.empty() && release_bindings.empty();
378 }
379
380 bool
381 Bindings::empty_mouse () const
382 {
383         return button_press_bindings.empty() && button_release_bindings.empty();
384 }
385
386 bool
387 Bindings::empty() const
388 {
389         return empty_keys() && empty_mouse ();
390 }
391
392 bool
393 Bindings::activate (KeyboardKey kb, Operation op)
394 {
395         KeybindingMap& kbm = get_keymap (op);
396
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 }