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