291cd6068693a128ea5cb600fb6a62685111109c
[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                         if ((*k).empty()) {
829                                 ostr << *p  << " ( " << *l << " ) "  << "</br>" << endl;
830                         } else {
831                                 ostr << *p << " ( " << *l << " ) " << " => " << *k << "</br>" << endl;
832                         }
833                 }
834         }
835         ostr << "</td>\n\n";
836         ostr << "</tr>\n\n";
837         ostr << "</tbody></table>\n\n";
838         
839         ostr << "</body>\n";
840         ostr << "</html>\n";
841 }
842
843 void
844 Bindings::save_as_html (ostream& ostr, bool categorize) const
845 {
846
847         if (!press_bindings.empty()) {
848
849                 ostr << "<h2><u>";
850                 if (categorize)
851                         ostr << _("Window") << ": " << name() << _(" (Categorized)");
852                 else
853                         ostr << _("Window") << ": " << name() << _(" (Alphabetical)");
854                 ostr << "</u></h2>\n\n";
855
856                 typedef std::map<std::string, std::vector<KeybindingMap::const_iterator> > GroupMap;
857                 GroupMap group_map;
858
859                 for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
860                         
861                         if (k->first.name().empty()) {
862                                 continue;
863                         }
864
865                         string group_name;
866                         if (categorize && !k->second.group_name.empty()) {
867                                 group_name = k->second.group_name;
868                         } else {
869                                 group_name = _("Uncategorized");
870                         }
871
872                         GroupMap::iterator gm = group_map.find (group_name);
873                         if (gm == group_map.end()) {
874                                 std::vector<KeybindingMap::const_iterator> li;
875                                 li.push_back (k);
876                                 group_map.insert (make_pair (group_name,li));
877                         } else {
878                                 gm->second.push_back (k);
879                         }
880                 }
881
882                 
883                 for (GroupMap::const_iterator gm = group_map.begin(); gm != group_map.end(); ++gm) {
884
885                         if (categorize) {
886                                 ostr << "<h3>" << gm->first << "</h3>\n";
887                         }
888                         
889                         for (vector<KeybindingMap::const_iterator>::const_iterator k = gm->second.begin(); k != gm->second.end(); ++k) {
890
891                                 if ((*k)->first.name().empty()) {
892                                         continue;
893                                 }
894
895                                 RefPtr<Action> action;
896
897                                 if ((*k)->second.action) {
898                                         action = (*k)->second.action;
899                                 } else {
900                                         if (_action_map) {
901                                                 action = _action_map->find_action ((*k)->second.action_name);
902                                         }
903                                 }
904
905                                 if (!action) {
906                                         continue;
907                                 }
908
909                                 string key_name = (*k)->first.native_short_name ();
910                                 replace_all (key_name, X_("KP_"), X_("Numpad "));
911                                 replace_all (key_name, X_("nabla"), X_("Tab"));
912
913                                 string::size_type pos;
914
915                                 char const *targets[] = { X_("Separator"), X_("Add"), X_("Subtract"), X_("Decimal"), X_("Divide"),
916                                                           X_("grave"), X_("comma"), X_("period"), X_("asterisk"), X_("backslash"),
917                                                           X_("apostrophe"), X_("minus"), X_("plus"), X_("slash"), X_("semicolon"),
918                                                           X_("colon"), X_("equal"), X_("bracketleft"), X_("bracketright"),
919                                                           X_("ampersand"), X_("numbersign"), X_("parenleft"), X_("parenright"),
920                                                           X_("quoteright"), X_("quoteleft"), X_("exclam"), X_("quotedbl"),
921                                                           0
922                                 };
923
924                                 char const *replacements[] = { X_("-"), X_("+"), X_("-"), X_("."), X_("/"),
925                                                                X_("`"), X_(","), X_("."), X_("*"), X_("\\"),
926                                                                X_("'"), X_("-"), X_("+"), X_("/"), X_(";"),
927                                                                X_(":"), X_("="), X_("{"), X_("{"),
928                                                                X_("&"), X_("#"), X_("("), X_(")"),
929                                                                X_("`"), X_("'"), X_("!"), X_("\""),
930                                 };
931
932                                 for (size_t n = 0; targets[n]; ++n) {
933                                         if ((pos = key_name.find (targets[n])) != string::npos) {
934                                                 key_name.replace (pos, strlen (targets[n]), replacements[n]);
935                                         }
936                                 }
937                                 
938                                 key_name.append(" ");
939
940                                 while (key_name.length()<28)
941                                         key_name.append("-");
942
943                                 ostr << "<span style=\"font-family:monospace;\">" << key_name;
944                                 ostr << "<i>" << action->get_label() << "</i></span></br>\n";
945                         }
946                         ostr << "\n\n";
947
948                 }
949                 
950                 ostr << "\n";
951         }
952 }
953
954 bool
955 Bindings::load (XMLNode const& node)
956 {
957         const XMLNodeList& children (node.children());
958
959         press_bindings.clear ();
960         release_bindings.clear ();
961
962         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
963                 /* each node could be Press or Release */
964                 load_operation (**i);
965         }
966
967         return true;
968 }
969
970 void
971 Bindings::load_operation (XMLNode const& node)
972 {
973         if (node.name() == X_("Press") || node.name() == X_("Release")) {
974
975                 Operation op;
976
977                 if (node.name() == X_("Press")) {
978                         op = Press;
979                 } else {
980                         op = Release;
981                 }
982
983                 const XMLNodeList& children (node.children());
984
985                 for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) {
986
987                         XMLProperty const * ap;
988                         XMLProperty const * kp;
989                         XMLProperty const * bp;
990                         XMLProperty const * gp;
991                         XMLNode const * child = *p;
992
993                         ap = child->property ("action");
994                         kp = child->property ("key");
995                         bp = child->property ("button");
996                         gp = child->property ("group");
997
998                         if (!ap || (!kp && !bp)) {
999                                 continue;
1000                         }
1001
1002                         if (kp) {
1003                                 KeyboardKey k;
1004                                 if (!KeyboardKey::make_key (kp->value(), k)) {
1005                                         continue;
1006                                 }
1007                                 add (k, op, ap->value(), gp);
1008                         } else {
1009                                 MouseButton b;
1010                                 if (!MouseButton::make_button (bp->value(), b)) {
1011                                         continue;
1012                                 }
1013                                 add (b, op, ap->value(), gp);
1014                         }
1015                 }
1016         }
1017 }
1018
1019 void
1020 Bindings::get_all_actions (std::vector<std::string>& paths,
1021                            std::vector<std::string>& labels,
1022                            std::vector<std::string>& tooltips,
1023                            std::vector<std::string>& keys,
1024                            std::vector<RefPtr<Action> >& actions)
1025 {
1026         if (!_action_map) {
1027                 return;
1028         }
1029
1030         /* build a reverse map from actions to bindings */
1031
1032         typedef map<Glib::RefPtr<Gtk::Action>,KeyboardKey> ReverseMap;
1033         ReverseMap rmap;
1034
1035         for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
1036                 rmap.insert (make_pair (k->second.action, k->first));
1037         }
1038
1039         /* get a list of all actions */
1040
1041         ActionMap::Actions all_actions;
1042         _action_map->get_actions (all_actions);
1043
1044         for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) {
1045
1046                 paths.push_back ((*act)->get_accel_path());
1047                 labels.push_back ((*act)->get_label());
1048                 tooltips.push_back ((*act)->get_tooltip());
1049
1050                 ReverseMap::iterator r = rmap.find (*act);
1051
1052                 if (r != rmap.end()) {
1053                         keys.push_back (r->second.display_label());
1054                 } else {
1055                         keys.push_back (string());
1056                 }
1057
1058                 actions.push_back (*act);
1059         }
1060 }
1061
1062 Bindings*
1063 Bindings::get_bindings (string const& name, ActionMap& map)
1064 {
1065         for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
1066                 if ((*b)->name() == name) {
1067                         (*b)->set_action_map (map);
1068                         return *b;
1069                 }
1070         }
1071
1072         return 0;
1073 }
1074
1075 void
1076 Bindings::associate_all ()
1077 {
1078         for (list<Bindings*>::iterator b = bindings.begin(); b != bindings.end(); b++) {
1079                 (*b)->associate ();
1080         }
1081 }
1082
1083 bool
1084 Bindings::is_bound (KeyboardKey const& kb, Operation op) const
1085 {
1086         const KeybindingMap& km = get_keymap(op);
1087         return km.find(kb) != km.end();
1088 }
1089
1090 std::string
1091 Bindings::bound_name (KeyboardKey const& kb, Operation op) const
1092 {
1093         const KeybindingMap& km = get_keymap(op);
1094         KeybindingMap::const_iterator b = km.find(kb);
1095         if (b == km.end()) {
1096                 return "";
1097         }
1098         return b->second.action_name;
1099 }
1100
1101 bool
1102 Bindings::is_registered (Operation op, std::string const& action_name) const
1103 {
1104         const KeybindingMap& km = get_keymap(op);
1105         return std::find_if(km.begin(),  km.end(),  ActionNameRegistered<KeybindingMap::const_iterator::value_type>(action_name)) != km.end();
1106 }
1107
1108 Bindings::KeybindingMap&
1109 Bindings::get_keymap (Operation op)
1110 {
1111         switch (op) {
1112         case Press:
1113                 return press_bindings;
1114         case Release:
1115         default:
1116                 return release_bindings;
1117         }
1118 }
1119
1120 const Bindings::KeybindingMap&
1121 Bindings::get_keymap (Operation op) const
1122 {
1123         switch (op) {
1124         case Press:
1125                 return press_bindings;
1126         case Release:
1127         default:
1128                 return release_bindings;
1129         }
1130 }
1131
1132 Bindings::MouseButtonBindingMap&
1133 Bindings::get_mousemap (Operation op)
1134 {
1135         switch (op) {
1136         case Press:
1137                 return button_press_bindings;
1138         case Release:
1139         default:
1140                 return button_release_bindings;
1141         }
1142 }
1143
1144 /*==========================================ACTION MAP =========================================*/
1145
1146 ActionMap::ActionMap (string const & name)
1147         : _name (name)
1148         , _bindings (0)
1149 {
1150         action_maps.push_back (this);
1151 }
1152
1153 ActionMap::~ActionMap ()
1154 {
1155         action_maps.remove (this);
1156 }
1157
1158 void
1159 ActionMap::set_bindings (Bindings* b)
1160 {
1161         _bindings = b;
1162 }
1163
1164 void
1165 ActionMap::get_actions (ActionMap::Actions& acts)
1166 {
1167         for (_ActionMap::iterator a = _actions.begin(); a != _actions.end(); ++a) {
1168                 acts.push_back (a->second);
1169         }
1170 }
1171
1172 RefPtr<Action>
1173 ActionMap::find_action (const string& name)
1174 {
1175         _ActionMap::iterator a = _actions.find (name);
1176
1177         if (a != _actions.end()) {
1178                 return a->second;
1179         }
1180
1181         return RefPtr<Action>();
1182 }
1183
1184 RefPtr<ActionGroup>
1185 ActionMap::create_action_group (const string& name)
1186 {
1187         Glib::ListHandle<Glib::RefPtr<ActionGroup> > agl =  ActionManager::ui_manager->get_action_groups ();
1188         for (Glib::ListHandle<Glib::RefPtr<ActionGroup> >::iterator i = agl.begin (); i != agl.end (); ++i) {
1189                 if ((*i)->get_name () == name) {
1190                         return *i;
1191                 }
1192         }
1193
1194         RefPtr<ActionGroup> g = ActionGroup::create (name);
1195
1196         /* this is one of the places where our own Action management code
1197            has to touch the GTK one, because we want the GtkUIManager to
1198            be able to create widgets (particularly Menus) from our actions.
1199
1200            This is a a necessary step for that to happen.
1201         */
1202
1203         if (g) {
1204                 ActionManager::ui_manager->insert_action_group (g);
1205         }
1206
1207         return g;
1208 }
1209
1210 RefPtr<Action>
1211 ActionMap::register_action (RefPtr<ActionGroup> group, const char* name, const char* label)
1212 {
1213         string fullpath;
1214
1215         RefPtr<Action> act = Action::create (name, label);
1216
1217         fullpath = group->get_name();
1218         fullpath += '/';
1219         fullpath += name;
1220
1221         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1222                 group->add (act);
1223                 return act;
1224         }
1225
1226         /* already registered */
1227         return RefPtr<Action> ();
1228 }
1229
1230 RefPtr<Action>
1231 ActionMap::register_action (RefPtr<ActionGroup> group,
1232                             const char* name, const char* label, sigc::slot<void> sl)
1233 {
1234         string fullpath;
1235
1236         RefPtr<Action> act = Action::create (name, label);
1237
1238         fullpath = group->get_name();
1239         fullpath += '/';
1240         fullpath += name;
1241
1242         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1243                 group->add (act, sl);
1244                 return act;
1245         }
1246
1247         /* already registered */
1248         return RefPtr<Action>();
1249 }
1250
1251 RefPtr<Action>
1252 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1253                                   Gtk::RadioAction::Group& rgroup,
1254                                   const char* name, const char* label,
1255                                   sigc::slot<void> sl)
1256 {
1257         string fullpath;
1258
1259         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1260         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1261
1262         fullpath = group->get_name();
1263         fullpath += '/';
1264         fullpath += name;
1265
1266         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1267                 group->add (act, sl);
1268                 return act;
1269         }
1270
1271         /* already registered */
1272         return RefPtr<Action>();
1273 }
1274
1275 RefPtr<Action>
1276 ActionMap::register_radio_action (RefPtr<ActionGroup> group,
1277                                   Gtk::RadioAction::Group& rgroup,
1278                                   const char* name, const char* label,
1279                                   sigc::slot<void,GtkAction*> sl,
1280                                   int value)
1281 {
1282         string fullpath;
1283
1284         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
1285         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
1286         ract->property_value() = value;
1287
1288         fullpath = group->get_name();
1289         fullpath += '/';
1290         fullpath += name;
1291
1292         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1293                 group->add (act, sigc::bind (sl, act->gobj()));
1294                 return act;
1295         }
1296
1297         /* already registered */
1298
1299         return RefPtr<Action>();
1300 }
1301
1302 RefPtr<Action>
1303 ActionMap::register_toggle_action (RefPtr<ActionGroup> group,
1304                                    const char* name, const char* label, sigc::slot<void> sl)
1305 {
1306         string fullpath;
1307
1308         fullpath = group->get_name();
1309         fullpath += '/';
1310         fullpath += name;
1311
1312         RefPtr<Action> act = ToggleAction::create (name, label);
1313
1314         if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) {
1315                 group->add (act, sl);
1316                 return act;
1317         }
1318
1319         /* already registered */
1320         return RefPtr<Action>();
1321 }
1322
1323 void
1324 ActionMap::get_all_actions (std::vector<std::string>& paths,
1325                             std::vector<std::string>& labels,
1326                             std::vector<std::string>& tooltips,
1327                             std::vector<std::string>& keys,
1328                             std::vector<RefPtr<Action> >& actions)
1329 {
1330         for (list<ActionMap*>::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) {
1331
1332                 ActionMap::Actions these_actions;
1333                 (*map)->get_actions (these_actions);
1334
1335                 for (ActionMap::Actions::const_iterator act = these_actions.begin(); act != these_actions.end(); ++act) {
1336
1337                         paths.push_back ((*act)->get_accel_path());
1338                         labels.push_back ((*act)->get_label());
1339                         tooltips.push_back ((*act)->get_tooltip());
1340                         actions.push_back (*act);
1341
1342                         Bindings* bindings = (*map)->bindings();
1343
1344                         if (bindings) {
1345
1346                                 KeyboardKey key;
1347                                 Bindings::Operation op;
1348
1349                                 key = bindings->get_binding_for_action (*act, op);
1350
1351                                 if (key == KeyboardKey::null_key()) {
1352                                         keys.push_back (string());
1353                                 } else {
1354                                         keys.push_back (key.display_label());
1355                                 }
1356                         } else {
1357                                 keys.push_back (string());
1358                         }
1359                 }
1360
1361                 these_actions.clear ();
1362         }
1363 }
1364
1365 std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) {
1366         char const *gdk_name = gdk_keyval_name (k.key());
1367         return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state "
1368                    << hex << k.state() << dec << ' ' << show_gdk_event_state (k.state());
1369 }