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