use new keyboard modifier names, and replace GDK names with better versions
[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 "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         add (kb, op, action_name, can_save);
615         return true;
616 }
617
618 bool
619 Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save)
620 {
621         if (is_registered (op, action_name)) {
622                 return false;
623         }
624
625         KeybindingMap& kbm = get_keymap (op);
626
627         KeybindingMap::value_type new_pair (kb, ActionInfo (action_name));
628         kbm.insert (new_pair).first;
629
630         if (can_save) {
631                 Keyboard::keybindings_changed ();
632         }
633
634         BindingsChanged (this); /* EMIT SIGNAL */
635         return true;
636 }
637
638 bool
639 Bindings::remove (Operation op, std::string const& action_name, bool can_save)
640 {
641         bool erased_action = false;
642         KeybindingMap& kbm = get_keymap (op);
643         for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) {
644                 if (k->second.action_name == action_name) {
645                         kbm.erase (k);
646                         erased_action = true;
647                         break;
648                 }
649         }
650
651         if (!erased_action) {
652                 return erased_action;
653         }
654
655         if (can_save) {
656                 Keyboard::keybindings_changed ();
657         }
658
659         BindingsChanged (this); /* EMIT SIGNAL */
660         return erased_action;
661 }
662
663
664 bool
665 Bindings::activate (MouseButton bb, Operation op)
666 {
667         MouseButtonBindingMap& bbm = get_mousemap(op);
668
669         MouseButtonBindingMap::iterator b = bbm.find (bb);
670
671         if (b == bbm.end()) {
672                 /* no entry for this key in the state map */
673                 return false;
674         }
675
676         RefPtr<Action> action;
677
678         if (b->second.action) {
679                 action = b->second.action;
680         } else {
681                 if (_action_map) {
682                         action = _action_map->find_action (b->second.action_name);
683                 }
684         }
685
686         if (action) {
687                 /* lets do it ... */
688                 DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action)));
689                 action->activate ();
690         }
691
692         /* return true even if the action could not be found */
693
694         return true;
695 }
696
697 void
698 Bindings::add (MouseButton bb, Operation op, string const& action_name)
699 {
700         MouseButtonBindingMap& bbm = get_mousemap(op);
701
702         MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name));
703         bbm.insert (newpair);
704 }
705
706 void
707 Bindings::remove (MouseButton bb, Operation op)
708 {
709         MouseButtonBindingMap& bbm = get_mousemap(op);
710         MouseButtonBindingMap::iterator b = bbm.find (bb);
711
712         if (b != bbm.end()) {
713                 bbm.erase (b);
714         }
715 }
716
717 void
718 Bindings::save (XMLNode& root)
719 {
720         XMLNode* presses = new XMLNode (X_("Press"));
721
722         for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
723                 XMLNode* child;
724
725                 if (k->first.name().empty()) {
726                         continue;
727                 }
728
729                 child = new XMLNode (X_("Binding"));
730                 child->add_property (X_("key"), k->first.name());
731                 child->add_property (X_("action"), k->second.action_name);
732                 presses->add_child_nocopy (*child);
733         }
734
735         for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) {
736                 XMLNode* child;
737                 child = new XMLNode (X_("Binding"));
738                 child->add_property (X_("button"), k->first.name());
739                 child->add_property (X_("action"), k->second.action_name);
740                 presses->add_child_nocopy (*child);
741         }
742
743         XMLNode* releases = new XMLNode (X_("Release"));
744
745         for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
746                 XMLNode* child;
747
748                 if (k->first.name().empty()) {
749                         continue;
750                 }
751
752                 child = new XMLNode (X_("Binding"));
753                 child->add_property (X_("key"), k->first.name());
754                 child->add_property (X_("action"), k->second.action_name);
755                 releases->add_child_nocopy (*child);
756         }
757
758         for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) {
759                 XMLNode* child;
760                 child = new XMLNode (X_("Binding"));
761                 child->add_property (X_("button"), k->first.name());
762                 child->add_property (X_("action"), k->second.action_name);
763                 releases->add_child_nocopy (*child);
764         }
765
766         root.add_child_nocopy (*presses);
767         root.add_child_nocopy (*releases);
768 }
769
770 void
771 Bindings::save_all_bindings_as_html (ostream& ostr)
772 {
773         if (bindings.empty()) {
774                 return;
775         }
776
777
778         ostr << "<html>\n<head>\n<title>";
779         ostr << PROGRAM_NAME;
780         ostr << "</title>\n";
781
782
783         ostr << "<style>\n";
784         ostr << "\n\
785 .key-name-even, .key-name-odd\n\
786 {\n\
787     font-weight: bold;\n\
788 }\n\
789 \n\
790 .key-action-odd, .key-action-even\n\
791 {\n\
792     font-weight: normal;\n\
793     font-style: italic;\n\
794 }";
795         ostr << "</style>\n";
796
797         ostr << "</head>\n<body>\n";
798
799         ostr << "<div class=\"container\">\n";
800
801         for (list<Bindings*>::const_iterator b = bindings.begin(); b != bindings.end(); ++b) {
802                 (*b)->save_as_html (ostr);
803         }
804
805         ostr << "</div>\n";
806         ostr << "</body>\n";
807         ostr << "</html>\n";
808 }
809
810 void
811 Bindings::save_as_html (ostream& ostr) const
812 {
813
814         if (!press_bindings.empty()) {
815
816                 ostr << "<div><h1 class=\"binding-set-name\">\n";
817                 ostr << name();
818                 ostr << "</h1><table><tr><th>Shortcut</th><th>Operation</th></tr>\n";
819
820                 int row_count = 0;
821
822                 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
823                         if (k->first.name().empty()) {
824                                 continue;
825                         }
826
827                         RefPtr<Action> action;
828
829                         if (k->second.action) {
830                                 action = k->second.action;
831                         } else {
832                                 if (_action_map) {
833                                         action = _action_map->find_action (k->second.action_name);
834                                 }
835                         }
836
837                         if (!action) {
838                                 continue;
839                         }
840
841                         string key_name = k->first.native_short_name ();
842                         replace_all (key_name, X_("KP_"), X_("Numpad "));
843
844                         string::size_type pos;
845
846                         char const *targets[] = { X_("Separator"), X_("Add"), X_("Subtract"), X_("Decimal"), X_("Divide"),
847                                                   X_("grave"), X_("comma"), X_("period"), X_("asterisk"), X_("backslash"),
848                                                   X_("apostrophe"), X_("minus"), X_("plus"), X_("slash"), X_("semicolon"),
849                                                   X_("colon"), X_("equal"), X_("bracketleft"), X_("bracketright"),
850                                                   X_("ampersand"), X_("numbersign"), X_("parenleft"), X_("parenright"),
851                                                   X_("quoteright"), X_("quoteleft"), X_("exclam"), X_("quotedbl"),
852                                                         0
853                         };
854
855                         char const *replacements[] = { X_("-"), X_("+"), X_("-"), X_("."), X_("/"),
856                                                        X_("`"), X_(","), X_("."), X_("*"), X_("\\"),
857                                                        X_("'"), X_("-"), X_("+"), X_("/"), X_(";"),
858                                                        X_(":"), X_("="), X_("{"), X_("{"),
859                                                        X_("&"), X_("#"), X_("("), X_(")"),
860                                                        X_("`"), X_("'"), X_("!"), X_("\""),
861                         };
862
863                         for (size_t n = 0; targets[n]; ++n) {
864                                 if ((pos = key_name.find (targets[n])) != string::npos) {
865                                         key_name.replace (pos, strlen (targets[n]), replacements[n]);
866                                 }
867                         }
868
869                         if (row_count % 2) {
870                                 ostr << "<tr><td class=\"key-name-odd\">";
871                         } else {
872                                 ostr << "<tr><td class=\"key-name-even\">";
873                         }
874                         ostr << key_name;
875                         if (row_count % 2) {
876                                 ostr << "</td><td class=\"key-action-odd\">";
877                         } else {
878                                 ostr << "</td><td class=\"key-action-even\">";
879                         }
880                         ostr << action->get_label();
881                         ostr << "</td></tr>\n";
882                         row_count++;
883                 }
884
885                 ostr << "</table></div>\n";
886         }
887 }
888
889 bool
890 Bindings::load (XMLNode const& node)
891 {
892         const XMLNodeList& children (node.children());
893
894         press_bindings.clear ();
895         release_bindings.clear ();
896
897         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
898                 /* each node could be Press or Release */
899                 load_operation (**i);
900         }
901
902         return true;
903 }
904
905 void
906 Bindings::load_operation (XMLNode const& node)
907 {
908         if (node.name() == X_("Press") || node.name() == X_("Release")) {
909
910                 Operation op;
911
912                 if (node.name() == X_("Press")) {
913                         op = Press;
914                 } else {
915                         op = Release;
916                 }
917
918                 const XMLNodeList& children (node.children());
919
920                 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
921
922                         XMLProperty const * ap;
923                         XMLProperty const * kp;
924                         XMLProperty const * bp;
925                         XMLNode const * child = *p;
926
927                         ap = child->property ("action");
928                         kp = child->property ("key");
929                         bp = child->property ("button");
930
931                         if (!ap || (!kp && !bp)) {
932                                 continue;
933                         }
934
935                         if (kp) {
936                                 KeyboardKey k;
937                                 if (!KeyboardKey::make_key (kp->value(), k)) {
938                                         continue;
939                                 }
940                                 add (k, op, ap->value());
941                         } else {
942                                 MouseButton b;
943                                 if (!MouseButton::make_button (bp->value(), b)) {
944                                         continue;
945                                 }
946                                 add (b, op, ap->value());
947                         }
948                 }
949         }
950 }
951
952 void
953 Bindings::get_all_actions (std::vector<std::string>& paths,
954                            std::vector<std::string>& labels,
955                            std::vector<std::string>& tooltips,
956                            std::vector<std::string>& keys,
957                            std::vector<RefPtr<Action> >& actions)
958 {
959         if (!_action_map) {
960                 return;
961         }
962
963         /* build a reverse map from actions to bindings */
964
965         typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
966         ReverseMap rmap;
967
968         for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
969                 rmap.insert (make_pair (k->second.action, k->first));
970         }
971
972         /* get a list of all actions */
973
974         ActionMap::Actions all_actions;
975         _action_map->get_actions (all_actions);
976
977         for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
978
979                 paths.push_back ((*act)->get_accel_path());
980                 labels.push_back ((*act)->get_label());
981                 tooltips.push_back ((*act)->get_tooltip());
982
983                 ReverseMap::iterator r = rmap.find (*act);
984
985                 if (r != rmap.end()) {
986                         keys.push_back (r->second.display_label());
987                 } else {
988                         keys.push_back (string());
989                 }
990
991                 actions.push_back (*act);
992         }
993 }
994
995 Bindings*
996 Bindings::get_bindings (string const& name, ActionMap& map)
997 {
998         for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
999                 if ((*b)->name() == name) {
1000                         (*b)->set_action_map (map);
1001                         return *b;
1002                 }
1003         }
1004
1005         return 0;
1006 }
1007
1008 void
1009 Bindings::associate_all ()
1010 {
1011         for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
1012                 (*b)->associate ();
1013         }
1014 }
1015
1016 bool
1017 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
1018 {
1019         const KeybindingMap& km = get_keymap(op);
1020         return km.find(kb) != km.end();
1021 }
1022
1023 bool
1024 Bindings::is_registered (Operation op, std::string const& action_name) const
1025 {
1026         const KeybindingMap& km = get_keymap(op);
1027         return std::find_if(km.begin(),  km.end(),  ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
1028 }
1029
1030 Bindings::KeybindingMap&
1031 Bindings::get_keymap (Operation op)
1032 {
1033         switch (op) {
1034         case Press:
1035                 return press_bindings;
1036         case Release:
1037         default:
1038                 return release_bindings;
1039         }
1040 }
1041
1042 const Bindings::KeybindingMap&
1043 Bindings::get_keymap (Operation op) const
1044 {
1045         switch (op) {
1046         case Press:
1047                 return press_bindings;
1048         case Release:
1049         default:
1050                 return release_bindings;
1051         }
1052 }
1053
1054 Bindings::MouseButtonBindingMap&
1055 Bindings::get_mousemap (Operation op)
1056 {
1057         switch (op) {
1058         case Press:
1059                 return button_press_bindings;
1060         case Release:
1061         default:
1062                 return button_release_bindings;
1063         }
1064 }
1065
1066 /*==========================================ACTION MAP =========================================*/
1067
1068 ActionMap::ActionMap (string const & name)
1069         : _name (name)
1070         , _bindings (0)
1071 {
1072         action_maps.push_back (this);
1073 }
1074
1075 ActionMap::~ActionMap ()
1076 {
1077         action_maps.remove (this);
1078 }
1079
1080 void
1081 ActionMap::set_bindings (Bindings* b)
1082 {
1083         _bindings = b;
1084 }
1085
1086 void
1087 ActionMap::get_actions (ActionMap::Actions& acts)
1088 {
1089         for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
1090                 acts.push_back (a->second);
1091         }
1092 }
1093
1094 RefPtr<Action>
1095 ActionMap::find_action (const string& name)
1096 {
1097         _ActionMap::iterator a = _actions.find (name);
1098
1099         if (a != _actions.end()) {
1100                 return a->second;
1101         }
1102
1103         return RefPtr<Action>();
1104 }
1105
1106 RefPtr<ActionGroup>
1107 ActionMap::create_action_group (const string& name)
1108 {
1109         RefPtr<ActionGroup> g = ActionGroup::create (name);
1110
1111         /* this is one of the places where our own Action management code
1112            has to touch the GTK one, because we want the GtkUIManager to
1113            be able to create widgets (particularly Menus) from our actions.
1114
1115            This is a a necessary step for that to happen.
1116         */
1117
1118         if (g) {
1119                 ActionManager::ui_manager->insert_action_group (g);
1120         }
1121
1122         return g;
1123 }
1124
1125 RefPtr<Action>
1126 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
1127 {
1128         string fullpath;
1129
1130         RefPtr<Action> act = Action::create (name, label);
1131
1132         fullpath = group->get_name();
1133         fullpath += '/';
1134         fullpath += name;
1135
1136         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1137                 group->add (act);
1138                 return act;
1139         }
1140
1141         /* already registered */
1142         return RefPtr<Action> ();
1143 }
1144
1145 RefPtr<Action>
1146 ActionMap::register_action (RefPtr<ActionGroup> group,
1147                             const char* name, const char* label, sigc::slot<void> sl)
1148 {
1149         string fullpath;
1150
1151         RefPtr<Action> act = Action::create (name, label);
1152
1153         fullpath = group->get_name();
1154         fullpath += '/';
1155         fullpath += name;
1156
1157         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1158                 group->add (act, sl);
1159                 return act;
1160         }
1161
1162         /* already registered */
1163         return RefPtr<Action>();
1164 }
1165
1166 RefPtr<Action>
1167 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1168                                   Gtk::RadioAction::Group& rgroup,
1169                                   const char* name, const char* label,
1170                                   sigc::slot<void> sl)
1171 {
1172         string fullpath;
1173
1174         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1175         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1176
1177         fullpath = group->get_name();
1178         fullpath += '/';
1179         fullpath += name;
1180
1181         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1182                 group->add (act, sl);
1183                 return act;
1184         }
1185
1186         /* already registered */
1187         return RefPtr<Action>();
1188 }
1189
1190 RefPtr<Action>
1191 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1192                                   Gtk::RadioAction::Group& rgroup,
1193                                   const char* name, const char* label,
1194                                   sigc::slot<void,GtkAction*> sl,
1195                                   int value)
1196 {
1197         string fullpath;
1198
1199         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1200         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1201         ract->property_value() = value;
1202
1203         fullpath = group->get_name();
1204         fullpath += '/';
1205         fullpath += name;
1206
1207         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1208                 group->add (act, sigc::bind (sl, act->gobj()));
1209                 return act;
1210         }
1211
1212         /* already registered */
1213
1214         return RefPtr<Action>();
1215 }
1216
1217 RefPtr<Action>
1218 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1219                                    const char* name, const char* label, sigc::slot<void> sl)
1220 {
1221         string fullpath;
1222
1223         fullpath = group->get_name();
1224         fullpath += '/';
1225         fullpath += name;
1226
1227         RefPtr<Action> act = ToggleAction::create (name, label);
1228
1229         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1230                 group->add (act, sl);
1231                 return act;
1232         }
1233
1234         /* already registered */
1235         return RefPtr<Action>();
1236 }
1237
1238 void
1239 ActionMap::get_all_actions (std::vector<std::string>& paths,
1240                             std::vector<std::string>& labels,
1241                             std::vector<std::string>& tooltips,
1242                             std::vector<std::string>& keys,
1243                             std::vector<RefPtr<Action> >& actions)
1244 {
1245         for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1246
1247                 ActionMap::Actions these_actions;
1248                 (*map)->get_actions (these_actions);
1249
1250                 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1251
1252                         paths.push_back ((*act)->get_accel_path());
1253                         labels.push_back ((*act)->get_label());
1254                         tooltips.push_back ((*act)->get_tooltip());
1255                         actions.push_back (*act);
1256
1257                         Bindings* bindings = (*map)->bindings();
1258
1259                         if (bindings) {
1260
1261                                 KeyboardKey key;
1262                                 Bindings::Operation op;
1263
1264                                 key = bindings->get_binding_for_action (*act, op);
1265
1266                                 if (key == KeyboardKey::null_key()) {
1267                                         keys.push_back (string());
1268                                 } else {
1269                                         keys.push_back (key.display_label());
1270                                 }
1271                         } else {
1272                                 keys.push_back (string());
1273                         }
1274                 }
1275
1276                 these_actions.clear ();
1277         }
1278 }
1279
1280 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1281         char const *gdk_name = gdk_keyval_name (k.key());
1282         return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec;
1283 }