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