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