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