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