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