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