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