Bindings save release bindings as well as press bindings
[ardour.git] / libs / gtkmm2ext / bindings.cc
1 #include <iostream>
2 #include "pbd/xml++.h"
3 #include "gtkmm2ext/bindings.h"
4 #include "gtkmm2ext/keyboard.h"
5
6 #include "i18n.h"
7
8 using namespace std;
9 using namespace Glib;
10 using namespace Gtk;
11 using namespace Gtkmm2ext;
12
13 uint32_t KeyboardKey::_ignored_state = 0;
14
15 KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode)
16 {
17         uint32_t ignore = _ignored_state;
18         
19         if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) {
20                 /* key is not subject to case, so ignore SHIFT
21                  */
22                 ignore |= GDK_SHIFT_MASK;
23         }
24
25         _val = (state & ~ignore);
26         _val <<= 32;
27         _val |= keycode;
28 };
29
30
31 string
32 KeyboardKey::name () const
33 {
34         int s = state();
35         
36         string str;
37
38         if (s & Keyboard::PrimaryModifier) {
39                 str += "Primary";
40         } 
41         if (s & Keyboard::SecondaryModifier) {
42                 if (!str.empty()) {
43                         str += '-';
44                 }
45                 str += "Secondary";
46         }
47         if (s & Keyboard::TertiaryModifier) {
48                 if (!str.empty()) {
49                         str += '-';
50                 }
51                 str += "Tertiary";
52         } 
53         if (s & Keyboard::Level4Modifier) {
54                 if (!str.empty()) {
55                         str += '-';
56                 }
57                 str += "Level4";
58         }
59         
60         if (!str.empty()) {
61                 str += '-';
62         }
63
64         str += gdk_keyval_name (key());
65
66         return str;
67 }
68
69 bool
70 KeyboardKey::make_key (const string& str, KeyboardKey& k)
71 {
72         int s = 0;
73
74         if (str.find ("Primary") != string::npos) {
75                 s |= Keyboard::PrimaryModifier;
76         }
77
78         if (str.find ("Secondary") != string::npos) {
79                 s |= Keyboard::SecondaryModifier;
80         }
81
82         if (str.find ("Tertiary") != string::npos) {
83                 s |= Keyboard::TertiaryModifier;
84         }
85
86         if (str.find ("Level4") != string::npos) {
87                 s |= Keyboard::Level4Modifier;
88         }
89
90         string::size_type lastmod = str.find_last_of ('-');
91         guint keyval;
92
93         if (lastmod == string::npos) {
94                 keyval = gdk_keyval_from_name (str.c_str());
95         } else {
96                 keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str());
97         }
98
99         if (keyval == GDK_VoidSymbol) {
100                 return false;
101         }
102
103         k = KeyboardKey (s, keyval);
104         return true;
105 }
106
107 Bindings::Bindings ()
108         : action_map (0)
109 {
110 }
111
112 Bindings::~Bindings()
113 {
114 }
115
116 void
117 Bindings::set_action_map (ActionMap& am)
118 {
119         action_map = &am;
120         press_bindings.clear ();
121         release_bindings.clear ();
122 }
123
124 bool
125 Bindings::activate (KeyboardKey kb, KeyboardKey::Operation op)
126 {
127         KeybindingMap* kbm;
128
129         switch (op) {
130         case KeyboardKey::Press:
131                 kbm = &press_bindings;
132                 break;
133         case KeyboardKey::Release:
134                 kbm = &release_bindings;
135                 break;
136         }
137
138         KeybindingMap::iterator k = kbm->find (kb);
139
140         if (k == kbm->end()) {
141                 /* no entry for this key in the state map */
142                 return false;
143         }
144
145         /* lets do it ... */
146
147         k->second->activate ();
148         return true;
149 }
150
151 void
152 Bindings::add (KeyboardKey kb, KeyboardKey::Operation op, RefPtr<Action> what)
153 {
154         KeybindingMap* kbm;
155
156         switch (op) {
157         case KeyboardKey::Press:
158                 kbm = &press_bindings;
159                 break;
160         case KeyboardKey::Release:
161                 kbm = &release_bindings;
162                 break;
163         }
164
165         KeybindingMap::iterator k = kbm->find (kb);
166
167         if (k == kbm->end()) {
168                 pair<KeyboardKey,RefPtr<Action> > newpair (kb, what);
169                 kbm->insert (newpair);
170                 cerr << "Bindings added " << kb.key() << " w/ " << kb.state() << endl;
171         } else {
172                 k->second = what;
173         }
174 }
175
176 void
177 Bindings::remove (KeyboardKey kb, KeyboardKey::Operation op)
178 {
179         KeybindingMap* kbm;
180
181         switch (op) {
182         case KeyboardKey::Press:
183                 kbm = &press_bindings;
184                 break;
185         case KeyboardKey::Release:
186                 kbm = &release_bindings;
187                 break;
188         }
189
190         KeybindingMap::iterator k = kbm->find (kb);
191
192         if (k != kbm->end()) {
193                 kbm->erase (k);
194         }
195 }
196
197 bool
198 Bindings::save (const string& path)
199 {
200         XMLTree tree;
201         XMLNode* root = new XMLNode (X_("Bindings"));
202         tree.set_root (root);
203
204         XMLNode* presses = new XMLNode (X_("Press"));
205         root->add_child_nocopy (*presses);
206
207         for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) {
208                 XMLNode* child;
209                 child = new XMLNode (X_("Binding"));
210                 child->add_property (X_("key"), k->first.name());
211                 child->add_property (X_("action"), k->second->get_name());
212                 presses->add_child_nocopy (*child);
213         }
214
215         XMLNode* releases = new XMLNode (X_("Release"));
216         root->add_child_nocopy (*releases);
217
218         for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) {
219                 XMLNode* child;
220                 child = new XMLNode (X_("Binding"));
221                 child->add_property (X_("key"), k->first.name());
222                 child->add_property (X_("action"), k->second->get_name());
223                 releases->add_child_nocopy (*child);
224         }
225
226         if (!tree.write (path)) {
227                 ::unlink (path.c_str());
228                 return false;
229         }
230
231         return true;
232 }
233
234 bool
235 Bindings::load (const string& path)
236 {
237         XMLTree tree;
238
239         if (!action_map) {
240                 return false;
241         }
242
243         if (!tree.read (path)) {
244                 return false;
245         }
246         
247         press_bindings.clear ();
248         release_bindings.clear ();
249
250         XMLNode& root (*tree.root());
251         const XMLNodeList& children (root.children());
252
253         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
254
255                 if ((*i)->name() == X_("Press") || (*i)->name() == X_("Release")) {
256
257                         KeyboardKey::Operation op;
258
259                         if ((*i)->name() == X_("Press")) {
260                                 op = KeyboardKey::Press;
261                         } else {
262                                 op = KeyboardKey::Release;
263                         }
264                         
265                         const XMLNodeList& gchildren ((*i)->children());
266
267                         for (XMLNodeList::const_iterator p = gchildren.begin(); p != gchildren.end(); ++p) {
268
269                                 XMLProperty* ap;
270                                 XMLProperty* kp;
271
272                                 ap = (*p)->property ("action");
273                                 kp = (*p)->property ("key");
274
275                                 if (!ap || !kp) {
276                                         continue;
277                                 }
278
279                                 RefPtr<Action> act = action_map->find_action (ap->value());
280                                 
281                                 if (!act) {
282                                         continue;
283                                 }
284
285                                 KeyboardKey k;
286                                 
287                                 if (!KeyboardKey::make_key (kp->value(), k)) {
288                                         continue;
289                                 }
290
291                                 add (k, op, act);
292                         }
293                 }
294         }
295
296         return true;
297 }
298
299 RefPtr<Action>
300 ActionMap::find_action (const string& name)
301 {
302         _ActionMap::iterator a = actions.find (name);
303
304         if (a != actions.end()) {
305                 return a->second;
306         }
307
308         return RefPtr<Action>();
309 }
310
311 RefPtr<Action> 
312 ActionMap::register_action (const char* path,
313                             const char* name, const char* label, sigc::slot<void> sl)
314 {
315         string fullpath;
316
317         RefPtr<Action> act = Action::create (name, label);
318
319         act->signal_activate().connect (sl);
320
321         fullpath = path;
322         fullpath += '/';
323         fullpath += name;
324
325         actions.insert (_ActionMap::value_type (fullpath, act));
326         return act;
327 }
328
329 RefPtr<Action> 
330 ActionMap::register_radio_action (const char* path, Gtk::RadioAction::Group& rgroup,
331                                   const char* name, const char* label, 
332                                   sigc::slot<void,GtkAction*> sl,
333                                   int value)
334 {
335         string fullpath;
336
337         RefPtr<Action> act = RadioAction::create (rgroup, name, label);
338         RefPtr<RadioAction> ract = RefPtr<RadioAction>::cast_dynamic(act);
339         ract->property_value() = value;
340
341         act->signal_activate().connect (sigc::bind (sl, act->gobj()));
342
343         fullpath = path;
344         fullpath += '/';
345         fullpath += name;
346
347         actions.insert (_ActionMap::value_type (fullpath, act));
348         return act;
349 }
350
351 RefPtr<Action> 
352 ActionMap::register_toggle_action (const char* path,
353                                    const char* name, const char* label, sigc::slot<void> sl)
354 {
355         string fullpath;
356
357         RefPtr<Action> act = ToggleAction::create (name, label);
358
359         act->signal_activate().connect (sl);
360
361         fullpath = path;
362         fullpath += '/';
363         fullpath += name;
364
365         actions.insert (_ActionMap::value_type (fullpath, act));
366         return act;
367 }