remove unused member
[ardour.git] / libs / surfaces / faderport8 / gui.cc
1 /*
2  * Copyright (C) 2017-2018 Robin Gareus <robin@gareus.org>
3  * Copyright (C) 2018-2019 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2019 Johannes Mueller <github@johannes-mueller.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include <gtkmm/alignment.h>
22 #include <gtkmm/label.h>
23 #include <gtkmm/liststore.h>
24 #include <gtkmm/separator.h>
25
26 #include "pbd/unwind.h"
27 #include "pbd/strsplit.h"
28 #include "pbd/file_utils.h"
29
30 #include "gtkmm2ext/actions.h"
31 #include "gtkmm2ext/action_model.h"
32 #include "gtkmm2ext/bindings.h"
33 #include "gtkmm2ext/gtk_ui.h"
34 #include "gtkmm2ext/gui_thread.h"
35 #include "gtkmm2ext/utils.h"
36
37 #include "ardour/audioengine.h"
38 #include "ardour/filesystem_paths.h"
39
40 #include "faderport8.h"
41 #include "gui.h"
42
43 #include "pbd/i18n.h"
44
45 using namespace PBD;
46 using namespace ARDOUR;
47 using namespace std;
48 using namespace Gtk;
49 using namespace Gtkmm2ext;
50 using namespace ArdourSurface::FP_NAMESPACE;
51
52 void*
53 FaderPort8::get_gui () const
54 {
55         if (!gui) {
56                 const_cast<FaderPort8*>(this)->build_gui ();
57         }
58         static_cast<Gtk::VBox*>(gui)->show_all();
59         return gui;
60 }
61
62 void
63 FaderPort8::tear_down_gui ()
64 {
65         if (gui) {
66                 Gtk::Widget *w = static_cast<Gtk::VBox*>(gui)->get_parent();
67                 if (w) {
68                         w->hide();
69                         delete w;
70                 }
71         }
72         delete static_cast<FP8GUI*> (gui);
73         gui = 0;
74 }
75
76 void
77 FaderPort8::build_gui ()
78 {
79         gui = (void*) new FP8GUI (*this);
80 }
81
82 /* ****************************************************************************/
83
84 FP8GUI::FP8GUI (FaderPort8& p)
85         : fp (p)
86         , table (2, 3)
87         , ignore_active_change (false)
88         , two_line_text_cb (_("Two Line Trackname"))
89         , auto_pluginui_cb (_("Auto Show/Hide Plugin GUIs"))
90         , action_model (ActionManager::ActionModel::instance ())
91 {
92         set_border_width (12);
93
94         table.set_row_spacings (4);
95         table.set_col_spacings (6);
96         table.set_border_width (12);
97         table.set_homogeneous (false);
98
99         std::string data_file_path;
100 #ifdef FADERPORT16
101         string name = "faderport16-small.png";
102 #elif defined FADERPORT2
103         string name = "faderport2018-small.png";
104 #else
105         string name = "faderport8-small.png";
106 #endif
107         Searchpath spath(ARDOUR::ardour_data_search_path());
108         spath.add_subdirectory_to_paths ("icons");
109         find_file (spath, name, data_file_path);
110         if (!data_file_path.empty()) {
111                 image.set (data_file_path);
112                 hpacker.pack_start (image, false, false);
113         }
114
115         Gtk::Label* l;
116         int row = 0;
117
118         input_combo.pack_start (midi_port_columns.short_name);
119         output_combo.pack_start (midi_port_columns.short_name);
120
121         build_prefs_combos ();
122         update_prefs_combos ();
123
124         input_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FP8GUI::active_port_changed), &input_combo, true));
125         output_combo.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FP8GUI::active_port_changed), &output_combo, false));
126
127         clock_combo.signal_changed().connect (sigc::mem_fun (*this, &FP8GUI::clock_mode_changed));
128         scribble_combo.signal_changed().connect (sigc::mem_fun (*this, &FP8GUI::scribble_mode_changed));
129         two_line_text_cb.signal_toggled().connect(sigc::mem_fun (*this, &FP8GUI::twolinetext_toggled));
130         auto_pluginui_cb.signal_toggled().connect(sigc::mem_fun (*this, &FP8GUI::auto_pluginui_toggled));
131
132         l = manage (new Gtk::Label);
133         l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Incoming MIDI on:")));
134         l->set_alignment (1.0, 0.5);
135         table.attach (*l, 1, 4, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
136         table.attach (input_combo, 4, 8, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
137         row++;
138
139         l = manage (new Gtk::Label);
140         l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Outgoing MIDI on:")));
141         l->set_alignment (1.0, 0.5);
142         table.attach (*l, 1, 4, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
143         table.attach (output_combo, 4, 8, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
144         row++;
145
146         Gtk::HSeparator *hsep = manage(new Gtk::HSeparator);
147         table.attach (*hsep, 0, 8, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 6);
148         row++;
149
150         hpacker.pack_start (table, true, true);
151         pack_start (hpacker, false, false);
152
153         /* actions */
154
155         int action_row = 0;
156         int action_col = 0;
157         Gtk::Alignment* align;
158
159         for (FP8Controls::UserButtonMap::const_iterator i = fp.control().user_buttons ().begin ();
160                         i != fp.control().user_buttons ().end (); ++i) {
161                 Gtk::ComboBox* user_combo = manage (new Gtk::ComboBox);
162                 build_action_combo (*user_combo, i->first);
163                 l = manage (new Gtk::Label);
164                 l->set_markup (string_compose ("<span weight=\"bold\">%1:</span>", i->second));
165                 l->set_alignment (1.0, 0.5);
166                 table.attach (*l, 3 * action_col, 3 * action_col + 1, row + action_row, row + action_row + 1, AttachOptions(FILL|EXPAND), AttachOptions (0));
167                 align = manage (new Alignment);
168                 align->set (0.0, 0.5);
169                 align->add (*user_combo);
170                 table.attach (*align, 3 * action_col + 1, 3 * action_col + 2, row + action_row, row + action_row + 1, AttachOptions(FILL|EXPAND), AttachOptions (0));
171
172 #ifdef FADERPORT2
173                 if (++action_row == 2)
174 #else
175                 if (++action_row == 4)
176 #endif
177                 {
178                         action_row = 0;
179                         ++action_col;
180                 }
181         }
182
183         for (int c = 0; c < 2; ++c) {
184                 Gtk::VSeparator *vsep = manage(new Gtk::VSeparator);
185                 table.attach (*vsep, 3 * c + 2, 3 * c + 3, row, row + 4, AttachOptions(0), AttachOptions(FILL), 6, 0);
186         }
187
188         row += 4;
189
190 #ifndef FADERPORT2
191         hsep = manage(new Gtk::HSeparator);
192         table.attach (*hsep, 0, 8, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 6);
193         row++;
194
195         l = manage (new Gtk::Label);
196         l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Clock:")));
197         l->set_alignment (1.0, 0.5);
198         table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
199         table.attach (clock_combo, 1, 4, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
200
201         table.attach (two_line_text_cb, 4, 8, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
202         row++;
203
204         l = manage (new Gtk::Label);
205         l->set_markup (string_compose ("<span weight=\"bold\">%1</span>", _("Display:")));
206         l->set_alignment (1.0, 0.5);
207         table.attach (*l, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
208         table.attach (scribble_combo, 1, 4, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
209
210         table.attach (auto_pluginui_cb, 4, 8, row, row+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
211         row++;
212 #endif
213
214         /* update the port connection combos */
215         update_port_combos ();
216
217         /* catch future changes to connection state */
218         fp.ConnectionChange.connect (connection_change_connection, invalidator (*this), boost::bind (&FP8GUI::connection_handler, this), gui_context());
219 }
220
221 FP8GUI::~FP8GUI ()
222 {
223 }
224
225 void
226 FP8GUI::connection_handler ()
227 {
228         PBD::Unwinder<bool> ici (ignore_active_change, true);
229         update_port_combos ();
230 }
231
232 void
233 FP8GUI::update_port_combos ()
234 {
235         vector<string> midi_inputs;
236         vector<string> midi_outputs;
237
238         ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsOutput|ARDOUR::IsTerminal), midi_inputs);
239         ARDOUR::AudioEngine::instance()->get_ports ("", ARDOUR::DataType::MIDI, ARDOUR::PortFlags (ARDOUR::IsInput|ARDOUR::IsTerminal), midi_outputs);
240
241         Glib::RefPtr<Gtk::ListStore> input = build_midi_port_list (midi_inputs, true);
242         Glib::RefPtr<Gtk::ListStore> output = build_midi_port_list (midi_outputs, false);
243         bool input_found = false;
244         bool output_found = false;
245         int n;
246
247         input_combo.set_model (input);
248         output_combo.set_model (output);
249
250         Gtk::TreeModel::Children children = input->children();
251         Gtk::TreeModel::Children::iterator i;
252         i = children.begin();
253         ++i; /* skip "Disconnected" */
254
255         for (n = 1;  i != children.end(); ++i, ++n) {
256                 string port_name = (*i)[midi_port_columns.full_name];
257                 if (fp.input_port()->connected_to (port_name)) {
258                         input_combo.set_active (n);
259                         input_found = true;
260                         break;
261                 }
262         }
263
264         if (!input_found) {
265                 input_combo.set_active (0); /* disconnected */
266         }
267
268         children = output->children();
269         i = children.begin();
270         ++i; /* skip "Disconnected" */
271
272         for (n = 1;  i != children.end(); ++i, ++n) {
273                 string port_name = (*i)[midi_port_columns.full_name];
274                 if (fp.output_port()->connected_to (port_name)) {
275                         output_combo.set_active (n);
276                         output_found = true;
277                         break;
278                 }
279         }
280
281         if (!output_found) {
282                 output_combo.set_active (0); /* disconnected */
283         }
284 }
285
286
287 Glib::RefPtr<Gtk::ListStore>
288 FP8GUI::build_midi_port_list (vector<string> const & ports, bool for_input)
289 {
290         Glib::RefPtr<Gtk::ListStore> store = ListStore::create (midi_port_columns);
291         TreeModel::Row row;
292
293         row = *store->append ();
294         row[midi_port_columns.full_name] = string();
295         row[midi_port_columns.short_name] = _("Disconnected");
296
297         for (vector<string>::const_iterator p = ports.begin(); p != ports.end(); ++p) {
298                 row = *store->append ();
299                 row[midi_port_columns.full_name] = *p;
300                 std::string pn = ARDOUR::AudioEngine::instance()->get_pretty_name_by_name (*p);
301                 if (pn.empty ()) {
302                         pn = (*p).substr ((*p).find (':') + 1);
303                 }
304                 row[midi_port_columns.short_name] = pn;
305         }
306
307         return store;
308 }
309
310 void
311 FP8GUI::active_port_changed (Gtk::ComboBox* combo, bool for_input)
312 {
313         if (ignore_active_change) {
314                 return;
315         }
316
317         TreeModel::iterator active = combo->get_active ();
318         string new_port = (*active)[midi_port_columns.full_name];
319
320         if (new_port.empty()) {
321                 if (for_input) {
322                         fp.input_port()->disconnect_all ();
323                 } else {
324                         fp.output_port()->disconnect_all ();
325                 }
326
327                 return;
328         }
329
330         if (for_input) {
331                 if (!fp.input_port()->connected_to (new_port)) {
332                         fp.input_port()->disconnect_all ();
333                         fp.input_port()->connect (new_port);
334                 }
335         } else {
336                 if (!fp.output_port()->connected_to (new_port)) {
337                         fp.output_port()->disconnect_all ();
338                         fp.output_port()->connect (new_port);
339                 }
340         }
341 }
342
343 void
344 FP8GUI::build_action_combo (Gtk::ComboBox& cb, FP8Controls::ButtonId id)
345 {
346         /* set the active "row" to the right value for the current button binding */
347         const string current_action = fp.get_button_action (id, false); /* lookup release action */
348         action_model.build_action_combo(cb, current_action);
349         cb.signal_changed().connect (sigc::bind (sigc::mem_fun (*this, &FP8GUI::action_changed), &cb, id));
350 }
351
352 void
353 FP8GUI::action_changed (Gtk::ComboBox* cb, FP8Controls::ButtonId id)
354 {
355         TreeModel::const_iterator row = cb->get_active ();
356         string action_path = (*row)[action_model.path()];
357         fp.set_button_action (id, false, action_path);
358 }
359
360
361 void
362 FP8GUI::build_prefs_combos ()
363 {
364         vector<string> clock_strings;
365         vector<string> scribble_strings;
366
367         //clock_strings.push_back (_("Off"));
368         clock_strings.push_back (_("Timecode"));
369         clock_strings.push_back (_("BBT"));
370         clock_strings.push_back (_("Timecode + BBT"));
371
372         scribble_strings.push_back (_("Off"));
373         scribble_strings.push_back (_("Meter"));
374         scribble_strings.push_back (_("Pan"));
375         scribble_strings.push_back (_("Meter + Pan"));
376
377         set_popdown_strings (clock_combo, clock_strings);
378         set_popdown_strings (scribble_combo, scribble_strings);
379 }
380
381 void
382 FP8GUI::update_prefs_combos ()
383 {
384         switch (fp.clock_mode()) {
385                 default:
386                         clock_combo.set_active_text (_("Off"));
387                         break;
388                 case 1:
389                         clock_combo.set_active_text (_("Timecode"));
390                         break;
391                 case 2:
392                         clock_combo.set_active_text (_("BBT"));
393                         break;
394                 case 3:
395                         clock_combo.set_active_text (_("Timecode + BBT"));
396                         break;
397         }
398
399         switch (fp.scribble_mode()) {
400                 default:
401                         scribble_combo.set_active_text (_("Off"));
402                         break;
403                 case 1:
404                         scribble_combo.set_active_text (_("Meter"));
405                         break;
406                 case 2:
407                         scribble_combo.set_active_text (_("Pan"));
408                         break;
409                 case 3:
410                         scribble_combo.set_active_text (_("Meter + Pan"));
411                         break;
412         }
413         two_line_text_cb.set_active (fp.twolinetext ());
414         auto_pluginui_cb.set_active (fp.auto_pluginui ());
415 }
416
417 void
418 FP8GUI::clock_mode_changed ()
419 {
420         string str = clock_combo.get_active_text();
421         if (str == _("BBT")) {
422                 fp.set_clock_mode (2);
423         } else if (str == _("Timecode + BBT")) {
424                 fp.set_clock_mode (3);
425         } else {
426                 fp.set_clock_mode (1);
427         }
428 }
429
430 void
431 FP8GUI::scribble_mode_changed ()
432 {
433         string str = scribble_combo.get_active_text();
434         if (str == _("Off")) {
435                 fp.set_scribble_mode (0);
436         } else if (str == _("Meter")) {
437                 fp.set_scribble_mode (1);
438         } else if (str == _("Pan")) {
439                 fp.set_scribble_mode (2);
440         } else {
441                 fp.set_scribble_mode (3);
442         }
443 }
444
445 void
446 FP8GUI::twolinetext_toggled ()
447 {
448         fp.set_two_line_text (two_line_text_cb.get_active ());
449 }
450
451
452 void
453 FP8GUI::auto_pluginui_toggled ()
454 {
455         fp.set_auto_pluginui (auto_pluginui_cb.get_active ());
456 }