1 /*
2  * Copyright (C) 2016-2018 Len Ovens <len@ovenwerks.net>
3  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <iostream>
21 #include <list>
22 #include <string>
23 #include <vector>
24 
25 #include <errno.h>
26 
27 #include "pbd/file_utils.h"
28 
29 #include <gtkmm/box.h>
30 #include <gtkmm/notebook.h>
31 #include <gtkmm/table.h>
32 #include <gtkmm/label.h>
33 #include <gtkmm/button.h>
34 #include <gtkmm/spinbutton.h>
35 #include <gtkmm/comboboxtext.h>
36 
37 #include "gtkmm2ext/gtk_ui.h"
38 #include "gtkmm2ext/gui_thread.h"
39 #include "gtkmm2ext/utils.h"
40 
41 #include "ardour/filesystem_paths.h"
42 
43 #include "osc.h"
44 #include "osc_gui.h"
45 
46 #include "pbd/i18n.h"
47 
48 using namespace PBD;
49 using namespace ARDOUR;
50 using namespace Gtk;
51 using namespace Gtkmm2ext;
52 using namespace ArdourSurface;
53 
OSC_GUI(OSC & p)54 OSC_GUI::OSC_GUI (OSC& p)
55 	: cp (p)
56 {
57 	int n = 0; // table row
58 	Table* table = manage (new Table);
59 	Label* label;
60 	table->set_row_spacings (16);
61 	table->set_col_spacings (6);
62 	table->set_border_width (12);
63 	get_session ();
64 	preset_busy = true;
65 
66 	// show our url
67 	label = manage (new Gtk::Label(_("Connection:")));
68 	label->set_alignment(1, .5);
69 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
70 	label = manage (new Gtk::Label(cp.get_server_url()));
71 	table->attach (*label, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
72 	++n;
73 
74 	// show and set port to auto (default) or manual (one surface only)
75 	label = manage (new Gtk::Label(_("Port Mode:")));
76 	label->set_alignment(1, .5);
77 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
78 	table->attach (portmode_combo, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
79 	std::vector<std::string> portmode_options;
80 	portmode_options.push_back (_("Auto - Reply to Originating Port"));
81 	portmode_options.push_back (_("Manual - Specify Below"));
82 
83 	set_popdown_strings (portmode_combo, portmode_options);
84 	portmode_combo.set_active ((int)cp.get_portmode());
85 	++n;
86 
87 	// port entry box
88 	label = manage (new Gtk::Label(_("Reply Manual Port:")));
89 	label->set_alignment(1, .5);
90 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
91 	table->attach (port_entry, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
92 	port_entry.set_range(1024, 0xffff);
93 	port_entry.set_increments (1, 100);
94 	port_entry.set_text(cp.get_remote_port().c_str());
95 	if (!cp.get_portmode()) {
96 		port_entry.set_sensitive (false);
97 	}
98 	++n;
99 
100 	// default banksize setting
101 	label = manage (new Gtk::Label(_("Bank Size:")));
102 	label->set_alignment(1, .5);
103 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
104 	table->attach (bank_entry, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
105 	bank_entry.set_range (0, 0xffff);
106 	bank_entry.set_increments (1, 8);
107 	bank_entry.set_value (cp.get_banksize());
108 
109 	++n;
110 
111 	// default send page setting
112 	label = manage (new Gtk::Label(_("Send Page Size:")));
113 	label->set_alignment(1, .5);
114 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
115 	table->attach (send_page_entry, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
116 	send_page_entry.set_range (0, 0xffff);
117 	send_page_entry.set_increments (1, 8);
118 	send_page_entry.set_value (cp.get_send_size());
119 
120 	++n;
121 
122 	// default plugin page setting
123 	label = manage (new Gtk::Label(_("Plugin Page Size:")));
124 	label->set_alignment(1, .5);
125 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
126 	table->attach (plugin_page_entry, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
127 	plugin_page_entry.set_range (0, 0xffff);
128 	plugin_page_entry.set_increments (1, 8);
129 	plugin_page_entry.set_value (cp.get_send_size());
130 
131 	++n;
132 
133 	// Gain Mode
134 	label = manage (new Gtk::Label(_("Gain Mode:")));
135 	label->set_alignment(1, .5);
136 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
137 	table->attach (gainmode_combo, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
138 	std::vector<std::string> gainmode_options;
139 	gainmode_options.push_back (_("/strip/gain (dB)"));
140 	gainmode_options.push_back (_("/strip/fader (Position) and dB in control name"));
141 	gainmode_options.push_back (_("/strip/fader (Position) and /strip/gain (dB)"));
142 	gainmode_options.push_back (_("/strip/fader (Position)"));
143 
144 	set_popdown_strings (gainmode_combo, gainmode_options);
145 	gainmode_combo.set_active ((int)cp.get_gainmode());
146 	++n;
147 
148 	// debug setting
149 	label = manage (new Gtk::Label(_("Debug:")));
150 	label->set_alignment(1, .5);
151 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
152 	table->attach (debug_combo, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
153 
154 	std::vector<std::string> debug_options;
155 	debug_options.push_back (_("Off"));
156 	debug_options.push_back (_("Log invalid messages"));
157 	debug_options.push_back (_("Log all messages"));
158 	debug_options.push_back (_("Print surface information to Log window"));
159 
160 	set_popdown_strings (debug_combo, debug_options);
161 	debug_combo.set_active ((int)cp.get_debug_mode());
162 	++n;
163 
164 	// Preset loader combo
165 	label = manage (new Gtk::Label(_("Preset:")));
166 	label->set_alignment(1, .5);
167 	table->attach (*label, 0, 1, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
168 	table->attach (preset_combo, 1, 2, n, n+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
169 
170 	preset_files.clear();
171 	// no files for these two
172 	preset_options.push_back (_("Last Loaded Session"));
173 	preset_options.push_back (_("Ardour Factory Setting"));
174 	// user is special it appears in menu even if no file is present
175 	preset_options.push_back ("User");
176 	preset_files["User"] = "";
177 	// scan for OSC .preset files
178 	scan_preset_files ();
179 
180 	set_popdown_strings (preset_combo, preset_options);
181 	preset_combo.set_active (0);
182 	preset_combo.signal_changed().connect (sigc::mem_fun (*this, &OSC_GUI::preset_changed));
183 	++n;
184 
185 	table->show_all ();
186 	append_page (*table, _("OSC Setup"));
187 
188 	debug_combo.signal_changed().connect (sigc::mem_fun (*this, &OSC_GUI::debug_changed));
189 	portmode_combo.signal_changed().connect (sigc::mem_fun (*this, &OSC_GUI::portmode_changed));
190 	gainmode_combo.signal_changed().connect (sigc::mem_fun (*this, &OSC_GUI::gainmode_changed));
191 	port_entry.signal_changed().connect (sigc::mem_fun (*this, &OSC_GUI::port_changed));
192 	port_entry.signal_focus_out_event().connect (sigc::mem_fun (*this, &OSC_GUI::port_focus_out));
193 	bank_entry.signal_changed().connect (sigc::mem_fun (*this, &OSC_GUI::bank_changed));
194 	send_page_entry.signal_changed().connect (sigc::mem_fun (*this, &OSC_GUI::send_page_changed));
195 	plugin_page_entry.signal_changed().connect (sigc::mem_fun (*this, &OSC_GUI::plugin_page_changed));
196 
197 	// Strip Types Calculate Page
198 	int stn = 0; // table row
199 	Table* sttable = manage (new Table);
200 	sttable->set_row_spacings (8);
201 	sttable->set_col_spacings (6);
202 	sttable->set_border_width (25);
203 
204 	// show our url
205 	label = manage (new Gtk::Label(_("Select Desired Types of Tracks")));
206 	sttable->attach (*label, 0, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
207 	++stn;
208 
209 	label = manage (new Gtk::Label(_("Strip Types Value:")));
210 	label->set_alignment(1, .5);
211 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 15);
212 	calculate_strip_types ();
213 	current_strip_types.set_width_chars(10);
214 	sttable->attach (current_strip_types, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 15);
215 	++stn;
216 
217 	label = manage (new Gtk::Label(_("Audio Tracks:")));
218 	label->set_alignment(1, .5);
219 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
220 	sttable->attach (audio_tracks, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
221 	++stn;
222 
223 	label = manage (new Gtk::Label(_("Midi Tracks:")));
224 	label->set_alignment(1, .5);
225 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
226 	sttable->attach (midi_tracks, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
227 	++stn;
228 
229 	label = manage (new Gtk::Label(_("Audio Busses:")));
230 	label->set_alignment(1, .5);
231 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
232 	sttable->attach (audio_buses, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
233 	++stn;
234 
235 	label = manage (new Gtk::Label(_("Foldback Busses:")));
236 	label->set_alignment(1, .5);
237 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
238 	sttable->attach (foldback_busses, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
239 	++stn;
240 
241 	label = manage (new Gtk::Label(_("Midi Busses:")));
242 	label->set_alignment(1, .5);
243 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
244 	sttable->attach (midi_buses, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
245 	++stn;
246 
247 	label = manage (new Gtk::Label(_("Control Masters:")));
248 	label->set_alignment(1, .5);
249 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
250 	sttable->attach (control_masters, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
251 	++stn;
252 
253 	label = manage (new Gtk::Label(_("Master (use /master instead):")));
254 	label->set_alignment(1, .5);
255 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
256 	sttable->attach (master_type, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
257 	++stn;
258 
259 	label = manage (new Gtk::Label(_("Monitor (use /monitor instead):")));
260 	label->set_alignment(1, .5);
261 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
262 	sttable->attach (monitor_type, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
263 	++stn;
264 
265 	label = manage (new Gtk::Label(_("Selected Tracks (use for selected tracks only):")));
266 	label->set_alignment(1, .5);
267 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
268 	sttable->attach (selected_tracks, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
269 	++stn;
270 
271 	label = manage (new Gtk::Label(_("Hidden Tracks:")));
272 	label->set_alignment(1, .5);
273 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
274 	sttable->attach (hidden_tracks, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
275 	++stn;
276 
277 	label = manage (new Gtk::Label(_("Use Groups:")));
278 	label->set_alignment(1, .5);
279 	sttable->attach (*label, 0, 1, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
280 	sttable->attach (usegroups, 1, 2, stn, stn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
281 	++stn;
282 
283 
284 	sttable->show_all ();
285 	append_page (*sttable, _("Default Strip Types"));
286 
287 
288 	// Feedback Calculate Page
289 	int fn = 0; // table row
290 	Table* fbtable = manage (new Table);
291 	fbtable->set_row_spacings (4);
292 	fbtable->set_col_spacings (6);
293 	fbtable->set_border_width (12);
294 
295 	label = manage (new Gtk::Label(_("Select Desired Types of Feedback")));
296 	fbtable->attach (*label, 0, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
297 	++fn;
298 
299 	label = manage (new Gtk::Label(_("Feedback Value:")));
300 	label->set_alignment(1, .5);
301 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 15);
302 	calculate_feedback ();
303 	current_feedback.set_width_chars(10);
304 	fbtable->attach (current_feedback, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 15);
305 	++fn;
306 
307 	label = manage (new Gtk::Label(_("Strip Buttons:")));
308 	label->set_alignment(1, .5);
309 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
310 	fbtable->attach (strip_buttons_button, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
311 	++fn;
312 
313 	label = manage (new Gtk::Label(_("Strip Controls:")));
314 	label->set_alignment(1, .5);
315 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
316 	fbtable->attach (strip_control_button, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
317 	++fn;
318 
319 	label = manage (new Gtk::Label(_("Use SSID as Path Extension:")));
320 	label->set_alignment(1, .5);
321 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
322 	fbtable->attach (ssid_as_path, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
323 	++fn;
324 
325 	label = manage (new Gtk::Label(_("Use Heart Beat:")));
326 	label->set_alignment(1, .5);
327 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
328 	fbtable->attach (heart_beat, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
329 	++fn;
330 
331 	label = manage (new Gtk::Label(_("Master Section:")));
332 	label->set_alignment(1, .5);
333 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
334 	fbtable->attach (master_fb, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
335 	++fn;
336 
337 	label = manage (new Gtk::Label(_("Play Head Position as Bar and Beat:")));
338 	label->set_alignment(1, .5);
339 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
340 	fbtable->attach (bar_and_beat, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
341 	++fn;
342 
343 	label = manage (new Gtk::Label(_("Play Head Position as SMPTE Time:")));
344 	label->set_alignment(1, .5);
345 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
346 	fbtable->attach (smpte, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
347 	++fn;
348 
349 	label = manage (new Gtk::Label(_("Metering as a Float:")));
350 	label->set_alignment(1, .5);
351 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
352 	fbtable->attach (meter_float, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
353 	++fn;
354 
355 	label = manage (new Gtk::Label(_("Metering as a LED Strip:")));
356 	label->set_alignment(1, .5);
357 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
358 	fbtable->attach (meter_led, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
359 	++fn;
360 
361 	label = manage (new Gtk::Label(_("Signal Present:")));
362 	label->set_alignment(1, .5);
363 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
364 	fbtable->attach (signal_present, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
365 	++fn;
366 
367 	label = manage (new Gtk::Label(_("Play Head Position as Samples:")));
368 	label->set_alignment(1, .5);
369 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
370 	fbtable->attach (hp_samples, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
371 	++fn;
372 
373 	label = manage (new Gtk::Label(_("Playhead Position as Minutes Seconds:")));
374 	label->set_alignment(1, .5);
375 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
376 	fbtable->attach (hp_min_sec, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
377 	++fn;
378 
379 	label = manage (new Gtk::Label(_("Playhead Position as per GUI Clock:")));
380 	label->set_alignment(1, .5);
381 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
382 	fbtable->attach (hp_gui, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
383 	hp_gui.set_sensitive (false); // we don't have this yet (Mixbus wants)
384 	++fn;
385 
386 	label = manage (new Gtk::Label(_("Extra Select Only Feedback:")));
387 	label->set_alignment(1, .5);
388 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
389 	fbtable->attach (select_fb, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
390 	++fn;
391 
392 	label = manage (new Gtk::Label(_("Use /reply instead of #reply:")));
393 	label->set_alignment(1, .5);
394 	fbtable->attach (*label, 0, 1, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0));
395 	fbtable->attach (use_osc10, 1, 2, fn, fn+1, AttachOptions(FILL|EXPAND), AttachOptions(0), 0, 0);
396 	++fn;
397 
398 	fbtable->show_all ();
399 	append_page (*fbtable, _("Default Feedback"));
400 	// set strips and feedback from loaded default values
401 	reshow_values ();
402 	// connect signals
403 	audio_tracks.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
404 	midi_tracks.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
405 	audio_buses.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
406 	foldback_busses.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
407 	midi_buses.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
408 	control_masters.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
409 	master_type.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
410 	monitor_type.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
411 	selected_tracks.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
412 	hidden_tracks.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
413 	usegroups.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
414 	strip_buttons_button.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
415 	strip_control_button.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
416 	ssid_as_path.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
417 	heart_beat.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
418 	master_fb.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
419 	bar_and_beat.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
420 	smpte.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
421 	meter_float.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
422 	meter_led.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
423 	signal_present.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
424 	hp_samples.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
425 	hp_min_sec.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
426 	hp_gui.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
427 	select_fb.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
428 	use_osc10.signal_clicked().connect (sigc::mem_fun (*this, &OSC_GUI::set_bitsets));
429 	preset_busy = false;
430 
431 }
432 
~OSC_GUI()433 OSC_GUI::~OSC_GUI ()
434 {
435 }
436 
437 // static directory and file handling stuff
438 static Searchpath
preset_search_path()439 preset_search_path ()
440 {
441 	bool preset_path_defined = false;
442         std::string spath_env (Glib::getenv (preset_env_variable_name, preset_path_defined));
443 
444 	if (preset_path_defined) {
445 		return spath_env;
446 	}
447 
448 	Searchpath spath (ardour_data_search_path());
449 	spath.add_subdirectory_to_paths(preset_dir_name);
450 
451 	return spath;
452 }
453 
454 static std::string
user_preset_directory()455 user_preset_directory ()
456 {
457 	return Glib::build_filename (user_config_directory(), preset_dir_name);
458 }
459 
460 static bool
preset_filter(const std::string & str,void *)461 preset_filter (const std::string &str, void* /*arg*/)
462 {
463 	return (str.length() > strlen(preset_suffix) &&
464 		str.find (preset_suffix) == (str.length() - strlen (preset_suffix)));
465 }
466 
467 static std::string
legalize_for_path(const std::string & str)468 legalize_for_path (const std::string& str)
469 {
470 	std::string::size_type pos;
471 	std::string illegal_chars = "/\\"; /* DOS, POSIX. Yes, we're going to ignore HFS */
472 	std::string legal;
473 
474 	legal = str;
475 	pos = 0;
476 
477 	while ((pos = legal.find_first_of (illegal_chars, pos)) != std::string::npos) {
478 		legal.replace (pos, 1, "_");
479 		pos += 1;
480 	}
481 
482 	return std::string (legal);
483 }
484 
485 // end of static functions
486 
487 void
debug_changed()488 OSC_GUI::debug_changed ()
489 {
490 	std::string str = debug_combo.get_active_text ();
491 	if (str == _("Off")) {
492 		cp.set_debug_mode (OSC::Off);
493 	}
494 	else if (str == _("Log invalid messages")) {
495 		cp.set_debug_mode (OSC::Unhandled);
496 	}
497 	else if (str == _("Log all messages")) {
498 		cp.set_debug_mode (OSC::All);
499 	}
500 	else if (str == _("Print surface information to Log window")) {
501 		cp.get_surfaces ();
502 		debug_combo.set_active ((int)cp.get_debug_mode());
503 	}
504 	else {
505 		std::cerr << "Invalid OSC Debug Mode\n";
506 		assert (0);
507 	}
508 }
509 
510 void
portmode_changed()511 OSC_GUI::portmode_changed ()
512 {
513 	int pm = portmode_combo.get_active_row_number ();
514 	cp.set_portmode (pm);
515 	if (pm) {
516 		port_entry.set_sensitive (true);
517 	} else {
518 		port_entry.set_sensitive (false);
519 	}
520 	save_user ();
521 }
522 
523 void
port_changed()524 OSC_GUI::port_changed ()
525 {
526 	std::string str = port_entry.get_text ();
527 	int prt = atoi (str.c_str());
528 	if (prt == 3819 || prt < 1024) {
529 		// indicate non-valid text
530 		port_entry.set_progress_fraction (1.0);
531 	} else {
532 		port_entry.set_progress_fraction (0.0);
533 		cp.set_remote_port (string_compose ("%1", prt));
534 		save_user ();
535 	}
536 }
537 
538 bool
port_focus_out(GdkEventFocus *)539 OSC_GUI::port_focus_out (GdkEventFocus* )
540 {
541 	std::string str = port_entry.get_text ();
542 	int prt = atoi (str.c_str());
543 	if (prt == 3819 || prt < 1024) {
544 		port_entry.set_text(cp.get_remote_port().c_str());
545 		port_entry.set_progress_fraction (0.0);
546 	}
547 	return false;
548 }
549 
550 void
bank_changed()551 OSC_GUI::bank_changed ()
552 {
553 	uint32_t bsize = atoi(bank_entry.get_text ());
554 	bank_entry.set_text (string_compose ("%1", bsize));
555 	cp.set_banksize (bsize);
556 	save_user ();
557 
558 }
559 
560 void
send_page_changed()561 OSC_GUI::send_page_changed ()
562 {
563 	uint32_t ssize = atoi (send_page_entry.get_text ());
564 	send_page_entry.set_text (string_compose ("%1", ssize));
565 	cp.set_send_size (ssize);
566 	save_user ();
567 
568 }
569 
570 void
plugin_page_changed()571 OSC_GUI::plugin_page_changed ()
572 {
573 	uint32_t psize = atoi (plugin_page_entry.get_text ());
574 	plugin_page_entry.set_text (string_compose ("%1", psize));
575 	cp.set_plugin_size (psize);
576 	save_user ();
577 
578 }
579 
580 void
gainmode_changed()581 OSC_GUI::gainmode_changed ()
582 {
583 	std::string str = gainmode_combo.get_active_text ();
584 	if (str == _("/strip/gain (dB)")) {
585 		cp.set_gainmode (0);
586 	}
587 	else if (str == _("/strip/fader (Position) and dB in control name")) {
588 		cp.set_gainmode (1);
589 	}
590 	else if (str == _("/strip/fader (Position) and /strip/gain (dB)")) {
591 		cp.set_gainmode (2);
592 	}
593 	else if (str == _("/strip/fader (Position)")) {
594 		cp.set_gainmode (3);
595 	}
596 	else {
597 		std::cerr << "Invalid OSC Gain Mode\n";
598 		assert (0);
599 	}
600 	save_user ();
601 }
602 
603 void
clear_device()604 OSC_GUI::clear_device ()
605 {
606 	cp.clear_devices();
607 }
608 
609 void
preset_changed()610 OSC_GUI::preset_changed ()
611 {
612 	preset_busy = true;
613 	std::string str = preset_combo.get_active_text ();
614 	if (str == "Last Loaded Session") {
615 		restore_sesn_values ();
616 	}
617 	else if (str == "Ardour Factory Setting") {
618 		factory_reset ();
619 	}
620 	else if (str == "User") {
621 		load_preset ("User");
622 	}
623 	else {
624 		load_preset (str);
625 	}
626 	cp.clear_devices ();
627 	preset_busy = false;
628 }
629 
630 void
factory_reset()631 OSC_GUI::factory_reset ()
632 {
633 	cp.set_banksize (0);
634 	bank_entry.set_text ("0");
635 	cp.set_send_size (0);
636 	send_page_entry.set_text ("0");
637 	cp.set_plugin_size (0);
638 	plugin_page_entry.set_text ("0");
639 	cp.set_defaultstrip (31);
640 	cp.set_defaultfeedback (0);
641 	reshow_values ();
642 	cp.set_gainmode (0);
643 	gainmode_combo.set_active (0);
644 	cp.set_portmode (1);
645 	portmode_combo.set_active (1);
646 	cp.set_remote_port ("8000");
647 	port_entry.set_text ("8000");
648 	cp.clear_devices ();
649 	cp.gui_changed ();
650 }
651 
652 void
reshow_values()653 OSC_GUI::reshow_values ()
654 {
655 	def_strip = cp.get_defaultstrip();
656 	audio_tracks.set_active(def_strip & 1);
657 	midi_tracks.set_active(def_strip & 2);
658 	audio_buses.set_active(def_strip & 4);
659 	midi_buses.set_active(def_strip & 8);
660 	control_masters.set_active(def_strip & 16);
661 	master_type.set_active(def_strip & 32);
662 	monitor_type.set_active(def_strip & 64);
663 	foldback_busses.set_active(def_strip & 128);
664 	selected_tracks.set_active(def_strip & 256);
665 	hidden_tracks.set_active(def_strip & 512);
666 	usegroups.set_active(def_strip & 1024);
667 	def_feedback = cp.get_defaultfeedback();
668 	strip_buttons_button.set_active(def_feedback & 1);
669 	strip_control_button.set_active(def_feedback & 2);
670 	ssid_as_path.set_active(def_feedback & 4);
671 	heart_beat.set_active(def_feedback & 8);
672 	master_fb.set_active(def_feedback & 16);
673 	bar_and_beat.set_active(def_feedback & 32);
674 	smpte.set_active(def_feedback & 64);
675 	meter_float.set_active(def_feedback & 128);
676 	meter_led.set_active(def_feedback & 256);
677 	signal_present.set_active(def_feedback & 512);
678 	hp_samples.set_active(def_feedback & 1024);
679 	hp_min_sec.set_active (def_feedback & 2048);
680 	//hp_gui.set_active (false); // we don't have this yet (Mixbus wants)
681 	select_fb.set_active(def_feedback & 8192);
682 	use_osc10.set_active(def_feedback & 16384);
683 
684 	calculate_strip_types ();
685 	calculate_feedback ();
686 }
687 
688 void
calculate_feedback()689 OSC_GUI::calculate_feedback ()
690 {
691 	fbvalue = 0;
692 	if (strip_buttons_button.get_active()) {
693 		fbvalue += 1;
694 	}
695 	if (strip_control_button.get_active()) {
696 		fbvalue += 2;
697 	}
698 	if (ssid_as_path.get_active()) {
699 		fbvalue += 4;
700 	}
701 	if (heart_beat.get_active()) {
702 		fbvalue += 8;
703 	}
704 	if (master_fb.get_active()) {
705 		fbvalue += 16;
706 	}
707 	if (bar_and_beat.get_active()) {
708 		fbvalue += 32;
709 	}
710 	if (smpte.get_active()) {
711 		fbvalue += 64;
712 	}
713 	if (meter_float.get_active()) {
714 		fbvalue += 128;
715 	}
716 	if (meter_led.get_active()) {
717 		fbvalue += 256;
718 	}
719 	if (signal_present.get_active()) {
720 		fbvalue += 512;
721 	}
722 	if (hp_samples.get_active()) {
723 		fbvalue += 1024;
724 	}
725 	if (hp_min_sec.get_active()) {
726 		fbvalue += 2048;
727 	}
728 	if (hp_gui.get_active()) {
729 		fbvalue += 4096;
730 	}
731 	if (select_fb.get_active()) {
732 		fbvalue += 8192;
733 	}
734 	if (use_osc10.get_active()) {
735 		fbvalue += 16384;
736 	}
737 
738 	current_feedback.set_text(string_compose("%1", fbvalue));
739 }
740 
741 void
calculate_strip_types()742 OSC_GUI::calculate_strip_types ()
743 {
744 	stvalue = 0;
745 	if (audio_tracks.get_active()) {
746 		stvalue += 1;
747 	}
748 	if (midi_tracks.get_active()) {
749 		stvalue += 2;
750 	}
751 	if (audio_buses.get_active()) {
752 		stvalue += 4;
753 	}
754 	if (midi_buses.get_active()) {
755 		stvalue += 8;
756 	}
757 	if (control_masters.get_active()) {
758 		stvalue += 16;
759 	}
760 	if (master_type.get_active()) {
761 		stvalue += 32;
762 	}
763 	if (monitor_type.get_active()) {
764 		stvalue += 64;
765 	}
766 	if (foldback_busses.get_active()) {
767 		stvalue += 128;
768 	}
769 	if (selected_tracks.get_active()) {
770 		stvalue += 256;
771 	}
772 	if (hidden_tracks.get_active()) {
773 		stvalue += 512;
774 	}
775 	if (usegroups.get_active()) {
776 		stvalue += 1024;
777 	}
778 
779 	current_strip_types.set_text(string_compose("%1", stvalue));
780 }
781 
782 void
set_bitsets()783 OSC_GUI::set_bitsets ()
784 {
785 	if (preset_busy) {
786 		return;
787 	}
788 	calculate_strip_types ();
789 	calculate_feedback ();
790 	cp.set_defaultstrip (stvalue);
791 	cp.set_defaultfeedback (fbvalue);
792 	save_user ();
793 }
794 
795 void
scan_preset_files()796 OSC_GUI::scan_preset_files ()
797 {
798 	std::vector<std::string> presets;
799 	Searchpath spath (preset_search_path());
800 
801 	find_files_matching_filter (presets, spath, preset_filter, 0, false, true);
802 	//device_profiles.clear ();preset_list.clear // first two entries already there
803 
804 	if (presets.empty()) {
805 		error << "No OSC preset files found using " << spath.to_string() << endmsg;
806 		return;
807 	}
808 
809 	for (std::vector<std::string>::iterator i = presets.begin(); i != presets.end(); ++i) {
810 		std::string fullpath = *i;
811 		//DeviceProfile dp; // has to be initial every loop or info from last added.
812 
813 		XMLTree tree;
814 
815 		if (!tree.read (fullpath.c_str())) {
816 			continue;
817 		}
818 
819 		XMLNode* root = tree.root ();
820 		if (!root) {
821 			continue;
822 		}
823 		const XMLProperty* prop;
824 		const XMLNode* child;
825 
826 		if (root->name() != "OSCPreset") {
827 			continue;
828 		}
829 
830 		if ((child = root->child ("Name")) == 0 || (prop = child->property ("value")) == 0) {
831 			continue;
832 		} else {
833 			if (prop->value() == "User") {
834 				// We already added user but no file name
835 				preset_files[prop->value()] = fullpath;
836 			} else if (preset_files.find(prop->value()) == preset_files.end()) {
837 				preset_options.push_back (prop->value());
838 				preset_files[prop->value()] = fullpath;
839 			}
840 		}
841 
842 	}
843 }
844 
845 void
save_user()846 OSC_GUI::save_user ()
847 {
848 	if (preset_busy) {
849 		return;
850 	}
851 	std::string fullpath = user_preset_directory();
852 
853 	if (g_mkdir_with_parents (fullpath.c_str(), 0755) < 0) {
854 		error << string_compose(_("Session: cannot create user OSC profile folder \"%1\" (%2)"), fullpath, strerror (errno)) << endmsg;
855 		return;
856 	}
857 
858 	fullpath = Glib::build_filename (fullpath, string_compose ("%1%2", legalize_for_path ("user"), preset_suffix));
859 
860 	XMLNode* node = new XMLNode ("OSCPreset");
861 	XMLNode* child = new XMLNode ("Name");
862 
863 	child->set_property ("value", "User");
864 	node->add_child_nocopy (*child);
865 
866 	child = new XMLNode ("PortMode");
867 	child->set_property ("value", cp.get_portmode());
868 	node->add_child_nocopy (*child);
869 
870 	child = new XMLNode ("Remote-Port");
871 	child->set_property ("value", cp.get_remote_port());
872 	node->add_child_nocopy (*child);
873 
874 	child = new XMLNode ("Bank-Size");
875 	child->set_property ("value", cp.get_banksize());
876 	node->add_child_nocopy (*child);
877 
878 	child = new XMLNode ("Send-Size");
879 	child->set_property ("value", cp.get_send_size());
880 	node->add_child_nocopy (*child);
881 
882 	child = new XMLNode ("Plugin-Size");
883 	child->set_property ("value", cp.get_plugin_size());
884 	node->add_child_nocopy (*child);
885 
886 	child = new XMLNode ("Strip-Types");
887 	child->set_property ("value", cp.get_defaultstrip());
888 	node->add_child_nocopy (*child);
889 
890 	child = new XMLNode ("Feedback");
891 	child->set_property ("value", cp.get_defaultfeedback());
892 	node->add_child_nocopy (*child);
893 
894 	child = new XMLNode ("Gain-Mode");
895 	child->set_property ("value", cp.get_gainmode());
896 	node->add_child_nocopy (*child);
897 
898 	XMLTree tree;
899 	tree.set_root (node);
900 
901 	if (!tree.write (fullpath)) {
902 		error << string_compose ("OSC profile not saved to %1", fullpath) << endmsg;
903 	}
904 	preset_combo.set_active (2);
905 	cp.gui_changed();
906 	clear_device ();
907 
908 }
909 
910 void
load_preset(std::string preset)911 OSC_GUI::load_preset (std::string preset)
912 {
913 	if (preset == "User" && preset_files["User"] == "") {
914 		restore_sesn_values ();
915 	} else if (preset_files.find(preset) != preset_files.end()) {
916 		XMLTree tree;
917 
918 		if (!tree.read (preset_files[preset])) {
919 			std::cerr << "preset file not found " << preset_files[preset] << "\n";
920 			return;
921 		}
922 
923 		XMLNode* root = tree.root ();
924 		if (!root) {
925 			std::cerr << "invalid preset file " << preset_files[preset] << "\n";
926 			return;
927 		}
928 		const XMLProperty* prop;
929 		const XMLNode* child;
930 
931 		if (root->name() != "OSCPreset") {
932 			std::cerr << "invalid preset file " << preset_files[preset] << "\n";
933 			return;
934 		}
935 
936 		if ((child = root->child ("Name")) == 0 || (prop = child->property ("value")) == 0) {
937 			std::cerr << "preset file missing Name " << preset_files[preset] << "\n";
938 			return;
939 		}
940 		if ((child = root->child ("PortMode")) == 0 || (prop = child->property ("value")) == 0) {
941 			cp.set_portmode (sesn_portmode);
942 			portmode_combo.set_active (sesn_portmode);
943 		} else {
944 			cp.set_portmode (atoi (prop->value().c_str()));
945 			portmode_combo.set_active (atoi (prop->value().c_str()));
946 		}
947 		if ((child = root->child ("Remote-Port")) == 0 || (prop = child->property ("value")) == 0) {
948 			cp.set_remote_port (sesn_port);
949 			port_entry.set_text (sesn_port);
950 		} else {
951 			cp.set_remote_port (prop->value());
952 			port_entry.set_text (prop->value());
953 		}
954 		if ((child = root->child ("Bank-Size")) == 0 || (prop = child->property ("value")) == 0) {
955 			cp.set_banksize (sesn_bank);
956 			bank_entry.set_text (string_compose("%1", sesn_bank));
957 		} else {
958 			cp.set_banksize (atoi (prop->value().c_str()));
959 			bank_entry.set_text (prop->value().c_str());
960 		}
961 		if ((child = root->child ("Send-Size")) == 0 || (prop = child->property ("value")) == 0) {
962 			cp.set_send_size (sesn_send);
963 			send_page_entry.set_text (string_compose("%1", sesn_send));
964 		} else {
965 			cp.set_send_size (atoi (prop->value().c_str()));
966 			send_page_entry.set_text (prop->value().c_str());
967 		}
968 		if ((child = root->child ("Plugin-Size")) == 0 || (prop = child->property ("value")) == 0) {
969 			cp.set_plugin_size (sesn_plugin);
970 			plugin_page_entry.set_text (string_compose("%1", sesn_plugin));
971 		} else {
972 			cp.set_plugin_size (atoi (prop->value().c_str()));
973 			plugin_page_entry.set_text (prop->value().c_str());
974 		}
975 		if ((child = root->child ("Strip-Types")) == 0 || (prop = child->property ("value")) == 0) {
976 			cp.set_defaultstrip (sesn_strips);
977 		} else {
978 			cp.set_defaultstrip (atoi (prop->value().c_str()));
979 		}
980 		if ((child = root->child ("Feedback")) == 0 || (prop = child->property ("value")) == 0) {
981 			cp.set_defaultfeedback (sesn_feedback);
982 		} else {
983 			cp.set_defaultfeedback (atoi (prop->value().c_str()));
984 		}
985 		reshow_values (); // show strip types and feed back in GUI
986 
987 		if ((child = root->child ("Gain-Mode")) == 0 || (prop = child->property ("value")) == 0) {
988 			cp.set_gainmode (sesn_gainmode);
989 			gainmode_combo.set_active (sesn_gainmode);
990 		} else {
991 			cp.set_gainmode (atoi (prop->value().c_str()));
992 			gainmode_combo.set_active (atoi (prop->value().c_str()));
993 		}
994 		cp.gui_changed();
995 		clear_device ();
996 
997 	}
998 }
999 
1000 void
get_session()1001 OSC_GUI::get_session ()
1002 {
1003 	sesn_portmode = cp.get_portmode ();
1004 	sesn_port = cp.get_remote_port ();
1005 	sesn_bank = cp.get_banksize ();
1006 	sesn_send = cp.get_send_size ();
1007 	sesn_plugin = cp.get_plugin_size ();
1008 	sesn_strips = cp.get_defaultstrip ();
1009 	sesn_feedback = cp.get_defaultfeedback ();
1010 	sesn_gainmode = cp.get_gainmode ();
1011 }
1012 
1013 void
restore_sesn_values()1014 OSC_GUI::restore_sesn_values ()
1015 {
1016 	cp.set_portmode (sesn_portmode);
1017 	portmode_combo.set_active (sesn_portmode);
1018 	cp.set_remote_port (sesn_port);
1019 	port_entry.set_text (sesn_port);
1020 	cp.set_banksize (sesn_bank);
1021 	bank_entry.set_text (string_compose ("%1", sesn_bank));
1022 	cp.set_send_size (sesn_send);
1023 	send_page_entry.set_text (string_compose ("%1", sesn_send));
1024 	cp.set_plugin_size (sesn_plugin);
1025 	plugin_page_entry.set_text (string_compose ("%1", sesn_plugin));
1026 	cp.set_defaultstrip (sesn_strips);
1027 	cp.set_defaultfeedback (sesn_feedback);
1028 	reshow_values ();
1029 	cp.set_gainmode (sesn_gainmode);
1030 	gainmode_combo.set_active (sesn_gainmode);
1031 }
1032