61ca6504cb844ed5db005c89f73b7f24e0837e1f
[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 #include <gtkmm/scale.h>
29 #include <gtkmm/alignment.h>
30
31 #include "pbd/error.h"
32 #include "pbd/unwind.h"
33 #include "pbd/strsplit.h"
34 #include "pbd/stacktrace.h"
35
36 #include "gtkmm2ext/actions.h"
37 #include "gtkmm2ext/gui_thread.h"
38 #include "gtkmm2ext/utils.h"
39
40 #include "ardour/audioengine.h"
41 #include "ardour/port.h"
42 #include "ardour/rc_configuration.h"
43
44 #include "mackie_control_protocol.h"
45 #include "device_info.h"
46 #include "gui.h"
47 #include "surface.h"
48 #include "surface_port.h"
49
50 #include "i18n.h"
51
52 using namespace std;
53 using namespace Gtk;
54 using namespace ArdourSurface;
55 using namespace Mackie;
56
57 void*
58 MackieControlProtocol::get_gui () const
59 {
60         if (!_gui) {
61                 const_cast<MackieControlProtocol*>(this)->build_gui ();
62         }
63         static_cast<Gtk::Notebook*>(_gui)->show_all();
64         return _gui;
65 }
66
67 void
68 MackieControlProtocol::tear_down_gui ()
69 {
70         if (_gui) {
71                 Gtk::Widget *w = static_cast<Gtk::Widget*>(_gui)->get_parent();
72                 if (w) {
73                         w->hide();
74                         delete w;
75                 }
76         }
77         delete (MackieControlProtocolGUI*) _gui;
78         _gui = 0;
79 }
80
81 void
82 MackieControlProtocol::build_gui ()
83 {
84         _gui = (void *) new MackieControlProtocolGUI (*this);
85 }
86
87 MackieControlProtocolGUI::MackieControlProtocolGUI (MackieControlProtocol& p)
88         : _cp (p)
89         , table (2, 9)
90         , touch_sensitivity_adjustment (0, 0, 9, 1, 4)
91         , touch_sensitivity_scale (touch_sensitivity_adjustment)
92         , recalibrate_fader_button (_("Recalibrate Faders"))
93         , ipmidi_base_port_adjustment (_cp.ipmidi_base(), 0, 32767, 1, 1000)
94         , discover_button (_("Discover Mackie Devices"))
95         , _device_dependent_widget (0)
96         , ignore_active_change (false)
97 {
98         Gtk::Label* l;
99         Gtk::Alignment* align;
100         int row = 0;
101
102         set_border_width (12);
103
104         table.set_row_spacings (4);
105         table.set_col_spacings (6);
106         table.set_border_width (12);
107         table.set_homogeneous (false);
108
109         l = manage (new Gtk::Label (_("Device Type:")));
110         l->set_alignment (1.0, 0.5);
111         table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
112         table.attach (_surface_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
113         row++;
114
115         vector<string> surfaces;
116
117         for (std::map<std::string,DeviceInfo>::iterator i = DeviceInfo::device_info.begin(); i != DeviceInfo::device_info.end(); ++i) {
118                 surfaces.push_back (i->first);
119         }
120         Gtkmm2ext::set_popdown_strings (_surface_combo, surfaces);
121         _surface_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::surface_combo_changed));
122
123         _cp.DeviceChanged.connect (device_change_connection, invalidator (*this), boost::bind (&MackieControlProtocolGUI::device_changed, this), gui_context());
124         _cp.ConnectionChange.connect (connection_change_connection, invalidator (*this), boost::bind (&MackieControlProtocolGUI::connection_handler, this), gui_context());
125
126         ipmidi_base_port_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::ipmidi_spinner_changed));
127
128         /* device-dependent part */
129
130         device_dependent_row = row;
131
132         if (_device_dependent_widget) {
133                 table.remove (*_device_dependent_widget);
134                 _device_dependent_widget = 0;
135         }
136
137         _device_dependent_widget = device_dependent_widget ();
138         table.attach (*_device_dependent_widget, 0, 12, row, row+1, AttachOptions(0), AttachOptions(0), 0, 0);
139         row++;
140
141         /* back to the boilerplate */
142
143         RadioButtonGroup rb_group = absolute_touch_mode_button.get_group();
144         touch_move_mode_button.set_group (rb_group);
145
146         recalibrate_fader_button.signal_clicked().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::recalibrate_faders));
147         backlight_button.signal_clicked().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::toggle_backlight));
148
149         touch_sensitivity_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::touch_sensitive_change));
150         touch_sensitivity_scale.set_update_policy (Gtk::UPDATE_DISCONTINUOUS);
151
152         l = manage (new Gtk::Label (_("Button click")));
153         l->set_alignment (1.0, 0.5);
154         table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
155         align = manage (new Alignment);
156         align->set (0.0, 0.5);
157         align->add (relay_click_button);
158         table.attach (*align, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
159         row++;
160
161         l = manage (new Gtk::Label (_("Backlight")));
162         l->set_alignment (1.0, 0.5);
163         table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
164         align = manage (new Alignment);
165         align->set (0.0, 0.5);
166         align->add (backlight_button);
167         table.attach (*align, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
168         row++;
169
170         l = manage (new Gtk::Label (_("Send Fader Position Only When Touched")));
171         l->set_alignment (1.0, 0.5);
172         table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
173         align = manage (new Alignment);
174         align->set (0.0, 0.5);
175         align->add (absolute_touch_mode_button);
176         table.attach (*align, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
177         row++;
178
179         l = manage (new Gtk::Label (_("Send Fader Position When Moved")));
180         l->set_alignment (1.0, 0.5);
181         table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
182         align = manage (new Alignment);
183         align->set (0.0, 0.5);
184         align->add (touch_move_mode_button);
185         table.attach (*align, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
186         row++;
187
188         l = manage (new Gtk::Label (_("Fader Touch Sense Sensitivity")));
189         l->set_alignment (1.0, 0.5);
190         table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
191         touch_sensitivity_scale.property_digits() = 0;
192         touch_sensitivity_scale.property_draw_value() = false;
193         table.attach (touch_sensitivity_scale, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
194         row++;
195         table.attach (recalibrate_fader_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
196         row++;
197
198
199         table.attach (discover_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
200         discover_button.signal_clicked().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::discover_clicked));
201         row++;
202
203         vector<string> profiles;
204
205         for (std::map<std::string,DeviceProfile>::iterator i = DeviceProfile::device_profiles.begin(); i != DeviceProfile::device_profiles.end(); ++i) {
206                 profiles.push_back (i->first);
207         }
208         Gtkmm2ext::set_popdown_strings (_profile_combo, profiles);
209         _profile_combo.set_active_text (p.device_profile().name());
210         _profile_combo.signal_changed().connect (sigc::mem_fun (*this, &MackieControlProtocolGUI::profile_combo_changed));
211
212         append_page (table, _("Device Setup"));
213         table.show_all();
214
215         /* function key editor */
216
217         VBox* fkey_packer = manage (new VBox);
218         HBox* profile_packer = manage (new HBox);
219         HBox* observation_packer = manage (new HBox);
220
221         l = manage (new Gtk::Label (_("Profile/Settings:")));
222         profile_packer->pack_start (*l, false, false);
223         profile_packer->pack_start (_profile_combo, true, true);
224         profile_packer->set_spacing (12);
225         profile_packer->set_border_width (12);
226
227         l = manage (new Gtk::Label (_("* Button available at the original Mackie MCU PRO or current device if enabled (NOT implemented yet). Device specific name presented.")));
228         observation_packer->pack_start (*l, false, false);
229
230         fkey_packer->pack_start (*profile_packer, false, false);
231         fkey_packer->pack_start (function_key_scroller, true, true);
232         fkey_packer->pack_start (*observation_packer, false, false);
233         fkey_packer->set_spacing (12);
234         function_key_scroller.property_shadow_type() = Gtk::SHADOW_NONE;
235         function_key_scroller.add (function_key_editor);
236         append_page (*fkey_packer, _("Function Keys"));
237
238         build_available_action_menu ();
239         build_function_key_editor ();
240         refresh_function_key_editor ();
241         fkey_packer->show_all();
242 }
243
244 void
245 MackieControlProtocolGUI::connection_handler ()
246 {
247         /* ignore all changes to combobox active strings here, because we're
248            updating them to match a new ("external") reality - we were called
249            because port connections have changed.
250         */
251
252         PBD::Unwinder<bool> ici (ignore_active_change, true);
253
254         vector<Gtk::ComboBox*>::iterator ic;
255         vector<Gtk::ComboBox*>::iterator oc;
256
257         vector<string> midi_inputs;
258         vector<string> midi_outputs;
259
260         ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsTerminal), midi_inputs);
261         ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsTerminal), midi_outputs);
262
263         for (ic = input_combos.begin(), oc = output_combos.begin(); ic != input_combos.end() && oc != output_combos.end(); ++ic, ++oc) {
264
265                 boost::shared_ptr<Surface> surface = _cp.get_surface_by_raw_pointer ((*ic)->get_data ("surface"));
266
267                 if (surface) {
268                         update_port_combos (midi_inputs, midi_outputs, *ic, *oc, surface);
269                 }
270         }
271 }
272
273 void
274 MackieControlProtocolGUI::update_port_combos (vector<string> const& midi_inputs, vector<string> const& midi_outputs,
275                                               Gtk::ComboBox* input_combo,
276                                               Gtk::ComboBox* output_combo,
277                                               boost::shared_ptr<Surface> surface)
278 {
279         Glib::RefPtr<Gtk::ListStore> input = build_midi_port_list (midi_inputs, true);
280         Glib::RefPtr<Gtk::ListStore> output = build_midi_port_list (midi_outputs, false);
281         bool input_found = false;
282         bool output_found = false;
283         int n;
284
285         input_combo->set_model (input);
286         output_combo->set_model (output);
287
288         Gtk::TreeModel::Children children = input->children();
289         Gtk::TreeModel::Children::iterator i;
290         i = children.begin();
291         ++i; /* skip "Disconnected" */
292
293
294         for (n = 1;  i != children.end(); ++i, ++n) {
295                 string port_name = (*i)[midi_port_columns.full_name];
296                 if (surface->port().input().connected_to (port_name)) {
297                         input_combo->set_active (n);
298                         input_found = true;
299                         break;
300                 }
301         }
302
303         if (!input_found) {
304                 input_combo->set_active (0); /* disconnected */
305         }
306
307         children = output->children();
308         i = children.begin();
309         ++i; /* skip "Disconnected" */
310
311         for (n = 1;  i != children.end(); ++i, ++n) {
312                 string port_name = (*i)[midi_port_columns.full_name];
313                 if (surface->port().output().connected_to (port_name)) {
314                         output_combo->set_active (n);
315                         output_found = true;
316                         break;
317                 }
318         }
319
320         if (!output_found) {
321                 output_combo->set_active (0); /* disconnected */
322         }
323 }
324
325 Gtk::Widget*
326 MackieControlProtocolGUI::device_dependent_widget ()
327 {
328         Gtk::Table* dd_table;
329         Gtk::Label* l;
330         int row = 0;
331
332         uint32_t n_surfaces = 1 + _cp.device_info().extenders();
333
334         if (!_cp.device_info().uses_ipmidi()) {
335                 dd_table = Gtk::manage (new Gtk::Table (n_surfaces, 2));
336         } else {
337                 dd_table = Gtk::manage (new Gtk::Table (1, 2));
338         }
339
340         dd_table = Gtk::manage (new Gtk::Table (2, n_surfaces));
341         dd_table->set_row_spacings (4);
342         dd_table->set_col_spacings (6);
343         dd_table->set_border_width (12);
344
345         _surface_combo.set_active_text (_cp.device_info().name());
346
347         vector<string> midi_inputs;
348         vector<string> midi_outputs;
349
350         ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsPhysical), midi_inputs);
351         ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsPhysical), midi_outputs);
352
353         input_combos.clear ();
354         output_combos.clear ();
355
356         if (!_cp.device_info().uses_ipmidi()) {
357
358                 for (uint32_t n = 0; n < n_surfaces; ++n) {
359
360                         boost::shared_ptr<Surface> surface = _cp.nth_surface (n);
361
362                         if (!surface) {
363                                 PBD::fatal << string_compose (_("programming error: %1\n"), string_compose ("n=%1 surface not found!", n)) << endmsg;
364                                 /*NOTREACHED*/
365                         }
366
367                         Gtk::ComboBox* input_combo = manage (new Gtk::ComboBox);
368                         Gtk::ComboBox* output_combo = manage (new Gtk::ComboBox);
369
370                         update_port_combos (midi_inputs, midi_outputs, input_combo, output_combo, surface);
371
372                         input_combo->pack_start (midi_port_columns.short_name);
373                         input_combo->set_data ("surface", surface.get());
374                         input_combos.push_back (input_combo);
375                         output_combo->pack_start (midi_port_columns.short_name);
376                         output_combo->set_data ("surface", surface.get());
377                         output_combos.push_back (output_combo);
378
379                         boost::weak_ptr<Surface> ws (surface);
380                         input_combo->signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &MackieControlProtocolGUI::active_port_changed), input_combo, ws, true));
381                         output_combo->signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &MackieControlProtocolGUI::active_port_changed), output_combo, ws, false));
382
383                         string send_string;
384                         string receive_string;
385
386                         if (n_surfaces > 1) {
387                                 if (n == 0) {
388                                         send_string = _("Main surface sends via:");
389                                         receive_string = _("Main surface receives via:");
390                                 } else {
391                                         send_string = string_compose (_("Extender %1 sends via:"), n);
392                                         receive_string = string_compose (_("Extender %1 receives via:"), n);
393                                 }
394                         } else {
395                                 send_string = _("Surface sends via:");
396                                 receive_string = _("Surface receives via:");
397                         }
398
399                         l = manage (new Gtk::Label (send_string));
400                         l->set_alignment (1.0, 0.5);
401                         dd_table->attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
402                         dd_table->attach (*input_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
403                         row++;
404
405                         l = manage (new Gtk::Label (receive_string));
406                         l->set_alignment (1.0, 0.5);
407                         dd_table->attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
408                         dd_table->attach (*output_combo, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
409                         row++;
410                 }
411
412         } else {
413
414                 l = manage (new Gtk::Label (_("ipMIDI Port (lowest)")));
415                 l->set_alignment (1.0, 0.5);
416
417                 Gtk::SpinButton*  ipmidi_base_port_spinner = manage (new Gtk::SpinButton (ipmidi_base_port_adjustment));
418                 dd_table->attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
419                 dd_table->attach (*ipmidi_base_port_spinner, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions (0));
420                 row++;
421         }
422
423         return dd_table;
424 }
425
426 CellRendererCombo*
427 MackieControlProtocolGUI::make_action_renderer (Glib::RefPtr<TreeStore> model, Gtk::TreeModelColumnBase column)
428 {
429         CellRendererCombo* renderer = manage (new CellRendererCombo);
430         renderer->property_model() = model;
431         renderer->property_editable() = true;
432         renderer->property_text_column() = 0;
433         renderer->property_has_entry() = false;
434         renderer->signal_edited().connect (sigc::bind (sigc::mem_fun(*this, &MackieControlProtocolGUI::action_changed), column));
435
436         return renderer;
437 }
438
439 void
440 MackieControlProtocolGUI::build_available_action_menu ()
441 {
442         /* build a model of all available actions (needs to be tree structured
443          * more)
444          */
445
446         available_action_model = TreeStore::create (available_action_columns);
447
448         vector<string> paths;
449         vector<string> labels;
450         vector<string> tooltips;
451         vector<string> keys;
452         vector<AccelKey> bindings;
453         typedef std::map<string,TreeIter> NodeMap;
454         NodeMap nodes;
455         NodeMap::iterator r;
456
457         ActionManager::get_all_actions (labels, paths, tooltips, keys, bindings);
458
459         vector<string>::iterator k;
460         vector<string>::iterator p;
461         vector<string>::iterator t;
462         vector<string>::iterator l;
463
464         available_action_model->clear ();
465
466         /* Because there are button bindings built in that are not
467         in the key binding map, there needs to be a way to undo
468         a profile edit. */
469         TreeIter rowp;
470         TreeModel::Row parent;
471         rowp = available_action_model->append();
472         parent = *(rowp);
473         parent[available_action_columns.name] = _("Remove Binding");
474
475         /* Key aliasing */
476
477         rowp = available_action_model->append();
478         parent = *(rowp);
479         parent[available_action_columns.name] = _("Shift");
480         rowp = available_action_model->append();
481         parent = *(rowp);
482         parent[available_action_columns.name] = _("Control");
483         rowp = available_action_model->append();
484         parent = *(rowp);
485         parent[available_action_columns.name] = _("Option");
486         rowp = available_action_model->append();
487         parent = *(rowp);
488         parent[available_action_columns.name] = _("CmdAlt");
489
490
491         for (l = labels.begin(), k = keys.begin(), p = paths.begin(), t = tooltips.begin(); l != labels.end(); ++k, ++p, ++t, ++l) {
492
493                 TreeModel::Row row;
494                 vector<string> parts;
495
496                 parts.clear ();
497
498                 split (*p, parts, '/');
499
500                 if (parts.empty()) {
501                         continue;
502                 }
503
504                 //kinda kludgy way to avoid displaying menu items as mappable
505                 if ( parts[1] == _("Main_menu") )
506                         continue;
507                 if ( parts[1] == _("JACK") )
508                         continue;
509                 if ( parts[1] == _("redirectmenu") )
510                         continue;
511                 if ( parts[1] == _("Editor_menus") )
512                         continue;
513                 if ( parts[1] == _("RegionList") )
514                         continue;
515                 if ( parts[1] == _("ProcessorMenu") )
516                         continue;
517
518                 if ((r = nodes.find (parts[1])) == nodes.end()) {
519
520                         /* top level is missing */
521
522                         TreeIter rowp;
523                         TreeModel::Row parent;
524                         rowp = available_action_model->append();
525                         nodes[parts[1]] = rowp;
526                         parent = *(rowp);
527                         parent[available_action_columns.name] = parts[1];
528
529                         row = *(available_action_model->append (parent.children()));
530
531                 } else {
532
533                         row = *(available_action_model->append ((*r->second)->children()));
534
535                 }
536
537                 /* add this action */
538
539                 if (l->empty ()) {
540                         row[available_action_columns.name] = *t;
541                         action_map[*t] = *p;
542                 } else {
543                         row[available_action_columns.name] = *l;
544                         action_map[*l] = *p;
545                 }
546
547                 row[available_action_columns.path] = (*p);
548         }
549 }
550
551 void
552 MackieControlProtocolGUI::build_function_key_editor ()
553 {
554         function_key_editor.append_column (_("Key"), function_key_columns.name);
555
556         TreeViewColumn* col;
557         CellRendererCombo* renderer;
558
559         renderer = make_action_renderer (available_action_model, function_key_columns.plain);
560         col = manage (new TreeViewColumn (_("Plain"), *renderer));
561         col->add_attribute (renderer->property_text(), function_key_columns.plain);
562         function_key_editor.append_column (*col);
563
564         renderer = make_action_renderer (available_action_model, function_key_columns.shift);
565         col = manage (new TreeViewColumn (_("Shift"), *renderer));
566         col->add_attribute (renderer->property_text(), function_key_columns.shift);
567         function_key_editor.append_column (*col);
568
569         renderer = make_action_renderer (available_action_model, function_key_columns.control);
570         col = manage (new TreeViewColumn (_("Control"), *renderer));
571         col->add_attribute (renderer->property_text(), function_key_columns.control);
572         function_key_editor.append_column (*col);
573
574         renderer = make_action_renderer (available_action_model, function_key_columns.option);
575         col = manage (new TreeViewColumn (_("Option"), *renderer));
576         col->add_attribute (renderer->property_text(), function_key_columns.option);
577         function_key_editor.append_column (*col);
578
579         renderer = make_action_renderer (available_action_model, function_key_columns.cmdalt);
580         col = manage (new TreeViewColumn (_("Cmd/Alt"), *renderer));
581         col->add_attribute (renderer->property_text(), function_key_columns.cmdalt);
582         function_key_editor.append_column (*col);
583
584         renderer = make_action_renderer (available_action_model, function_key_columns.shiftcontrol);
585         col = manage (new TreeViewColumn (_("Shift+Control"), *renderer));
586         col->add_attribute (renderer->property_text(), function_key_columns.shiftcontrol);
587         function_key_editor.append_column (*col);
588
589         function_key_model = ListStore::create (function_key_columns);
590         function_key_editor.set_model (function_key_model);
591 }
592
593 void
594 MackieControlProtocolGUI::refresh_function_key_editor ()
595 {
596         function_key_editor.set_model (Glib::RefPtr<TreeModel>());
597         function_key_model->clear ();
598
599         /* now fill with data */
600
601         TreeModel::Row row;
602         DeviceProfile dp (_cp.device_profile());
603         DeviceInfo di;
604
605         for (int n = 0; n < Mackie::Button::FinalGlobalButton; ++n) {
606
607                 Mackie::Button::ID bid = (Mackie::Button::ID) n;
608
609                 row = *(function_key_model->append());
610                 if (di.global_buttons().find (bid) == di.global_buttons().end()) {
611                         row[function_key_columns.name] = Mackie::Button::id_to_name (bid);
612                 } else {
613                         row[function_key_columns.name] = di.get_global_button_name (bid) + "*";
614                 }
615                 row[function_key_columns.id] = bid;
616
617                 Glib::RefPtr<Gtk::Action> act;
618                 string action;
619                 const string defstring = "\u2022";
620
621                 action = dp.get_button_action (bid, 0);
622                 if (action.empty()) {
623                         row[function_key_columns.plain] = defstring;
624                 } else {
625                         if (action.find ('/') == string::npos) {
626                                 /* Probably a key alias */
627                                 row[function_key_columns.plain] = action;
628                         } else {
629
630                                 act = ActionManager::get_action (action.c_str());
631                                 if (act) {
632                                         row[function_key_columns.plain] = act->get_label();
633                                 } else {
634                                         row[function_key_columns.plain] = defstring;
635                                 }
636                         }
637                 }
638
639                 action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_CONTROL);
640                 if (action.empty()) {
641                         row[function_key_columns.control] = defstring;
642                 } else {
643                         if (action.find ('/') == string::npos) {
644                                 /* Probably a key alias */
645                                 row[function_key_columns.control] = action;
646                         } else {
647                                 act = ActionManager::get_action (action.c_str());
648                                 if (act) {
649                                         row[function_key_columns.control] = act->get_label();
650                                 } else {
651                                         row[function_key_columns.control] = defstring;
652                                 }
653                         }
654                 }
655
656                 action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_SHIFT);
657                 if (action.empty()) {
658                         row[function_key_columns.shift] = defstring;
659                 } else {
660                         if (action.find ('/') == string::npos) {
661                                 /* Probably a key alias */
662                                 row[function_key_columns.shift] = action;
663                         } else {
664                                 act = ActionManager::get_action (action.c_str());
665                                 if (act) {
666                                         row[function_key_columns.shift] = act->get_label();
667                                 } else {
668                                         row[function_key_columns.shift] = defstring;
669                                 }
670                         }
671                 }
672
673                 action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_OPTION);
674                 if (action.empty()) {
675                         row[function_key_columns.option] = defstring;
676                 } else {
677                         if (action.find ('/') == string::npos) {
678                                 /* Probably a key alias */
679                                 row[function_key_columns.option] = action;
680                         } else {
681                                 act = ActionManager::get_action (action.c_str());
682                                 if (act) {
683                                         row[function_key_columns.option] = act->get_label();
684                                 } else {
685                                         row[function_key_columns.option] = defstring;
686                                 }
687                         }
688                 }
689
690                 action = dp.get_button_action (bid, MackieControlProtocol::MODIFIER_CMDALT);
691                 if (action.empty()) {
692                         row[function_key_columns.cmdalt] = defstring;
693                 } else {
694                         if (action.find ('/') == string::npos) {
695                                 /* Probably a key alias */
696                                 row[function_key_columns.cmdalt] = action;
697                         } else {
698                                 act = ActionManager::get_action (action.c_str());
699                                 if (act) {
700                                         row[function_key_columns.cmdalt] = act->get_label();
701                                 } else {
702                                         row[function_key_columns.cmdalt] = defstring;
703                                 }
704                         }
705                 }
706
707                 action = dp.get_button_action (bid, (MackieControlProtocol::MODIFIER_SHIFT|MackieControlProtocol::MODIFIER_CONTROL));
708                 if (action.empty()) {
709                         row[function_key_columns.shiftcontrol] = defstring;
710                 } else {
711                         act = ActionManager::get_action (action.c_str());
712                         if (act) {
713                                 row[function_key_columns.shiftcontrol] = act->get_label();
714                         } else {
715                                 row[function_key_columns.shiftcontrol] = defstring;
716                         }
717                 }
718         }
719
720         function_key_editor.set_model (function_key_model);
721 }
722
723 void
724 MackieControlProtocolGUI::action_changed (const Glib::ustring &sPath, const Glib::ustring &text, TreeModelColumnBase col)
725 {
726         // Remove Binding is not in the action map but still valid
727         bool remove (false);
728         if ( text == "Remove Binding") {
729                 remove = true;
730         }
731         Gtk::TreePath path(sPath);
732         Gtk::TreeModel::iterator row = function_key_model->get_iter(path);
733
734         if (row) {
735
736                 std::map<std::string,std::string>::iterator i = action_map.find (text);
737
738                 if (i == action_map.end()) {
739                         if (!remove) {
740                                 return;
741                         }
742                 }
743                 Glib::RefPtr<Gtk::Action> act = ActionManager::get_action (i->second.c_str());
744
745                 if (act || remove) {
746                         /* update visible text, using string supplied by
747                            available action model so that it matches and is found
748                            within the model.
749                         */
750                         if (remove) {
751                                 Glib::ustring dot = "\u2022";
752                                 (*row).set_value (col.index(), dot);
753                         } else {
754                                 (*row).set_value (col.index(), text);
755                         }
756
757                         /* update the current DeviceProfile, using the full
758                          * path
759                          */
760
761                         int modifier;
762
763                         switch (col.index()) {
764                         case 3:
765                                 modifier = MackieControlProtocol::MODIFIER_SHIFT;
766                                 break;
767                         case 4:
768                                 modifier = MackieControlProtocol::MODIFIER_CONTROL;
769                                 break;
770                         case 5:
771                                 modifier = MackieControlProtocol::MODIFIER_OPTION;
772                                 break;
773                         case 6:
774                                 modifier = MackieControlProtocol::MODIFIER_CMDALT;
775                                 break;
776                         case 7:
777                                 modifier = (MackieControlProtocol::MODIFIER_SHIFT|MackieControlProtocol::MODIFIER_CONTROL);
778                                 break;
779                         default:
780                                 modifier = 0;
781                         }
782
783                         if (remove) {
784                                 _cp.device_profile().set_button_action ((*row)[function_key_columns.id], modifier, "");
785                         } else {
786                                 _cp.device_profile().set_button_action ((*row)[function_key_columns.id], modifier, i->second);
787                         }
788
789                 } else {
790                         std::cerr << "no such action\n";
791                 }
792         }
793 }
794
795 void
796 MackieControlProtocolGUI::surface_combo_changed ()
797 {
798         _cp.set_device (_surface_combo.get_active_text(), false);
799 }
800
801 void
802 MackieControlProtocolGUI::device_changed ()
803 {
804         if (_device_dependent_widget) {
805                 table.remove (*_device_dependent_widget);
806                 _device_dependent_widget = 0;
807         }
808
809         _device_dependent_widget = device_dependent_widget ();
810         _device_dependent_widget->show_all ();
811
812         table.attach (*_device_dependent_widget, 0, 12, device_dependent_row, device_dependent_row+1, AttachOptions(0), AttachOptions(0), 0, 0);
813 }
814
815 void
816 MackieControlProtocolGUI::profile_combo_changed ()
817 {
818         string profile = _profile_combo.get_active_text();
819
820         _cp.set_profile (profile);
821
822         refresh_function_key_editor ();
823 }
824
825 void
826 MackieControlProtocolGUI::ipmidi_spinner_changed ()
827 {
828         _cp.set_ipmidi_base ((int16_t) lrintf (ipmidi_base_port_adjustment.get_value()));
829 }
830
831 void
832 MackieControlProtocolGUI::discover_clicked ()
833 {
834         /* this should help to get things started */
835         _cp.ping_devices ();
836 }
837
838 void
839 MackieControlProtocolGUI::recalibrate_faders ()
840 {
841         _cp.recalibrate_faders ();
842 }
843
844 void
845 MackieControlProtocolGUI::toggle_backlight ()
846 {
847         _cp.toggle_backlight ();
848 }
849
850 void
851 MackieControlProtocolGUI::touch_sensitive_change ()
852 {
853         int sensitivity = (int) touch_sensitivity_adjustment.get_value ();
854         _cp.set_touch_sensitivity (sensitivity);
855 }
856
857 Glib::RefPtr<Gtk::ListStore>
858 MackieControlProtocolGUI::build_midi_port_list (vector<string> const & ports, bool for_input)
859 {
860         Glib::RefPtr<Gtk::ListStore> store = ListStore::create (midi_port_columns);
861         TreeModel::Row row;
862
863         row = *store->append ();
864         row[midi_port_columns.full_name] = string();
865         row[midi_port_columns.short_name] = _("Disconnected");
866
867         for (vector<string>::const_iterator p = ports.begin(); p != ports.end(); ++p) {
868                 row = *store->append ();
869                 row[midi_port_columns.full_name] = *p;
870                 std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p);
871                 if (pn.empty ()) {
872                         pn = (*p).substr ((*p).find (':') + 1);
873                 }
874                 row[midi_port_columns.short_name] = pn;
875         }
876
877         return store;
878 }
879
880 void
881 MackieControlProtocolGUI::active_port_changed (Gtk::ComboBox* combo, boost::weak_ptr<Surface> ws, bool for_input)
882 {
883         if (ignore_active_change) {
884                 return;
885         }
886
887         boost::shared_ptr<Surface> surface = ws.lock();
888
889         if (!surface) {
890                 return;
891         }
892
893         TreeModel::iterator active = combo->get_active ();
894         string new_port = (*active)[midi_port_columns.full_name];
895
896         if (new_port.empty()) {
897                 if (for_input) {
898                         surface->port().input().disconnect_all ();
899                 } else {
900                         surface->port().output().disconnect_all ();
901                 }
902
903                 return;
904         }
905
906         if (for_input) {
907                 if (!surface->port().input().connected_to (new_port)) {
908                         surface->port().input().disconnect_all ();
909                         surface->port().input().connect (new_port);
910                 }
911         } else {
912                 if (!surface->port().output().connected_to (new_port)) {
913                         surface->port().output().disconnect_all ();
914                         surface->port().output().connect (new_port);
915                 }
916         }
917 }