8234b476f673b480c2a370b814465b7fba58a571
[ardour.git] / gtk2_ardour / ardour_ui2.cc
1 /*
2     Copyright (C) 1999 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <unistd.h>
27 #include <cerrno>
28 #include <iostream>
29 #include <cmath>
30
31 #include <sigc++/bind.h>
32 #include <gtkmm/settings.h>
33
34 #include "canvas/canvas.h"
35
36 #include "pbd/error.h"
37 #include "pbd/basename.h"
38 #include "pbd/fastlog.h"
39
40 #include "gtkmm2ext/utils.h"
41 #include "gtkmm2ext/window_title.h"
42
43 #include "ardour/profile.h"
44 #include "ardour/session.h"
45 #include "ardour/types.h"
46
47 #include "ardour_ui.h"
48 #include "keyboard.h"
49 #include "public_editor.h"
50 #include "audio_clock.h"
51 #include "actions.h"
52 #include "main_clock.h"
53 #include "mixer_ui.h"
54 #include "utils.h"
55 #include "time_info_box.h"
56 #include "midi_tracer.h"
57 #include "global_port_matrix.h"
58 #include "location_ui.h"
59 #include "rc_option_editor.h"
60
61 #include "pbd/i18n.h"
62
63 using namespace std;
64 using namespace ARDOUR;
65 using namespace PBD;
66 using namespace Gtkmm2ext;
67 using namespace ArdourWidgets;
68 using namespace Gtk;
69 using namespace Glib;
70 using namespace ARDOUR_UI_UTILS;
71
72 void
73 ARDOUR_UI::setup_tooltips ()
74 {
75         ArdourCanvas::Canvas::set_tooltip_timeout (Gtk::Settings::get_default()->property_gtk_tooltip_timeout ());
76
77         set_tip (auto_return_button, _("Return to last playback start when stopped"));
78         set_tip (follow_edits_button, _("Playhead follows Range tool clicks, and Range selections"));
79         set_tip (auto_input_button, _("Track Input Monitoring automatically follows transport state"));
80         parameter_changed("click-gain");
81         set_tip (solo_alert_button, _("When active, something is soloed.\nClick to de-solo everything"));
82         set_tip (auditioning_alert_button, _("When active, auditioning is taking place.\nClick to stop the audition"));
83         set_tip (feedback_alert_button, _("When active, there is a feedback loop."));
84         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"));
85         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"));
86         set_tip (editor_meter_peak_display, _("Reset All Peak Indicators"));
87         set_tip (error_alert_button, _("Show Error Log and acknowledge warnings"));
88
89         synchronize_sync_source_and_video_pullup ();
90
91         editor->setup_tooltips ();
92 }
93
94 bool
95 ARDOUR_UI::status_bar_button_press (GdkEventButton* ev)
96 {
97         bool handled = false;
98
99         switch (ev->button) {
100         case 1:
101                 status_bar_label.set_text ("");
102                 handled = true;
103                 break;
104         default:
105                 break;
106         }
107
108         return handled;
109 }
110
111 XMLNode*
112 ARDOUR_UI::tearoff_settings (const char* name) const
113 {
114         XMLNode* ui_node = Config->extra_xml(X_("UI"));
115
116         if (ui_node) {
117                 XMLNode* tearoff_node = ui_node->child (X_("Tearoffs"));
118                 if (tearoff_node) {
119                         XMLNode* mnode = tearoff_node->child (name);
120                         return mnode;
121                 }
122         }
123
124         return 0;
125 }
126
127 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
128
129 static
130 bool drag_failed (const Glib::RefPtr<Gdk::DragContext>& context, DragResult result, Tabbable* tab)
131 {
132         if (result == Gtk::DRAG_RESULT_NO_TARGET) {
133                 tab->detach ();
134                 return true;
135         }
136         return false;
137 }
138
139 void
140 ARDOUR_UI::repack_transport_hbox ()
141 {
142         if (dsp_load_indicator.get_parent()) {
143                 transport_hbox.remove (dsp_load_indicator);
144         }
145         if (UIConfiguration::instance().get_show_dsp_load_info ()) {
146                 transport_hbox.pack_start (dsp_load_indicator, false, false);
147                 dsp_load_indicator.show();
148         }
149
150         if (disk_space_indicator.get_parent()) {
151                 transport_hbox.remove (disk_space_indicator);
152         }
153         if (UIConfiguration::instance().get_show_disk_space_info ()) {
154                 transport_hbox.pack_start (disk_space_indicator, false, false);
155                 disk_space_indicator.show();
156         }
157
158         if (status_spacer.get_parent()) {
159                 transport_hbox.remove (status_spacer);
160         }
161         transport_hbox.pack_start (status_spacer, false, false, 3);
162
163         if (time_info_box) {
164                 if (time_info_box->get_parent()) {
165                         transport_hbox.remove (*time_info_box);
166                 }
167                 if (UIConfiguration::instance().get_show_toolbar_selclock ()) {
168                         transport_hbox.pack_start (*time_info_box, false, false);
169                         time_info_box->show();
170                 }
171         }
172
173         if (mini_timeline.get_parent()) {
174                 transport_hbox.remove (mini_timeline);
175         }
176         if (UIConfiguration::instance().get_show_mini_timeline ()) {
177                 transport_hbox.pack_start (mini_timeline, true, true);
178                 mini_timeline.show();
179         }
180
181         if (editor_meter) {
182                 if (editor_meter_table.get_parent()) {
183                         transport_hbox.remove (editor_meter_table);
184                 }
185                 if (meterbox_spacer.get_parent()) {
186                         transport_hbox.remove (meterbox_spacer);
187                         transport_hbox.remove (meterbox_spacer2);
188                 }
189
190                 if (UIConfiguration::instance().get_show_editor_meter()) {
191                         transport_hbox.pack_end (meterbox_spacer, false, false, 3);
192                         transport_hbox.pack_end (editor_meter_table, false, false);
193                         transport_hbox.pack_end (meterbox_spacer2, false, false, 3);
194                         editor_meter_table.show();
195                         meterbox_spacer.show();
196                         meterbox_spacer2.show();
197                 }
198         }
199
200         bool show_mon = UIConfiguration::instance().get_show_toolbar_monitoring ();
201         if (show_mon) {
202                 monitor_in_button.show ();
203                 monitor_disk_button.show ();
204                 auto_input_button.show ();
205                 monitoring_spacer.show ();
206         } else {
207                 monitor_in_button.hide ();
208                 monitor_disk_button.hide ();
209                 auto_input_button.hide ();
210                 monitoring_spacer.hide ();
211         }
212
213         bool show_rec = UIConfiguration::instance().get_show_toolbar_recpunch ();
214         if (show_rec) {
215                 punch_label.show ();
216                 layered_label.show ();
217                 punch_in_button.show ();
218                 punch_out_button.show ();
219                 layered_button.show ();
220                 recpunch_spacer.show ();
221         } else {
222                 punch_label.hide ();
223                 layered_label.hide ();
224                 punch_in_button.hide ();
225                 punch_out_button.hide ();
226                 layered_button.hide ();
227                 recpunch_spacer.hide ();
228         }
229
230 }
231
232 void
233 ARDOUR_UI::update_clock_visibility ()
234 {
235         if (ARDOUR::Profile->get_small_screen()) {
236                 return;
237         }
238         if (UIConfiguration::instance().get_show_secondary_clock ()) {
239                 secondary_clock->show();
240                 secondary_clock->left_btn()->show();
241                 secondary_clock->right_btn()->show();
242                 if (secondary_clock_spacer) {
243                         secondary_clock_spacer->show();
244                 }
245         } else {
246                 secondary_clock->hide();
247                 secondary_clock->left_btn()->hide();
248                 secondary_clock->right_btn()->hide();
249                 if (secondary_clock_spacer) {
250                         secondary_clock_spacer->hide();
251                 }
252         }
253 }
254
255 void
256 ARDOUR_UI::setup_transport ()
257 {
258         RefPtr<Action> act;
259         /* setup actions */
260
261         act = ActionManager::get_action (X_("Transport"), X_("ToggleExternalSync"));
262         sync_button.set_related_action (act);
263         sync_button.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::sync_button_clicked), false);
264
265         sync_button.set_sizing_text (S_("LogestSync|M-Clk"));
266
267         /* CANNOT sigc::bind these to clicked or toggled, must use pressed or released */
268         act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
269         solo_alert_button.set_related_action (act);
270         auditioning_alert_button.signal_clicked.connect (sigc::mem_fun(*this,&ARDOUR_UI::audition_alert_clicked));
271         error_alert_button.signal_button_release_event().connect (sigc::mem_fun(*this,&ARDOUR_UI::error_alert_press), false);
272         act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
273         error_alert_button.set_related_action(act);
274         error_alert_button.set_fallthrough_to_parent(true);
275
276         layered_button.signal_clicked.connect (sigc::mem_fun(*this,&ARDOUR_UI::layered_button_clicked));
277
278         editor_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-editor-visibility")));
279         mixer_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-mixer-visibility")));
280         prefs_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-preferences-visibility")));
281
282         act = ActionManager::get_action ("Transport", "ToggleAutoReturn");
283         auto_return_button.set_related_action (act);
284         act = ActionManager::get_action (X_("Transport"), X_("ToggleFollowEdits"));
285         follow_edits_button.set_related_action (act);
286         act = ActionManager::get_action ("Transport", "ToggleAutoInput");
287         auto_input_button.set_related_action (act);
288
289         act = ActionManager::get_action ("Transport", "TogglePunchIn");
290         punch_in_button.set_related_action (act);
291         act = ActionManager::get_action ("Transport", "TogglePunchOut");
292         punch_out_button.set_related_action (act);
293
294         act = ActionManager::get_action ("Transport", "SessionMonitorIn");
295         monitor_in_button.set_related_action (act);
296         act = ActionManager::get_action ("Transport", "SessionMonitorDisk");
297         monitor_disk_button.set_related_action (act);
298
299         /* connect signals */
300         ARDOUR_UI::Clock.connect (sigc::bind (sigc::mem_fun (primary_clock, &MainClock::set), false, 0));
301         ARDOUR_UI::Clock.connect (sigc::bind (sigc::mem_fun (secondary_clock, &MainClock::set), false, 0));
302
303         primary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::primary_clock_value_changed));
304         secondary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::secondary_clock_value_changed));
305         big_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::big_clock_value_changed));
306
307         editor_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), editor));
308         mixer_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), mixer));
309         prefs_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), rc_option_editor));
310
311         /* catch context clicks so that we can show a menu on these buttons */
312
313         editor_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("editor")), false);
314         mixer_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("mixer")), false);
315         prefs_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("preferences")), false);
316
317         /* setup widget style/name */
318
319         auto_return_button.set_name ("transport option button");
320         follow_edits_button.set_name ("transport option button");
321
322         solo_alert_button.set_name ("rude solo");
323         auditioning_alert_button.set_name ("rude audition");
324         feedback_alert_button.set_name ("feedback alert");
325         error_alert_button.set_name ("error alert");
326
327         solo_alert_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
328         auditioning_alert_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
329         feedback_alert_button.set_elements (ArdourButton::Element(ArdourButton::Body|ArdourButton::Text));
330
331         solo_alert_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
332         auditioning_alert_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
333         feedback_alert_button.set_layout_font (UIConfiguration::instance().get_SmallerFont());
334
335         editor_visibility_button.set_name (X_("page switch button"));
336         mixer_visibility_button.set_name (X_("page switch button"));
337         prefs_visibility_button.set_name (X_("page switch button"));
338
339         punch_in_button.set_name ("punch button");
340         punch_out_button.set_name ("punch button");
341         layered_button.set_name (("layered button"));
342
343         monitor_in_button.set_name ("monitor button");
344         monitor_disk_button.set_name ("monitor button");
345         auto_input_button.set_name ("transport option button");
346
347         sync_button.set_name ("transport active option button");
348
349         /* and widget text */
350         auto_return_button.set_text(_("Auto Return"));
351         follow_edits_button.set_text(_("Follow Range"));
352         punch_in_button.set_text (_("In"));
353         punch_out_button.set_text (_("Out"));
354         layered_button.set_text (_("Non-Layered"));
355
356         monitor_in_button.set_text (_("All In"));
357         monitor_disk_button.set_text (_("All Disk"));
358         auto_input_button.set_text (_("Auto-Input"));
359
360         punch_label.set_text (_("Punch:"));
361         layered_label.set_text (_("Rec:"));
362
363         /* and tooltips */
364
365         Gtkmm2ext::UI::instance()->set_tip (editor_visibility_button,
366                                             string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
367                                                               "To re-attach the window, use the Window > %1 > Attach menu action"), editor->name()));
368
369         Gtkmm2ext::UI::instance()->set_tip (mixer_visibility_button,
370                                             string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
371                                                               "To re-attach the window, use the Window > %1 > Attach menu action"), mixer->name()));
372
373         Gtkmm2ext::UI::instance()->set_tip (prefs_visibility_button,
374                                             string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
375                                                               "To re-attach the window, use the Window > %1 > Attach menu action"), rc_option_editor->name()));
376
377         Gtkmm2ext::UI::instance()->set_tip (punch_in_button, _("Start recording at auto-punch start"));
378         Gtkmm2ext::UI::instance()->set_tip (punch_out_button, _("Stop recording at auto-punch end"));
379
380         Gtkmm2ext::UI::instance()->set_tip (monitor_in_button, _("Force all tracks to monitor Input, unless they are explicitly set to monitor Disk"));
381         Gtkmm2ext::UI::instance()->set_tip (monitor_disk_button, _("Force all tracks to monitor Disk playback, unless they are explicitly set to Input"));
382
383         /* transport control size-group */
384
385         Glib::RefPtr<SizeGroup> punch_button_size_group = SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL);
386         punch_button_size_group->add_widget (punch_in_button);
387         punch_button_size_group->add_widget (punch_out_button);
388
389         Glib::RefPtr<SizeGroup> monitor_button_size_group = SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL);
390         monitor_button_size_group->add_widget (monitor_in_button);
391         monitor_button_size_group->add_widget (monitor_disk_button);
392
393         /* and now the layout... */
394
395         /* top level packing */
396         transport_table.set_spacings (0);
397         transport_table.set_row_spacings (4);
398         transport_table.set_border_width (2);
399
400         transport_frame.set_name ("TransportFrame");
401         transport_frame.set_shadow_type (Gtk::SHADOW_NONE);
402
403         /* An event box to hold the table. We use this because we want specific
404            control over the background color, and without this event box,
405            nothing inside the transport_sample actually draws a background. We
406            would therefore end up seeing the background of the parent widget,
407            which is probably some default color. Adding the EventBox adds a
408            widget that will draw the background, using a style based on
409            the parent, "TransportFrame".
410         */
411         Gtk::EventBox* ebox = manage (new Gtk::EventBox);
412         transport_frame.add (*ebox);
413         ebox->add (transport_table);
414
415         /* alert box sub-group */
416         VBox* alert_box = manage (new VBox);
417         alert_box->set_homogeneous (true);
418         alert_box->set_spacing (1);
419         alert_box->set_border_width (0);
420         alert_box->pack_start (solo_alert_button, true, true);
421         alert_box->pack_start (auditioning_alert_button, true, true);
422         alert_box->pack_start (feedback_alert_button, true, true);
423
424         /* clock button size groups */
425         Glib::RefPtr<SizeGroup> button_height_size_group = SizeGroup::create (Gtk::SIZE_GROUP_VERTICAL);
426         button_height_size_group->add_widget (follow_edits_button);
427         button_height_size_group->add_widget (*primary_clock->left_btn());
428         button_height_size_group->add_widget (*primary_clock->right_btn());
429         button_height_size_group->add_widget (*secondary_clock->left_btn());
430         button_height_size_group->add_widget (*secondary_clock->right_btn());
431
432         button_height_size_group->add_widget (transport_ctrl.size_button ());
433 //      button_height_size_group->add_widget (sync_button);
434         button_height_size_group->add_widget (auto_return_button);
435
436         //tab selections
437         button_height_size_group->add_widget (editor_visibility_button);
438         button_height_size_group->add_widget (mixer_visibility_button);
439
440         //punch section
441         button_height_size_group->add_widget (punch_in_button);
442         button_height_size_group->add_widget (punch_out_button);
443         button_height_size_group->add_widget (layered_button);
444
445         //input monitoring section
446         button_height_size_group->add_widget (monitor_in_button);
447         button_height_size_group->add_widget (monitor_disk_button);
448         button_height_size_group->add_widget (auto_input_button);
449
450         Glib::RefPtr<SizeGroup> clock1_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
451         clock1_size_group->add_widget (*primary_clock->left_btn());
452         clock1_size_group->add_widget (*primary_clock->right_btn());
453
454         Glib::RefPtr<SizeGroup> clock2_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
455         clock2_size_group->add_widget (*secondary_clock->left_btn());
456         clock2_size_group->add_widget (*secondary_clock->right_btn());
457
458         /* sub-layout for Sync | Shuttle (grow) */
459         HBox* ssbox = manage (new HBox);
460         ssbox->set_spacing (PX_SCALE(2));
461         ssbox->pack_start (sync_button, false, false, 0);
462         ssbox->pack_start (shuttle_box, true, true, 0);
463         ssbox->pack_start (*shuttle_box.info_button(), false, false, 0);
464
465
466         /* and the main table layout */
467         int vpadding = 1;
468         int hpadding = 2;
469         int col = 0;
470 #define TCOL col, col + 1
471
472         transport_table.attach (transport_ctrl, TCOL, 0, 1 , SHRINK, SHRINK, 0, 0);
473         transport_table.attach (*ssbox, TCOL, 1, 2 , FILL, SHRINK, 0, 0);
474         ++col;
475
476         transport_table.attach (*(manage (new ArdourVSpacer ())), TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
477         ++col;
478
479         transport_table.attach (punch_label, TCOL, 0, 1 , FILL, SHRINK, 3, 0);
480         transport_table.attach (layered_label, TCOL, 1, 2 , FILL, SHRINK, 3, 0);
481         ++col;
482
483         transport_table.attach (punch_in_button,  col,      col + 1, 0, 1 , FILL, SHRINK, hpadding, vpadding);
484         transport_table.attach (punch_space,      col + 1,  col + 2, 0, 1 , FILL, SHRINK, 0, vpadding);
485         transport_table.attach (punch_out_button, col + 2,  col + 3, 0, 1 , FILL, SHRINK, hpadding, vpadding);
486         transport_table.attach (layered_button,   col,      col + 3, 1, 2 , FILL, SHRINK, hpadding, vpadding);
487         col += 3;
488
489         transport_table.attach (recpunch_spacer, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
490         ++col;
491
492         transport_table.attach (auto_input_button,   col,     col + 3, 0, 1 , FILL, SHRINK, hpadding, vpadding);
493         transport_table.attach (monitor_in_button,   col,     col + 1, 1, 2 , FILL, SHRINK, hpadding, vpadding);
494         transport_table.attach (mon_space,           col + 1, col + 2, 1, 2 , FILL, SHRINK, 2, vpadding);
495         transport_table.attach (monitor_disk_button, col + 2, col + 3, 1, 2 , FILL, SHRINK, hpadding, vpadding);
496         col += 3;
497
498         transport_table.attach (monitoring_spacer, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
499         ++col;
500
501         transport_table.attach (follow_edits_button, TCOL, 0, 1 , FILL, SHRINK, hpadding, vpadding);
502         transport_table.attach (auto_return_button,  TCOL, 1, 2 , FILL, SHRINK, hpadding, vpadding);
503         ++col;
504
505         transport_table.attach (*(manage (new ArdourVSpacer ())), TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
506         ++col;
507
508         transport_table.attach (*primary_clock,              col,     col + 2, 0, 1 , FILL, SHRINK, hpadding, 0);
509         transport_table.attach (*primary_clock->left_btn(),  col,     col + 1, 1, 2 , FILL, SHRINK, hpadding, 0);
510         transport_table.attach (*primary_clock->right_btn(), col + 1, col + 2, 1, 2 , FILL, SHRINK, hpadding, 0);
511         col += 2;
512
513         transport_table.attach (*(manage (new ArdourVSpacer ())), TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
514         ++col;
515
516         if (!ARDOUR::Profile->get_small_screen()) {
517                 transport_table.attach (*secondary_clock,              col,     col + 2, 0, 1 , FILL, SHRINK, hpadding, 0);
518                 transport_table.attach (*secondary_clock->left_btn(),  col,     col + 1, 1, 2 , FILL, SHRINK, hpadding, 0);
519                 transport_table.attach (*secondary_clock->right_btn(), col + 1, col + 2, 1, 2 , FILL, SHRINK, hpadding, 0);
520                 secondary_clock->set_no_show_all (true);
521                 secondary_clock->left_btn()->set_no_show_all (true);
522                 secondary_clock->right_btn()->set_no_show_all (true);
523                 col += 2;
524
525                 secondary_clock_spacer = manage (new ArdourVSpacer ());
526                 transport_table.attach (*secondary_clock_spacer, TCOL, 0, 2 , SHRINK, EXPAND|FILL, 3, 0);
527                 ++col;
528         }
529
530         transport_table.attach (*alert_box, TCOL, 0, 2, SHRINK, EXPAND|FILL, hpadding, 0);
531         ++col;
532
533         /* editor-meter, mini-timeline and selection clock are options in the transport_hbox */
534         transport_hbox.set_spacing (3);
535         transport_table.attach (transport_hbox, TCOL, 0, 2, EXPAND|FILL, EXPAND|FILL, hpadding, 0);
536         ++col;
537
538         /* lua script action buttons */
539         transport_table.attach (action_script_table, TCOL, 0, 2, SHRINK, EXPAND|FILL, 1, 0);
540         ++col;
541
542         transport_table.attach (editor_visibility_button, TCOL, 0, 1 , FILL, SHRINK, hpadding, vpadding);
543         transport_table.attach (mixer_visibility_button,  TCOL, 1, 2 , FILL, SHRINK, hpadding, vpadding);
544         ++col;
545
546         repack_transport_hbox ();
547         update_clock_visibility ();
548         /* desensitize */
549
550         feedback_alert_button.set_sensitive (false);
551         feedback_alert_button.set_visual_state (Gtkmm2ext::NoVisualState);
552         auditioning_alert_button.set_sensitive (false);
553         auditioning_alert_button.set_visual_state (Gtkmm2ext::NoVisualState);
554
555         set_transport_sensitivity (false);
556 }
557 #undef PX_SCALE
558 #undef TCOL
559
560 void
561 ARDOUR_UI::soloing_changed (bool onoff)
562 {
563         if (solo_alert_button.get_active() != onoff) {
564                 solo_alert_button.set_active (onoff);
565         }
566 }
567
568 void
569 ARDOUR_UI::_auditioning_changed (bool onoff)
570 {
571         auditioning_alert_button.set_active (onoff);
572         auditioning_alert_button.set_sensitive (onoff);
573         if (!onoff) {
574                 auditioning_alert_button.set_visual_state (Gtkmm2ext::NoVisualState);
575         }
576         set_transport_sensitivity (!onoff);
577 }
578
579 void
580 ARDOUR_UI::auditioning_changed (bool onoff)
581 {
582         UI::instance()->call_slot (MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::_auditioning_changed, this, onoff));
583 }
584
585 void
586 ARDOUR_UI::audition_alert_clicked ()
587 {
588         if (_session) {
589                 _session->cancel_audition();
590         }
591 }
592
593 bool
594 ARDOUR_UI::error_alert_press (GdkEventButton* ev)
595 {
596         bool do_toggle = true;
597         if (ev->button == 1) {
598                 if (_log_not_acknowledged == LogLevelError) {
599                         // just acknowledge the error, don't hide the log if it's already visible
600                         RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
601                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
602                         if (tact && tact->get_active()) {
603                                 do_toggle = false;
604                         }
605                 }
606                 _log_not_acknowledged = LogLevelNone;
607                 error_blink (false); // immediate acknowledge
608         }
609         // maybe fall through to to button toggle
610         return !do_toggle;
611 }
612
613 void
614 ARDOUR_UI::layered_button_clicked ()
615 {
616         if (_session) {
617                 _session->config.set_layered_record_mode (!_session->config.get_layered_record_mode ());
618         }
619 }
620
621 void
622 ARDOUR_UI::solo_blink (bool onoff)
623 {
624         if (_session == 0) {
625                 return;
626         }
627
628         if (_session->soloing() || _session->listening()) {
629                 if (onoff) {
630                         solo_alert_button.set_active (true);
631                 } else {
632                         solo_alert_button.set_active (false);
633                 }
634         } else {
635                 solo_alert_button.set_active (false);
636         }
637 }
638
639 void
640 ARDOUR_UI::sync_blink (bool onoff)
641 {
642         if (_session == 0 || !_session->config.get_external_sync()) {
643                 /* internal sync */
644                 sync_button.set_active (false);
645                 return;
646         }
647
648         if (!_session->transport_locked()) {
649                 /* not locked, so blink on and off according to the onoff argument */
650
651                 if (onoff) {
652                         sync_button.set_active (true);
653                 } else {
654                         sync_button.set_active (false);
655                 }
656         } else {
657                 /* locked */
658                 sync_button.set_active (true);
659         }
660 }
661
662 void
663 ARDOUR_UI::audition_blink (bool onoff)
664 {
665         if (_session == 0) {
666                 return;
667         }
668
669         if (_session->is_auditioning()) {
670                 if (onoff) {
671                         auditioning_alert_button.set_active (true);
672                 } else {
673                         auditioning_alert_button.set_active (false);
674                 }
675         } else {
676                 auditioning_alert_button.set_active (false);
677         }
678 }
679
680 void
681 ARDOUR_UI::feedback_blink (bool onoff)
682 {
683         if (_feedback_exists) {
684                 if (onoff) {
685                         feedback_alert_button.set_active (true);
686                 } else {
687                         feedback_alert_button.set_active (false);
688                 }
689         } else {
690                 feedback_alert_button.set_active (false);
691         }
692 }
693
694 void
695 ARDOUR_UI::error_blink (bool onoff)
696 {
697         switch (_log_not_acknowledged) {
698                 case LogLevelError:
699                         // blink
700                         if (onoff) {
701                                 error_alert_button.set_custom_led_color(0xff0000ff); // bright red
702                         } else {
703                                 error_alert_button.set_custom_led_color(0x880000ff); // dark red
704                         }
705                         break;
706                 case LogLevelWarning:
707                         error_alert_button.set_custom_led_color(0xccaa00ff); // yellow
708                         break;
709                 case LogLevelInfo:
710                         error_alert_button.set_custom_led_color(0x88cc00ff); // lime green
711                         break;
712                 default:
713                         error_alert_button.set_custom_led_color(0x333333ff); // gray
714                         break;
715         }
716 }
717 void
718 ARDOUR_UI::set_transport_sensitivity (bool yn)
719 {
720         ActionManager::set_sensitive (ActionManager::transport_sensitive_actions, yn);
721         shuttle_box.set_sensitive (yn);
722 }
723
724 void
725 ARDOUR_UI::editor_realized ()
726 {
727         boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
728         Config->map_parameters (pc);
729
730         UIConfiguration::instance().reset_dpi ();
731 }
732
733 void
734 ARDOUR_UI::maximise_editing_space ()
735 {
736         if (editor) {
737                 editor->maximise_editing_space ();
738         }
739 }
740
741 void
742 ARDOUR_UI::restore_editing_space ()
743 {
744         if (editor) {
745                 editor->restore_editing_space ();
746         }
747 }
748
749 void
750 ARDOUR_UI::show_ui_prefs ()
751 {
752         if (rc_option_editor) {
753                 show_tabbable (rc_option_editor);
754                 rc_option_editor->set_current_page (_("Appearance"));
755         }
756 }
757
758 bool
759 ARDOUR_UI::click_button_clicked (GdkEventButton* ev)
760 {
761         if (ev->button != 3) {
762                 /* this handler is just for button-3 clicks */
763                 return false;
764         }
765
766         show_tabbable (rc_option_editor);
767         rc_option_editor->set_current_page (_("Metronome"));
768         return true;
769 }
770
771 bool
772 ARDOUR_UI::sync_button_clicked (GdkEventButton* ev)
773 {
774         if (ev->button != 3) {
775                 /* this handler is just for button-3 clicks */
776                 return false;
777         }
778
779         show_tabbable (rc_option_editor);
780         rc_option_editor->set_current_page (_("Sync"));
781         return true;
782 }
783
784 void
785 ARDOUR_UI::toggle_follow_edits ()
786 {
787         RefPtr<Action> act = ActionManager::get_action (X_("Transport"), X_("ToggleFollowEdits"));
788         assert (act);
789
790         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic (act);
791         assert (tact);
792
793         UIConfiguration::instance().set_follow_edits (tact->get_active ());
794 }
795
796 void
797 ARDOUR_UI::update_title ()
798 {
799         if (_session) {
800                 bool dirty = _session->dirty();
801
802                 string session_name;
803
804                 if (_session->snap_name() != _session->name()) {
805                         session_name = _session->snap_name();
806                 } else {
807                         session_name = _session->name();
808                 }
809
810                 if (dirty) {
811                         session_name = "*" + session_name;
812                 }
813
814                 WindowTitle title (session_name);
815                 title += Glib::get_application_name();
816                 _main_window.set_title (title.get_string());
817         } else {
818                 WindowTitle title (Glib::get_application_name());
819                 _main_window.set_title (title.get_string());
820         }
821
822 }