Fix confusion about the 'done' variable in InterThreadInfo during import. 'done...
[ardour.git] / gtk2_ardour / keyeditor.cc
1 #include <map>
2
3 #include "ardour/profile.h"
4
5 #include <gtkmm/stock.h>
6 #include <gtkmm/label.h>
7 #include <gtkmm/accelkey.h>
8 #include <gtkmm/accelmap.h>
9 #include <gtkmm/uimanager.h>
10
11 #include "gtkmm2ext/utils.h"
12
13 #include "pbd/strsplit.h"
14 #include "pbd/replace_all.h"
15
16 #include "ardour/profile.h"
17
18 #include "actions.h"
19 #include "keyboard.h"
20 #include "keyeditor.h"
21 #include "utils.h"
22
23 #include "i18n.h"
24
25 using namespace std;
26 using namespace Gtk;
27 using namespace Gdk;
28 using namespace PBD;
29
30 using Gtkmm2ext::Keyboard;
31
32 KeyEditor::KeyEditor ()
33         : ArdourDialog (_("Key Bindings"), false)
34         , unbind_button (_("Remove shortcut"))
35         , unbind_box (BUTTONBOX_END)
36
37 {
38         can_bind = false;
39         last_state = 0;
40
41         model = TreeStore::create(columns);
42
43         view.set_model (model);
44         view.append_column (_("Action"), columns.action);
45         view.append_column (_("Shortcut"), columns.binding);
46         view.set_headers_visible (true);
47         view.get_selection()->set_mode (SELECTION_SINGLE);
48         view.set_reorderable (false);
49         view.set_size_request (500,300);
50         view.set_enable_search (false);
51         view.set_rules_hint (true);
52         view.set_name (X_("KeyEditorTree"));
53
54         view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &KeyEditor::action_selected));
55
56         scroller.add (view);
57         scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
58
59
60         get_vbox()->set_spacing (6);
61         get_vbox()->pack_start (scroller);
62
63         if (!ARDOUR::Profile->get_sae()) {
64
65                 Label* hint = manage (new Label (_("Select an action, then press the key(s) to (re)set its shortcut")));
66                 hint->show ();
67                 unbind_box.set_spacing (6);
68                 unbind_box.pack_start (*hint, false, true);
69                 unbind_box.pack_start (unbind_button, false, false);
70                 unbind_button.signal_clicked().connect (sigc::mem_fun (*this, &KeyEditor::unbind));
71
72                 get_vbox()->pack_start (unbind_box, false, false);
73                 unbind_box.show ();
74                 unbind_button.show ();
75
76         }
77
78         get_vbox()->set_border_width (12);
79
80         view.show ();
81         scroller.show ();
82
83         unbind_button.set_sensitive (false);
84 }
85
86 void
87 KeyEditor::unbind ()
88 {
89         TreeModel::iterator i = view.get_selection()->get_selected();
90
91         unbind_button.set_sensitive (false);
92
93         cerr << "trying to unbind\n";
94
95         if (i != model->children().end()) {
96                 string path = (*i)[columns.path];
97
98                 if (!(*i)[columns.bindable]) {
99                         return;
100                 }
101
102                 bool result = AccelMap::change_entry (path,
103                                                       0,
104                                                       (ModifierType) 0,
105                                                       true);
106                 if (result) {
107                         (*i)[columns.binding] = string ();
108                 }
109         }
110 }
111
112 void
113 KeyEditor::on_show ()
114 {
115         populate ();
116         view.get_selection()->unselect_all ();
117         ArdourDialog::on_show ();
118 }
119
120 void
121 KeyEditor::on_unmap ()
122 {
123         ArdourDialog::on_unmap ();
124 }
125
126 void
127 KeyEditor::action_selected ()
128 {
129         if (view.get_selection()->count_selected_rows() == 0) {
130                 return;
131         }
132
133         TreeModel::iterator i = view.get_selection()->get_selected();
134
135         unbind_button.set_sensitive (false);
136
137         if (i != model->children().end()) {
138
139                 string path = (*i)[columns.path];
140
141                 if (!(*i)[columns.bindable]) {
142                         return;
143                 }
144
145                 string binding = (*i)[columns.binding];
146
147                 if (!binding.empty()) {
148                         unbind_button.set_sensitive (true);
149                 }
150         }
151 }
152
153 bool
154 KeyEditor::on_key_press_event (GdkEventKey* ev)
155 {
156         can_bind = true;
157         last_state = ev->state;
158         return false;
159 }
160
161 bool
162 KeyEditor::on_key_release_event (GdkEventKey* ev)
163 {
164         if (ARDOUR::Profile->get_sae() || !can_bind || ev->state != last_state) {
165                 return false;
166         }
167
168         TreeModel::iterator i = view.get_selection()->get_selected();
169
170         if (i != model->children().end()) {
171                 string path = (*i)[columns.path];
172
173                 if (!(*i)[columns.bindable]) {
174                         goto out;
175                 }
176
177                 cerr << "real lkeyval: " << ev->keyval << endl;
178                 Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (ev->keyval);
179                 cerr << "using keyval = " << ev->keyval << endl;
180
181
182                 bool result = AccelMap::change_entry (path,
183                                                       ev->keyval,
184                                                       ModifierType (Keyboard::RelevantModifierKeyMask & ev->state),
185                                                       true);
186
187                 cerr << "New binding to " << ev->keyval << " worked: " << result << endl;
188
189                 if (result) {
190                         AccelKey key;
191                         (*i)[columns.binding] = ActionManager::get_key_representation (path, key);
192                 }
193         }
194
195   out:
196         can_bind = false;
197         return true;
198 }
199
200 void
201 KeyEditor::populate ()
202 {
203         vector<string> paths;
204         vector<string> labels;
205         vector<string> keys;
206         vector<AccelKey> bindings;
207         typedef std::map<string,TreeIter> NodeMap;
208         NodeMap nodes;
209         NodeMap::iterator r;
210
211         ActionManager::get_all_actions (labels, paths, keys, bindings);
212
213         vector<string>::iterator k;
214         vector<string>::iterator p;
215         vector<string>::iterator l;
216
217         model->clear ();
218
219         for (l = labels.begin(), k = keys.begin(), p = paths.begin(); l != labels.end(); ++k, ++p, ++l) {
220
221                 TreeModel::Row row;
222                 vector<string> parts;
223
224                 parts.clear ();
225
226                 split (*p, parts, '/');
227
228                 if (parts.empty()) {
229                         continue;
230                 }
231
232                 if ((r = nodes.find (parts[1])) == nodes.end()) {
233
234                         /* top level is missing */
235
236                         TreeIter rowp;
237                         TreeModel::Row parent;
238                         rowp = model->append();
239                         nodes[parts[1]] = rowp;
240                         parent = *(rowp);
241                         parent[columns.action] = parts[1];
242                         parent[columns.bindable] = false;
243
244                         row = *(model->append (parent.children()));
245
246                 } else {
247
248                         row = *(model->append ((*r->second)->children()));
249
250                 }
251
252                 /* add this action */
253
254                 row[columns.action] = (*l);
255                 row[columns.path] = (*p);
256                 row[columns.bindable] = true;
257
258                 if (*k == ActionManager::unbound_string) {
259                         row[columns.binding] = string();
260                 } else {
261
262 #ifdef GTKOSX
263                         string label = (*k);
264
265                         /* Gtk/Quartz maps:
266                            NSAlternate/NSOption key to Mod1
267                            NSCommand key to Meta
268                         */
269
270                         replace_all (label, "<Meta>", _("Command-"));
271                         replace_all (label, "<Alt>", _("Option-"));
272                         replace_all (label, "<Shift>", _("Shift-"));
273                         replace_all (label, "<Control>", _("Control-"));
274                         row[columns.binding] = label;
275 #else
276                         row[columns.binding] = (*k);
277 #endif
278                 }
279         }
280 }