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