MCP: somewhat functional (?) full keybinding GUI
[ardour.git] / libs / surfaces / mackie / gui.cc
1 /*
2         Copyright (C) 2010 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 #include <gtkmm/comboboxtext.h>
20 #include <gtkmm/box.h>
21 #include <gtkmm/spinbutton.h>
22 #include <gtkmm/table.h>
23 #include <gtkmm/treeview.h>
24 #include <gtkmm/liststore.h>
25 #include <gtkmm/treestore.h>
26 #include <gtkmm/notebook.h>
27 #include <gtkmm/cellrenderercombo.h>
28
29 #include "pbd/strsplit.h"
30
31 #include "gtkmm2ext/utils.h"
32 #include "gtkmm2ext/actions.h"
33
34 #include "mackie_control_protocol.h"
35 #include "device_info.h"
36 #include "gui.h"
37
38 #include "i18n.h"
39
40 using namespace std;
41 using namespace Mackie;
42 using namespace Gtk;
43
44 void*
45 MackieControlProtocol::get_gui () const
46 {
47         if (!_gui) {
48                 const_cast<MackieControlProtocol*>(this)->build_gui ();
49         }
50
51         return _gui;
52 }
53
54 void
55 MackieControlProtocol::tear_down_gui ()
56 {
57         delete (MackieControlProtocolGUI*) _gui;
58 }
59
60 void
61 MackieControlProtocol::build_gui ()
62 {
63         _gui = (void *) new MackieControlProtocolGUI (*this);
64 }
65
66 MackieControlProtocolGUI::MackieControlProtocolGUI (MackieControlProtocol& p)
67         : _cp (p)
68 {
69         set_border_width (12);
70
71         Gtk::Table* table = Gtk::manage (new Gtk::Table (2, 2));
72         table->set_spacings (4);
73         
74         table->attach (*manage (new Gtk::Label (_("Device Type:"))), 0, 1, 0, 1, AttachOptions(FILL|EXPAND), AttachOptions(0));
75         table->attach (_surface_combo, 1, 2, 0, 1, AttachOptions(FILL|EXPAND), AttachOptions(0));
76
77         table->attach (*manage (new Gtk::Label (_("Profile/Settings:"))), 0, 1, 1, 2, AttachOptions(FILL|EXPAND), AttachOptions(0));
78         table->attach (_profile_combo, 1, 2, 1, 2, AttachOptions(FILL|EXPAND), AttachOptions(0));
79
80         vector<string> surfaces;
81         
82         for (std::map<std::string,DeviceInfo>::iterator i = DeviceInfo::device_info.begin(); i != DeviceInfo::device_info.end(); ++i) {
83                 surfaces.push_back (i->first);
84         }
85         Gtkmm2ext::set_popdown_strings (_surface_combo, surfaces);
86         _surface_combo.set_active_text (p.device_info().name());
87         _surface_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::surface_combo_changed));
88
89         vector<string> profiles;
90         
91         profiles.push_back ("default");
92
93         for (std::map<std::string,DeviceProfile>::iterator i = DeviceProfile::device_profiles.begin(); i != DeviceProfile::device_profiles.end(); ++i) {
94                 profiles.push_back (i->first);
95         }
96         Gtkmm2ext::set_popdown_strings (_profile_combo, profiles);
97         _profile_combo.set_active_text (p.device_profile().name());
98         _profile_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::profile_combo_changed));
99
100         append_page (*table, _("Device Selection"));
101         table->show_all();
102
103         /* function key editor */
104
105         append_page (function_key_scroller, _("Function Keys"));
106         function_key_scroller.add (function_key_editor);
107         
108         build_available_action_menu ();
109         build_function_key_editor ();
110         refresh_function_key_editor ();
111         function_key_scroller.show_all();
112 }
113
114 CellRendererCombo*
115 MackieControlProtocolGUI::make_action_renderer (Glib::RefPtr<TreeStore> model, Gtk::TreeModelColumnBase column)
116 {
117         CellRendererCombo* renderer = manage (new CellRendererCombo);
118         renderer->property_model() = model;
119         renderer->property_editable() = true;
120         renderer->property_text_column() = 0;
121         renderer->property_has_entry() = false;
122         renderer->signal_edited().connect (sigc::bind (sigc::mem_fun(*this, &MackieControlProtocolGUI::action_changed), column));
123
124         return renderer;
125 }
126
127 void
128 MackieControlProtocolGUI::build_available_action_menu ()
129 {
130         /* build a model of all available actions (needs to be tree structured
131          * more) 
132          */
133
134         available_action_model = TreeStore::create (available_action_columns);
135
136         vector<string> paths;
137         vector<string> labels;
138         vector<string> tooltips;
139         vector<string> keys;
140         vector<AccelKey> bindings;
141         typedef std::map<string,TreeIter> NodeMap;
142         NodeMap nodes;
143         NodeMap::iterator r;
144
145         ActionManager::get_all_actions (labels, paths, tooltips, keys, bindings);
146
147         vector<string>::iterator k;
148         vector<string>::iterator p;
149         vector<string>::iterator t;
150         vector<string>::iterator l;
151
152         available_action_model->clear ();
153
154         for (l = labels.begin(), k = keys.begin(), p = paths.begin(), t = tooltips.begin(); l != labels.end(); ++k, ++p, ++t, ++l) {
155
156                 TreeModel::Row row;
157                 vector<string> parts;
158
159                 parts.clear ();
160
161                 split (*p, parts, '/');
162
163                 if (parts.empty()) {
164                         continue;
165                 }
166
167                 //kinda kludgy way to avoid displaying menu items as mappable
168                 if ( parts[1] == _("Main_menu") )
169                         continue;
170                 if ( parts[1] == _("JACK") )
171                         continue;
172                 if ( parts[1] == _("redirectmenu") )
173                         continue;
174                 if ( parts[1] == _("Editor_menus") )
175                         continue;
176                 if ( parts[1] == _("RegionList") )
177                         continue;
178                 if ( parts[1] == _("ProcessorMenu") )
179                         continue;
180
181                 if ((r = nodes.find (parts[1])) == nodes.end()) {
182
183                         /* top level is missing */
184
185                         TreeIter rowp;
186                         TreeModel::Row parent;
187                         rowp = available_action_model->append();
188                         nodes[parts[1]] = rowp;
189                         parent = *(rowp);
190                         parent[available_action_columns.name] = parts[1];
191
192                         row = *(available_action_model->append (parent.children()));
193
194                 } else {
195
196                         row = *(available_action_model->append ((*r->second)->children()));
197
198                 }
199
200                 /* add this action */
201
202                 if (l->empty ()) {
203                         row[available_action_columns.name] = *t;
204                         action_map[*t] = *p;
205                 } else {
206                         row[available_action_columns.name] = *l;
207                         action_map[*l] = *p;
208                 }
209
210                 row[available_action_columns.path] = (*p);
211         }
212 }
213
214 void
215 MackieControlProtocolGUI::build_function_key_editor ()
216 {
217         function_key_editor.append_column (_("Key"), function_key_columns.name);
218
219         TreeViewColumn* col;
220         CellRendererCombo* renderer;
221
222         renderer = make_action_renderer (available_action_model, function_key_columns.plain);
223         col = manage (new TreeViewColumn (_("Plain"), *renderer));
224         col->add_attribute (renderer->property_text(), function_key_columns.plain);
225         function_key_editor.append_column (*col);
226         
227         renderer = make_action_renderer (available_action_model, function_key_columns.shift);
228         col = manage (new TreeViewColumn (_("Shift"), *renderer));
229         col->add_attribute (renderer->property_text(), function_key_columns.shift);
230         function_key_editor.append_column (*col);
231
232         renderer = make_action_renderer (available_action_model, function_key_columns.control);
233         col = manage (new TreeViewColumn (_("Control"), *renderer));
234         col->add_attribute (renderer->property_text(), function_key_columns.control);
235         function_key_editor.append_column (*col);
236
237         renderer = make_action_renderer (available_action_model, function_key_columns.option);
238         col = manage (new TreeViewColumn (_("Option"), *renderer));
239         col->add_attribute (renderer->property_text(), function_key_columns.option);
240         function_key_editor.append_column (*col);
241
242         renderer = make_action_renderer (available_action_model, function_key_columns.cmdalt);
243         col = manage (new TreeViewColumn (_("Cmd/Alt"), *renderer));
244         col->add_attribute (renderer->property_text(), function_key_columns.cmdalt);
245         function_key_editor.append_column (*col);
246
247         renderer = make_action_renderer (available_action_model, function_key_columns.shiftcontrol);
248         col = manage (new TreeViewColumn (_("Shift+Control"), *renderer));
249         col->add_attribute (renderer->property_text(), function_key_columns.shiftcontrol);
250         function_key_editor.append_column (*col);
251
252         function_key_model = ListStore::create (function_key_columns);
253         function_key_editor.set_model (function_key_model);
254 }
255
256 void
257 MackieControlProtocolGUI::refresh_function_key_editor ()
258 {
259         function_key_editor.set_model (Glib::RefPtr<TreeModel>());
260         function_key_model->clear ();
261
262         /* now fill with data */
263
264         TreeModel::Row row;
265         DeviceProfile dp (_cp.device_profile());
266
267         for (int n = 0; n < Mackie::Button::FinalGlobalButton; ++n) {
268
269                 Mackie::Button::ID bid = (Mackie::Button::ID) n;
270
271                 row = *(function_key_model->append());
272                 row[function_key_columns.name] = Mackie::Button::id_to_name (bid);
273                 row[function_key_columns.id] = bid;
274
275                 Glib::RefPtr<Gtk::Action> act;
276                 string action;
277                 const string defstring = "def";
278
279                 action = dp.get_button_action (bid, 0);
280                 if (action.empty()) {
281                         row[function_key_columns.plain] = defstring;
282                 } else {
283                         act = ActionManager::get_action (action.c_str());
284                         if (act) {
285                                 row[function_key_columns.plain] = act->get_label();
286                         } else {
287                                 row[function_key_columns.plain] = defstring;
288                         }
289                 }
290
291                 action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_CONTROL);
292                 if (action.empty()) {
293                         row[function_key_columns.control] = defstring;
294                 } else {
295                         act = ActionManager::get_action (action.c_str());
296                         if (act) {
297                                 row[function_key_columns.control] = act->get_label();
298                         } else {
299                                 row[function_key_columns.control] = defstring;
300                         }
301                 }
302
303                 action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_SHIFT);
304                 if (action.empty()) {
305                         row[function_key_columns.shift] = defstring;
306                 } else {
307                         act = ActionManager::get_action (action.c_str());
308                         if (act) {
309                                 row[function_key_columns.shift] = act->get_label();
310                         } else {
311                                 row[function_key_columns.shift] = defstring;
312                         }
313                 }
314
315                 action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_OPTION);
316                 if (action.empty()) {
317                         row[function_key_columns.option] = defstring;
318                 } else {
319                         act = ActionManager::get_action (action.c_str());
320                         if (act) {
321                                 row[function_key_columns.option] = act->get_label();
322                         } else {
323                                 row[function_key_columns.option] = defstring;
324                         }
325                 }
326
327                 action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_CMDALT);
328                 if (action.empty()) {
329                         row[function_key_columns.cmdalt] = defstring;
330                 } else {
331                         act = ActionManager::get_action (action.c_str());
332                         if (act) {
333                                 row[function_key_columns.cmdalt] = act->get_label();
334                         } else {
335                                 row[function_key_columns.cmdalt] = defstring;
336                         }
337                 }
338
339                 action = dp.get_button_action (bid, (MackieControlProtocol::MODIFIER_SHIFT|MackieControlProtocol::MODIFIER_CONTROL));
340                 if (action.empty()) {
341                         row[function_key_columns.shiftcontrol] = defstring;
342                 } else {
343                         act = ActionManager::get_action (action.c_str());
344                         if (act) {
345                                 row[function_key_columns.shiftcontrol] = act->get_label();
346                         } else {
347                                 row[function_key_columns.shiftcontrol] = defstring;
348                         }
349                 }
350         }
351
352         function_key_editor.set_model (function_key_model);
353 }
354
355 void 
356 MackieControlProtocolGUI::action_changed (const Glib::ustring &sPath, const Glib::ustring &text, TreeModelColumnBase col)
357 {
358         Gtk::TreePath path(sPath);
359         Gtk::TreeModel::iterator row = function_key_model->get_iter(path);
360
361         if (row) {
362
363                 std::map<std::string,std::string>::iterator i = action_map.find (text);
364
365                 if (i == action_map.end()) {
366                         return;
367                 }
368
369                 Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (i->second.c_str());
370
371                 if (act) {
372                         (*row).set_value (col.index(), text);
373                 }
374                         
375         }
376 }
377
378 void
379 MackieControlProtocolGUI::surface_combo_changed ()
380 {
381         _cp.set_device (_surface_combo.get_active_text());
382 }
383
384 void
385 MackieControlProtocolGUI::profile_combo_changed ()
386 {
387         _cp.set_profile (_profile_combo.get_active_text());
388         refresh_function_key_editor ();
389 }
390
391