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