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