1 /*
2  * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
3  * Copyright (C) 2005-2015 Tim Mayberry <mojofunk@gmail.com>
4  * Copyright (C) 2005-2018 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2006-2007 Doug McLain <doug@nostar.net>
6  * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
7  * Copyright (C) 2006 Karsten Wiese <fzuuzf@googlemail.com>
8  * Copyright (C) 2006 Nick Mainsbridge <mainsbridge@gmail.com>
9  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
10  * Copyright (C) 2008 Hans Baier <hansfbaier@googlemail.com>
11  * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
12  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License along
25  * with this program; if not, write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27  */
28 
29 #ifdef WAF_BUILD
30 #include "gtk2ardour-config.h"
31 #endif
32 
33 #include <fcntl.h>
34 #include <signal.h>
35 #include <unistd.h>
36 #include <cerrno>
37 #include <iostream>
38 #include <cmath>
39 
40 #include <sigc++/bind.h>
41 #include <gtkmm/settings.h>
42 
43 #include "canvas/canvas.h"
44 
45 #include "pbd/error.h"
46 #include "pbd/basename.h"
47 #include "pbd/fastlog.h"
48 
49 #include "gtkmm2ext/utils.h"
50 #include "gtkmm2ext/window_title.h"
51 
52 #include "ardour/audioengine.h"
53 #include "ardour/profile.h"
54 #include "ardour/session.h"
55 #include "ardour/types.h"
56 
57 #include "ardour_ui.h"
58 #include "keyboard.h"
59 #include "public_editor.h"
60 #include "audio_clock.h"
61 #include "actions.h"
62 #include "main_clock.h"
63 #include "mixer_ui.h"
64 #include "recorder_ui.h"
65 #include "utils.h"
66 #include "time_info_box.h"
67 #include "midi_tracer.h"
68 #include "global_port_matrix.h"
69 #include "location_ui.h"
70 #include "rc_option_editor.h"
71 
72 #include "pbd/i18n.h"
73 
74 using namespace std;
75 using namespace ARDOUR;
76 using namespace PBD;
77 using namespace Gtkmm2ext;
78 using namespace ArdourWidgets;
79 using namespace Gtk;
80 using namespace Glib;
81 using namespace ARDOUR_UI_UTILS;
82 
83 void
setup_tooltips()84 ARDOUR_UI::setup_tooltips ()
85 {
86 	ArdourCanvas::Canvas::set_tooltip_timeout (Gtk::Settings::get_default()->property_gtk_tooltip_timeout ());
87 
88 	set_tip (auto_return_button, _("Return to last playback start when stopped"));
89 	set_tip (follow_edits_button, _("Playhead follows Range tool clicks, and Range selections"));
90 	parameter_changed("click-gain");
91 	set_tip (solo_alert_button, _("When active, something is soloed.\nClick to de-solo everything"));
92 	set_tip (auditioning_alert_button, _("When active, auditioning is taking place.\nClick to stop the audition"));
93 	set_tip (feedback_alert_button, _("When lit, there is a ports connection issue, leading to feedback loop or ambiguous alignment.\nThis is caused by connecting an output back to some input (feedback), or by multiple connections from a source to the same output via different paths (ambiguous latency, record alignment)."));
94 	set_tip (primary_clock, _("<b>Primary Clock</b> right-click to set display mode. Click to edit, click+drag a digit or mouse-over+scroll wheel to modify.\nText edits: right-to-left overwrite <tt>Esc</tt>: cancel; <tt>Enter</tt>: confirm; postfix the edit with '+' or '-' to enter delta times.\n"));
95 	set_tip (secondary_clock, _("<b>Secondary Clock</b> right-click to set display mode. Click to edit, click+drag a digit or mouse-over+scroll wheel to modify.\nText edits: right-to-left overwrite <tt>Esc</tt>: cancel; <tt>Enter</tt>: confirm; postfix the edit with '+' or '-' to enter delta times.\n"));
96 	set_tip (editor_meter_peak_display, _("Reset All Peak Meters"));
97 	set_tip (error_alert_button, _("Show Error Log and acknowledge warnings"));
98 
99 	set_tip (latency_disable_button, _("Disable all Plugin Delay Compensation. This results in the shortest delay from live input to output, but any paths with delay-causing plugins will sound later than those without."));
100 
101 	synchronize_sync_source_and_video_pullup ();
102 
103 	editor->setup_tooltips ();
104 }
105 
106 bool
status_bar_button_press(GdkEventButton * ev)107 ARDOUR_UI::status_bar_button_press (GdkEventButton* ev)
108 {
109 	bool handled = false;
110 
111 	switch (ev->button) {
112 	case 1:
113 		status_bar_label.set_text ("");
114 		handled = true;
115 		break;
116 	default:
117 		break;
118 	}
119 
120 	return handled;
121 }
122 
123 void
display_message(const char * prefix,gint prefix_len,RefPtr<TextBuffer::Tag> ptag,RefPtr<TextBuffer::Tag> mtag,const char * msg)124 ARDOUR_UI::display_message (const char* prefix, gint prefix_len, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char* msg)
125 {
126 	UI::display_message (prefix, prefix_len, ptag, mtag, msg);
127 
128 	ArdourLogLevel ll = LogLevelNone;
129 
130 	if (strcmp (prefix, _("[ERROR]: ")) == 0) {
131 		ll = LogLevelError;
132 	} else if (strcmp (prefix, _("[WARNING]: ")) == 0) {
133 		ll = LogLevelWarning;
134 	} else if (strcmp (prefix, _("[INFO]: ")) == 0) {
135 		ll = LogLevelInfo;
136 	}
137 
138 	_log_not_acknowledged = std::max(_log_not_acknowledged, ll);
139 }
140 
141 XMLNode*
tearoff_settings(const char * name) const142 ARDOUR_UI::tearoff_settings (const char* name) const
143 {
144 	XMLNode* ui_node = Config->extra_xml(X_("UI"));
145 
146 	if (ui_node) {
147 		XMLNode* tearoff_node = ui_node->child (X_("Tearoffs"));
148 		if (tearoff_node) {
149 			XMLNode* mnode = tearoff_node->child (name);
150 			return mnode;
151 		}
152 	}
153 
154 	return 0;
155 }
156 
157 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
158 
159 static
drag_failed(const Glib::RefPtr<Gdk::DragContext> & context,DragResult result,Tabbable * tab)160 bool drag_failed (const Glib::RefPtr<Gdk::DragContext>& context, DragResult result, Tabbable* tab)
161 {
162 	if (result == Gtk::DRAG_RESULT_NO_TARGET) {
163 		tab->detach ();
164 		return true;
165 	}
166 	return false;
167 }
168 
169 void
repack_transport_hbox()170 ARDOUR_UI::repack_transport_hbox ()
171 {
172 	if (time_info_box) {
173 		if (time_info_box->get_parent()) {
174 			transport_hbox.remove (*time_info_box);
175 		}
176 		if (UIConfiguration::instance().get_show_toolbar_selclock ()) {
177 			transport_hbox.pack_start (*time_info_box, false, false);
178 			time_info_box->show();
179 		}
180 	}
181 
182 	if (mini_timeline.get_parent()) {
183 		transport_hbox.remove (mini_timeline);
184 	}
185 	if (UIConfiguration::instance().get_show_mini_timeline ()) {
186 		transport_hbox.pack_start (mini_timeline, true, true);
187 		mini_timeline.show();
188 	}
189 
190 	if (editor_meter) {
191 		if (editor_meter_table.get_parent()) {
192 			transport_hbox.remove (editor_meter_table);
193 		}
194 		if (meterbox_spacer.get_parent()) {
195 			transport_hbox.remove (meterbox_spacer);
196 			transport_hbox.remove (meterbox_spacer2);
197 		}
198 
199 		if (UIConfiguration::instance().get_show_editor_meter()) {
200 			transport_hbox.pack_end (meterbox_spacer, false, false, 3);
201 			transport_hbox.pack_end (editor_meter_table, false, false);
202 			transport_hbox.pack_end (meterbox_spacer2, false, false, 3);
203 			editor_meter_table.show();
204 			meterbox_spacer.show();
205 			meterbox_spacer2.show();
206 		}
207 	}
208 
209 	bool show_rec = UIConfiguration::instance().get_show_toolbar_recpunch ();
210 	if (show_rec) {
211 		punch_label.show ();
212 		layered_label.show ();
213 		punch_in_button.show ();
214 		punch_out_button.show ();
215 		layered_button.show ();
216 		recpunch_spacer.show ();
217 	} else {
218 		punch_label.hide ();
219 		layered_label.hide ();
220 		punch_in_button.hide ();
221 		punch_out_button.hide ();
222 		layered_button.hide ();
223 		recpunch_spacer.hide ();
224 	}
225 
226 	bool show_pdc = UIConfiguration::instance().get_show_toolbar_latency ();
227 	if (show_pdc) {
228 		latency_disable_button.show ();
229 		route_latency_value.show ();
230 		io_latency_label.show ();
231 		io_latency_value.show ();
232 		latency_spacer.show ();
233 	} else {
234 		latency_disable_button.hide ();
235 		route_latency_value.hide ();
236 		io_latency_label.hide ();
237 		io_latency_value.hide ();
238 		latency_spacer.hide ();
239 	}
240 
241 	bool show_mnfo = UIConfiguration::instance().get_show_toolbar_monitor_info ();
242 	if (show_mnfo) {
243 		monitor_dim_button.show ();
244 		monitor_mono_button.show ();
245 		monitor_mute_button.show ();
246 		monitor_spacer.show ();
247 	} else {
248 		monitor_dim_button.hide ();
249 		monitor_mono_button.hide ();
250 		monitor_mute_button.hide ();
251 		monitor_spacer.hide ();
252 	}
253 }
254 
255 void
update_clock_visibility()256 ARDOUR_UI::update_clock_visibility ()
257 {
258 	if (ARDOUR::Profile->get_small_screen()) {
259 		return;
260 	}
261 	if (UIConfiguration::instance().get_show_secondary_clock ()) {
262 		secondary_clock->show();
263 		secondary_clock->left_btn()->show();
264 		secondary_clock->right_btn()->show();
265 		if (secondary_clock_spacer) {
266 			secondary_clock_spacer->show();
267 		}
268 	} else {
269 		secondary_clock->hide();
270 		secondary_clock->left_btn()->hide();
271 		secondary_clock->right_btn()->hide();
272 		if (secondary_clock_spacer) {
273 			secondary_clock_spacer->hide();
274 		}
275 	}
276 }
277 
278 void
setup_transport()279 ARDOUR_UI::setup_transport ()
280 {
281 	RefPtr<Action> act;
282 	/* setup actions */
283 
284 	act = ActionManager::get_action (X_("Transport"), X_("ToggleExternalSync"));
285 	sync_button.set_related_action (act);
286 	sync_button.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::sync_button_clicked), false);
287 
288 	sync_button.set_sizing_text (S_("LogestSync|M-Clk"));
289 
290 	/* CANNOT sigc::bind these to clicked or toggled, must use pressed or released */
291 	act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
292 	solo_alert_button.set_related_action (act);
293 	auditioning_alert_button.signal_clicked.connect (sigc::mem_fun(*this,&ARDOUR_UI::audition_alert_clicked));
294 	error_alert_button.signal_button_release_event().connect (sigc::mem_fun(*this,&ARDOUR_UI::error_alert_press), false);
295 	act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
296 	error_alert_button.set_related_action(act);
297 	error_alert_button.set_fallthrough_to_parent(true);
298 
299 	layered_button.signal_clicked.connect (sigc::mem_fun(*this,&ARDOUR_UI::layered_button_clicked));
300 
301 	editor_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-editor-visibility")));
302 	mixer_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-mixer-visibility")));
303 	prefs_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-preferences-visibility")));
304 	recorder_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-recorder-visibility")));
305 
306 	act = ActionManager::get_action ("Transport", "ToggleAutoReturn");
307 	auto_return_button.set_related_action (act);
308 	act = ActionManager::get_action (X_("Transport"), X_("ToggleFollowEdits"));
309 	follow_edits_button.set_related_action (act);
310 
311 	act = ActionManager::get_action ("Transport", "TogglePunchIn");
312 	punch_in_button.set_related_action (act);
313 	act = ActionManager::get_action ("Transport", "TogglePunchOut");
314 	punch_out_button.set_related_action (act);
315 
316 	act = ActionManager::get_action (X_("Monitor Section"), X_("monitor-dim-all"));
317 	monitor_dim_button.set_related_action (act);
318 	act = ActionManager::get_action (X_("Monitor Section"), X_("monitor-mono"));
319 	monitor_mono_button.set_related_action (act);
320 	act = ActionManager::get_action (X_("Monitor Section"), X_("monitor-cut-all"));
321 	monitor_mute_button.set_related_action (act);
322 
323 	act = ActionManager::get_action ("Main", "ToggleLatencyCompensation");
324 	latency_disable_button.set_related_action (act);
325 
326 	set_size_request_to_display_given_text (route_latency_value, "1000 spl", 0, 0);
327 	set_size_request_to_display_given_text (io_latency_value, "1000 spl", 0, 0);
328 
329 	/* connect signals */
330 	ARDOUR_UI::Clock.connect (sigc::bind (sigc::mem_fun (primary_clock, &MainClock::set), false, 0));
331 	ARDOUR_UI::Clock.connect (sigc::bind (sigc::mem_fun (secondary_clock, &MainClock::set), false, 0));
332 
333 	primary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::primary_clock_value_changed));
334 	secondary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::secondary_clock_value_changed));
335 	big_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::big_clock_value_changed));
336 
337 	editor_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), editor));
338 	mixer_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), mixer));
339 	prefs_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), rc_option_editor));
340 	recorder_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), recorder));
341 
342 	/* catch context clicks so that we can show a menu on these buttons */
343 
344 	editor_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("editor")), false);
345 	mixer_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("mixer")), false);
346 	prefs_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("preferences")), false);
347 	recorder_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("recorder")), false);
348 
349 	/* setup widget style/name */
350 
351 	auto_return_button.set_name ("transport option button");
352 	follow_edits_button.set_name ("transport option button");
353 
354 	solo_alert_button.set_name ("rude solo");
355 	auditioning_alert_button.set_name ("rude audition");
356 	feedback_alert_button.set_name ("feedback alert");
357 	error_alert_button.set_name ("error alert");
358 
359 	solo_alert_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
360 	auditioning_alert_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
361 	feedback_alert_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
362 
363 	solo_alert_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
364 	auditioning_alert_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
365 	feedback_alert_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
366 
367 	feedback_alert_button.set_sizing_text (_("Facdbeek")); //< longest of "Feedback" and "No Align"
368 
369 	editor_visibility_button.set_name (X_("page switch button"));
370 	mixer_visibility_button.set_name (X_("page switch button"));
371 	prefs_visibility_button.set_name (X_("page switch button"));
372 	recorder_visibility_button.set_name (X_("page switch button"));
373 
374 	punch_in_button.set_name ("punch button");
375 	punch_out_button.set_name ("punch button");
376 	layered_button.set_name (("layered button"));
377 
378 	latency_disable_button.set_name ("latency button");
379 
380 	monitor_dim_button.set_name ("monitor section dim");
381 	monitor_mono_button.set_name ("monitor section mono");
382 	monitor_mute_button.set_name ("mute button");
383 
384 	monitor_dim_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
385 	monitor_mono_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
386 	monitor_mute_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
387 
388 	monitor_dim_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
389 	monitor_mono_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
390 	monitor_mute_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
391 
392 	sync_button.set_name ("transport active option button");
393 
394 	/* and widget text */
395 	auto_return_button.set_text(_("Auto Return"));
396 	follow_edits_button.set_text(_("Follow Range"));
397 	punch_in_button.set_text (_("In"));
398 	punch_out_button.set_text (_("Out"));
399 	layered_button.set_text (_("Non-Layered"));
400 
401 	latency_disable_button.set_text (_("Disable PDC"));
402 	io_latency_label.set_text (_("I/O Latency:"));
403 
404 	monitor_dim_button.set_text (_("Dim All"));
405 	monitor_mono_button.set_text (_("Mono"));
406 	monitor_mute_button.set_text (_("Mute All"));
407 
408 	punch_label.set_text (_("Punch:"));
409 	layered_label.set_text (_("Rec:"));
410 
411 	/* and tooltips */
412 
413 	Gtkmm2ext::UI::instance()->set_tip (editor_visibility_button,
414 	                                    string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
415 	                                                      "To re-attach the window, use the Window > %1 > Attach menu action"), editor->name()));
416 
417 	Gtkmm2ext::UI::instance()->set_tip (mixer_visibility_button,
418 	                                    string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
419 	                                                      "To re-attach the window, use the Window > %1 > Attach menu action"), mixer->name()));
420 
421 	Gtkmm2ext::UI::instance()->set_tip (prefs_visibility_button,
422 	                                    string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
423 	                                                      "To re-attach the window, use the Window > %1 > Attach menu action"), rc_option_editor->name()));
424 
425 	Gtkmm2ext::UI::instance()->set_tip (recorder_visibility_button,
426 	                                    string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
427 	                                                      "To re-attach the window, use the Window > %1 > Attach menu action"), recorder->name()));
428 
429 	Gtkmm2ext::UI::instance()->set_tip (punch_in_button, _("Start recording at auto-punch start"));
430 	Gtkmm2ext::UI::instance()->set_tip (punch_out_button, _("Stop recording at auto-punch end"));
431 
432 	/* monitor section */
433 	Gtkmm2ext::UI::instance()->set_tip (monitor_dim_button, _("Monitor section dim output"));
434 	Gtkmm2ext::UI::instance()->set_tip (monitor_mono_button, _("Monitor section mono output"));
435 	Gtkmm2ext::UI::instance()->set_tip (monitor_mute_button, _("Monitor section mute output"));
436 
437 	/* transport control size-group */
438 
439 	Glib::RefPtr<SizeGroup> punch_button_size_group = SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL);
440 	punch_button_size_group->add_widget (punch_in_button);
441 	punch_button_size_group->add_widget (punch_out_button);
442 
443 	Glib::RefPtr<SizeGroup> monitor_button_size_group = SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL);
444 	monitor_button_size_group->add_widget (monitor_dim_button);
445 	monitor_button_size_group->add_widget (monitor_mono_button);
446 	monitor_button_size_group->add_widget (monitor_mute_button);
447 
448 	/* and now the layout... */
449 
450 	/* top level packing */
451 	transport_table.set_spacings (0);
452 	transport_table.set_row_spacings (4);
453 	transport_table.set_border_width (0);
454 
455 	transport_frame.set_name ("TransportFrame");
456 	transport_frame.set_shadow_type (Gtk::SHADOW_NONE);
457 
458 	/* An event box to hold the table. We use this because we want specific
459 	   control over the background color, and without this event box,
460 	   nothing inside the transport_frame actually draws a background. We
461 	   would therefore end up seeing the background of the parent widget,
462 	   which is probably some default color. Adding the EventBox adds a
463 	   widget that will draw the background, using a style based on
464 	   the parent, "TransportFrame".
465 	*/
466 	Gtk::EventBox* ebox = manage (new Gtk::EventBox);
467 	transport_frame.add (*ebox);
468 	ebox->add (transport_table);
469 
470 	/* alert box sub-group */
471 	VBox* alert_box = manage (new VBox);
472 	alert_box->set_homogeneous (true);
473 	alert_box->set_spacing (1);
474 	alert_box->set_border_width (0);
475 	alert_box->pack_start (solo_alert_button, true, true);
476 	alert_box->pack_start (auditioning_alert_button, true, true);
477 	alert_box->pack_start (feedback_alert_button, true, true);
478 
479 	/* monitor section sub-group */
480 	VBox* monitor_box = manage (new VBox);
481 	monitor_box->set_homogeneous (true);
482 	monitor_box->set_spacing (1);
483 	monitor_box->set_border_width (0);
484 	monitor_box->pack_start (monitor_mono_button, true, true);
485 	monitor_box->pack_start (monitor_dim_button, true, true);
486 	monitor_box->pack_start (monitor_mute_button, true, true);
487 
488 	/* clock button size groups */
489 	Glib::RefPtr<SizeGroup> button_height_size_group = SizeGroup::create (Gtk::SIZE_GROUP_VERTICAL);
490 	button_height_size_group->add_widget (follow_edits_button);
491 	button_height_size_group->add_widget (*primary_clock->left_btn());
492 	button_height_size_group->add_widget (*primary_clock->right_btn());
493 	button_height_size_group->add_widget (*secondary_clock->left_btn());
494 	button_height_size_group->add_widget (*secondary_clock->right_btn());
495 
496 	button_height_size_group->add_widget (transport_ctrl.size_button ());
497 	button_height_size_group->add_widget (sync_button);
498 	button_height_size_group->add_widget (auto_return_button);
499 
500 	//tab selections
501 	button_height_size_group->add_widget (recorder_visibility_button);
502 	button_height_size_group->add_widget (editor_visibility_button);
503 	button_height_size_group->add_widget (mixer_visibility_button);
504 	button_height_size_group->add_widget (prefs_visibility_button);
505 
506 	//punch section
507 	button_height_size_group->add_widget (punch_in_button);
508 	button_height_size_group->add_widget (punch_out_button);
509 	button_height_size_group->add_widget (layered_button);
510 
511 	// PDC
512 	button_height_size_group->add_widget (latency_disable_button);
513 
514 	for (int i = 0; i < MAX_LUA_ACTION_BUTTONS; ++i) {
515 		button_height_size_group->add_widget (action_script_call_btn[i]);
516 	}
517 
518 	Glib::RefPtr<SizeGroup> clock1_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
519 	clock1_size_group->add_widget (*primary_clock->left_btn());
520 	clock1_size_group->add_widget (*primary_clock->right_btn());
521 
522 	Glib::RefPtr<SizeGroup> clock2_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
523 	clock2_size_group->add_widget (*secondary_clock->left_btn());
524 	clock2_size_group->add_widget (*secondary_clock->right_btn());
525 
526 	/* sub-layout for Sync | Shuttle (grow) */
527 	HBox* ssbox = manage (new HBox);
528 	ssbox->set_spacing (PX_SCALE(2));
529 	ssbox->pack_start (sync_button, false, false, 0);
530 	ssbox->pack_start (shuttle_box, true, true, 0);
531 	ssbox->pack_start (*shuttle_box.info_button(), false, false, 0);
532 
533 
534 	/* and the main table layout */
535 	int vpadding = 1;
536 	int hpadding = 2;
537 	int col = 0;
538 #define TCOL col, col + 1
539 
540 	transport_table.attach (transport_ctrl, TCOL, 0, 1 , SHRINK, SHRINK, 0, 0);
541 	transport_table.attach (*ssbox, TCOL, 1, 2 , FILL, SHRINK, 0, 0);
542 	++col;
543 
544 	transport_table.attach (*(manage (new ArdourVSpacer ())), TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
545 	++col;
546 
547 	transport_table.attach (punch_label, TCOL, 0, 1 , FILL, SHRINK, 3, 0);
548 	transport_table.attach (layered_label, TCOL, 1, 2 , FILL, SHRINK, 3, 0);
549 	++col;
550 
551 	transport_table.attach (punch_in_button,  col,      col + 1, 0, 1 , FILL, SHRINK, hpadding, vpadding);
552 	transport_table.attach (punch_space,      col + 1,  col + 2, 0, 1 , FILL, SHRINK, 0, vpadding);
553 	transport_table.attach (punch_out_button, col + 2,  col + 3, 0, 1 , FILL, SHRINK, hpadding, vpadding);
554 	transport_table.attach (layered_button,   col,      col + 3, 1, 2 , FILL, SHRINK, hpadding, vpadding);
555 	col += 3;
556 
557 	transport_table.attach (recpunch_spacer, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
558 	++col;
559 
560 	transport_table.attach (latency_disable_button, TCOL, 0, 1 , FILL, SHRINK, hpadding, vpadding);
561 	transport_table.attach (io_latency_label, TCOL, 1, 2 , SHRINK, EXPAND|FILL, hpadding, 0);
562 	++col;
563 	transport_table.attach (route_latency_value, TCOL, 0, 1 , SHRINK, EXPAND|FILL, hpadding, 0);
564 	transport_table.attach (io_latency_value, TCOL, 1, 2 , SHRINK, EXPAND|FILL, hpadding, 0);
565 	++col;
566 
567 	route_latency_value.set_alignment (Gtk::ALIGN_RIGHT, Gtk::ALIGN_CENTER);
568 	io_latency_value.set_alignment (Gtk::ALIGN_RIGHT, Gtk::ALIGN_CENTER);
569 
570 	transport_table.attach (latency_spacer, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
571 	++col;
572 
573 	transport_table.attach (follow_edits_button, TCOL, 0, 1 , FILL, SHRINK, hpadding, vpadding);
574 	transport_table.attach (auto_return_button,  TCOL, 1, 2 , FILL, SHRINK, hpadding, vpadding);
575 	++col;
576 
577 	transport_table.attach (*(manage (new ArdourVSpacer ())), TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
578 	++col;
579 
580 	transport_table.attach (*primary_clock,              col,     col + 2, 0, 1 , FILL, SHRINK, hpadding, 0);
581 	transport_table.attach (*primary_clock->left_btn(),  col,     col + 1, 1, 2 , FILL, SHRINK, hpadding, 0);
582 	transport_table.attach (*primary_clock->right_btn(), col + 1, col + 2, 1, 2 , FILL, SHRINK, hpadding, 0);
583 	col += 2;
584 
585 	transport_table.attach (*(manage (new ArdourVSpacer ())), TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
586 	++col;
587 
588 	if (!ARDOUR::Profile->get_small_screen()) {
589 		transport_table.attach (*secondary_clock,              col,     col + 2, 0, 1 , FILL, SHRINK, hpadding, 0);
590 		transport_table.attach (*secondary_clock->left_btn(),  col,     col + 1, 1, 2 , FILL, SHRINK, hpadding, 0);
591 		transport_table.attach (*secondary_clock->right_btn(), col + 1, col + 2, 1, 2 , FILL, SHRINK, hpadding, 0);
592 		secondary_clock->set_no_show_all (true);
593 		secondary_clock->left_btn()->set_no_show_all (true);
594 		secondary_clock->right_btn()->set_no_show_all (true);
595 		col += 2;
596 
597 		secondary_clock_spacer = manage (new ArdourVSpacer ());
598 		transport_table.attach (*secondary_clock_spacer, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
599 		++col;
600 	}
601 
602 	transport_table.attach (*alert_box, TCOL, 0, 2, SHRINK, EXPAND|FILL, hpadding, 0);
603 	++col;
604 
605 	transport_table.attach (monitor_spacer, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
606 	++col;
607 
608 	transport_table.attach (*monitor_box, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
609 	++col;
610 
611 	/* editor-meter, mini-timeline and selection clock are options in the transport_hbox */
612 	transport_hbox.set_spacing (3);
613 	transport_table.attach (transport_hbox, TCOL, 0, 2, EXPAND|FILL, EXPAND|FILL, hpadding, 0);
614 	++col;
615 
616 	/* lua script action buttons */
617 	for (int i = 0; i < MAX_LUA_ACTION_BUTTONS; ++i) {
618 		const int r = i % 2;
619 		const int c = col + i / 2;
620 		transport_table.attach (action_script_call_btn[i], c, c + 1, r, r + 1, FILL, SHRINK, 1, vpadding);
621 	}
622 	col += MAX_LUA_ACTION_BUTTONS / 2;
623 
624 	transport_table.attach (scripts_spacer, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
625 	++col;
626 
627 	transport_table.attach (recorder_visibility_button, TCOL,         0, 1 , FILL, SHRINK, hpadding, vpadding);
628 	transport_table.attach (mixer_visibility_button,    col, col + 2, 1, 2 , FILL, SHRINK, hpadding, vpadding);
629 	++col;
630 	transport_table.attach (editor_visibility_button,   TCOL,         0, 1 , FILL, SHRINK, hpadding, vpadding);
631 	++col;
632 
633 	/* initialize */
634 	latency_switch_changed ();
635 	session_latency_updated (true);
636 
637 	repack_transport_hbox ();
638 	update_clock_visibility ();
639 	/* desensitize */
640 
641 	feedback_alert_button.set_sensitive (false);
642 	feedback_alert_button.set_visual_state (Gtkmm2ext::NoVisualState);
643 	auditioning_alert_button.set_sensitive (false);
644 	auditioning_alert_button.set_visual_state (Gtkmm2ext::NoVisualState);
645 
646 	set_transport_sensitivity (false);
647 }
648 #undef PX_SCALE
649 #undef TCOL
650 
651 
652 void
latency_switch_changed()653 ARDOUR_UI::latency_switch_changed ()
654 {
655 	bool pdc_off = ARDOUR::Latent::zero_latency ();
656 	if (latency_disable_button.get_active() != pdc_off) {
657 		latency_disable_button.set_active (pdc_off);
658 	}
659 }
660 
661 void
session_latency_updated(bool for_playback)662 ARDOUR_UI::session_latency_updated (bool for_playback)
663 {
664 	if (!for_playback) {
665 		/* latency updates happen in pairs, in the following order:
666 		 *  - for capture
667 		 *  - for playback
668 		 */
669 		return;
670 	}
671 
672 	if (!_session) {
673 		route_latency_value.set_text ("--");
674 		io_latency_value.set_text ("--");
675 	} else {
676 		samplecnt_t wrl = _session->worst_route_latency ();
677 		samplecnt_t wpl = _session->worst_latency_preroll ();
678 		float rate      = _session->nominal_sample_rate ();
679 
680 		route_latency_value.set_text (samples_as_time_string (wrl, rate));
681 
682 		if (_session->engine().check_for_ambiguous_latency (true)) {
683 			_ambiguous_latency = true;
684 			io_latency_value.set_markup ("<span background=\"red\" foreground=\"white\">ambiguous</span>");
685 		} else {
686 			_ambiguous_latency = false;
687 			io_latency_value.set_text (samples_as_time_string (wpl, rate));
688 		}
689 	}
690 }
691 
692 void
soloing_changed(bool onoff)693 ARDOUR_UI::soloing_changed (bool onoff)
694 {
695 	if (solo_alert_button.get_active() != onoff) {
696 		solo_alert_button.set_active (onoff);
697 	}
698 }
699 
700 void
_auditioning_changed(bool onoff)701 ARDOUR_UI::_auditioning_changed (bool onoff)
702 {
703 	auditioning_alert_button.set_active (onoff);
704 	auditioning_alert_button.set_sensitive (onoff);
705 	if (!onoff) {
706 		auditioning_alert_button.set_visual_state (Gtkmm2ext::NoVisualState);
707 	}
708 	set_transport_sensitivity (!onoff);
709 }
710 
711 void
auditioning_changed(bool onoff)712 ARDOUR_UI::auditioning_changed (bool onoff)
713 {
714 	UI::instance()->call_slot (MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::_auditioning_changed, this, onoff));
715 }
716 
717 void
audition_alert_clicked()718 ARDOUR_UI::audition_alert_clicked ()
719 {
720 	if (_session) {
721 		_session->cancel_audition();
722 	}
723 }
724 
725 bool
error_alert_press(GdkEventButton * ev)726 ARDOUR_UI::error_alert_press (GdkEventButton* ev)
727 {
728 	bool do_toggle = true;
729 	if (ev->button == 1) {
730 		if (_log_not_acknowledged == LogLevelError) {
731 			// just acknowledge the error, don't hide the log if it's already visible
732 			RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Editor"), X_("toggle-log-window"));
733 			if (tact->get_active()) {
734 				do_toggle = false;
735 			}
736 		}
737 		_log_not_acknowledged = LogLevelNone;
738 		error_blink (false); // immediate acknowledge
739 	}
740 	// maybe fall through to to button toggle
741 	return !do_toggle;
742 }
743 
744 void
layered_button_clicked()745 ARDOUR_UI::layered_button_clicked ()
746 {
747 	if (_session) {
748 		_session->config.set_layered_record_mode (!_session->config.get_layered_record_mode ());
749 	}
750 }
751 
752 void
solo_blink(bool onoff)753 ARDOUR_UI::solo_blink (bool onoff)
754 {
755 	if (_session == 0) {
756 		return;
757 	}
758 
759 	if (_session->soloing() || _session->listening()) {
760 		if (onoff) {
761 			solo_alert_button.set_active (true);
762 		} else {
763 			solo_alert_button.set_active (false);
764 		}
765 	} else {
766 		solo_alert_button.set_active (false);
767 	}
768 }
769 
770 void
sync_blink(bool onoff)771 ARDOUR_UI::sync_blink (bool onoff)
772 {
773 	if (_session == 0 || !_session->config.get_external_sync()) {
774 		/* internal sync */
775 		sync_button.set_active (false);
776 		return;
777 	}
778 
779 	if (!_session->transport_locked()) {
780 		/* not locked, so blink on and off according to the onoff argument */
781 
782 		if (onoff) {
783 			sync_button.set_active (true);
784 		} else {
785 			sync_button.set_active (false);
786 		}
787 	} else {
788 		/* locked */
789 		sync_button.set_active (true);
790 	}
791 }
792 
793 void
audition_blink(bool onoff)794 ARDOUR_UI::audition_blink (bool onoff)
795 {
796 	if (_session == 0) {
797 		return;
798 	}
799 
800 	if (_session->is_auditioning()) {
801 		if (onoff) {
802 			auditioning_alert_button.set_active (true);
803 		} else {
804 			auditioning_alert_button.set_active (false);
805 		}
806 	} else {
807 		auditioning_alert_button.set_active (false);
808 	}
809 }
810 
811 void
feedback_blink(bool onoff)812 ARDOUR_UI::feedback_blink (bool onoff)
813 {
814 	if (_feedback_exists) {
815 		feedback_alert_button.set_active (true);
816 		feedback_alert_button.set_text (_("Feedback"));
817 		if (onoff) {
818 			feedback_alert_button.reset_fixed_colors ();
819 		} else {
820 			feedback_alert_button.set_active_color (UIConfigurationBase::instance().color ("feedback alert: alt active", NULL));
821 		}
822 	} else if (_ambiguous_latency && !UIConfiguration::instance().get_show_toolbar_latency ()) {
823 		feedback_alert_button.set_text (_("No Align"));
824 		feedback_alert_button.set_active (true);
825 		if (onoff) {
826 			feedback_alert_button.reset_fixed_colors ();
827 		} else {
828 			feedback_alert_button.set_active_color (UIConfigurationBase::instance().color ("feedback alert: alt active", NULL));
829 		}
830 	} else {
831 		feedback_alert_button.set_text ("Feedback");
832 		feedback_alert_button.reset_fixed_colors ();
833 		feedback_alert_button.set_active (false);
834 	}
835 }
836 
837 void
error_blink(bool onoff)838 ARDOUR_UI::error_blink (bool onoff)
839 {
840 	switch (_log_not_acknowledged) {
841 		case LogLevelError:
842 			// blink
843 			if (onoff) {
844 				error_alert_button.set_custom_led_color(0xff0000ff); // bright red
845 			} else {
846 				error_alert_button.set_custom_led_color(0x880000ff); // dark red
847 			}
848 			break;
849 		case LogLevelWarning:
850 			error_alert_button.set_custom_led_color(0xccaa00ff); // yellow
851 			break;
852 		case LogLevelInfo:
853 			error_alert_button.set_custom_led_color(0x88cc00ff); // lime green
854 			break;
855 		default:
856 			error_alert_button.set_custom_led_color(0x333333ff); // gray
857 			break;
858 	}
859 }
860 void
set_transport_sensitivity(bool yn)861 ARDOUR_UI::set_transport_sensitivity (bool yn)
862 {
863 	ActionManager::set_sensitive (ActionManager::transport_sensitive_actions, yn);
864 	shuttle_box.set_sensitive (yn);
865 }
866 
867 void
set_punch_sensitivity()868 ARDOUR_UI::set_punch_sensitivity ()
869 {
870 	bool can_punch = _session && _session->punch_is_possible() && _session->locations()->auto_punch_location ();
871 	ActionManager::get_action ("Transport", "TogglePunchIn")->set_sensitive (can_punch);
872 	ActionManager::get_action ("Transport", "TogglePunchOut")->set_sensitive (can_punch);
873 }
874 
875 void
editor_realized()876 ARDOUR_UI::editor_realized ()
877 {
878 	boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
879 	Config->map_parameters (pc);
880 
881 	UIConfiguration::instance().reset_dpi ();
882 }
883 
884 void
maximise_editing_space()885 ARDOUR_UI::maximise_editing_space ()
886 {
887 	if (editor) {
888 		editor->maximise_editing_space ();
889 	}
890 }
891 
892 void
restore_editing_space()893 ARDOUR_UI::restore_editing_space ()
894 {
895 	if (editor) {
896 		editor->restore_editing_space ();
897 	}
898 }
899 
900 void
show_ui_prefs()901 ARDOUR_UI::show_ui_prefs ()
902 {
903 	if (rc_option_editor) {
904 		show_tabbable (rc_option_editor);
905 		rc_option_editor->set_current_page (_("Appearance"));
906 	}
907 }
908 void
show_mixer_prefs()909 ARDOUR_UI::show_mixer_prefs ()
910 {
911 	if (rc_option_editor) {
912 		show_tabbable (rc_option_editor);
913 		rc_option_editor->set_current_page (_("Signal Flow"));
914 	}
915 }
916 
917 bool
click_button_clicked(GdkEventButton * ev)918 ARDOUR_UI::click_button_clicked (GdkEventButton* ev)
919 {
920 	if (ev->button != 3) {
921 		/* this handler is just for button-3 clicks */
922 		return false;
923 	}
924 
925 	show_tabbable (rc_option_editor);
926 	rc_option_editor->set_current_page (_("Metronome"));
927 	return true;
928 }
929 
930 bool
sync_button_clicked(GdkEventButton * ev)931 ARDOUR_UI::sync_button_clicked (GdkEventButton* ev)
932 {
933 	if (ev->button != 3) {
934 		/* this handler is just for button-3 clicks */
935 		return false;
936 	}
937 
938 	Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action ("Window", "toggle-transport-masters");
939 	tact->set_active();
940 	return true;
941 }
942 
943 void
toggle_follow_edits()944 ARDOUR_UI::toggle_follow_edits ()
945 {
946 	RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Transport"), X_("ToggleFollowEdits"));
947 	UIConfiguration::instance().set_follow_edits (tact->get_active ());
948 }
949 
950 void
update_title()951 ARDOUR_UI::update_title ()
952 {
953 	stringstream snap_label;
954 	snap_label << X_("<span weight=\"ultralight\">")
955 	           << _("Name")
956 	           << X_("</span>: ");
957 
958 	if (_session) {
959 		bool dirty = _session->dirty();
960 
961 		string session_name;
962 
963 		if (_session->snap_name() != _session->name()) {
964 			session_name = _session->snap_name();
965 		} else {
966 			session_name = _session->name();
967 		}
968 
969 		if (dirty) {
970 			session_name = "*" + session_name;
971 		}
972 
973 		WindowTitle title (session_name);
974 		title += Glib::get_application_name();
975 		_main_window.set_title (title.get_string());
976 
977 		snap_label << Gtkmm2ext::markup_escape_text (session_name);
978 	} else {
979 		WindowTitle title (Glib::get_application_name());
980 		_main_window.set_title (title.get_string());
981 		snap_label << "-";
982 	}
983 	snapshot_name_label.set_markup (snap_label.str());
984 }
985 
986