the basics of tabbed
[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 "pbd/error.h"
33 #include "pbd/basename.h"
34 #include "pbd/fastlog.h"
35 #include <gtkmm2ext/cairocell.h>
36 #include <gtkmm2ext/utils.h>
37 #include <gtkmm2ext/click_box.h>
38 #include <gtkmm2ext/tearoff.h>
39
40 #include "ardour/profile.h"
41 #include "ardour/session.h"
42 #include "ardour/types.h"
43
44 #include "ardour_ui.h"
45 #include "keyboard.h"
46 #include "public_editor.h"
47 #include "audio_clock.h"
48 #include "actions.h"
49 #include "main_clock.h"
50 #include "utils.h"
51 #include "theme_manager.h"
52 #include "midi_tracer.h"
53 #include "shuttle_control.h"
54 #include "global_port_matrix.h"
55 #include "location_ui.h"
56 #include "rc_option_editor.h"
57 #include "time_info_box.h"
58
59 #include "i18n.h"
60
61 using namespace std;
62 using namespace ARDOUR;
63 using namespace PBD;
64 using namespace Gtkmm2ext;
65 using namespace Gtk;
66 using namespace Glib;
67 using namespace ARDOUR_UI_UTILS;
68
69
70 static GtkNotebook*
71 tab_window_root_drop (GtkNotebook* src,
72                       GtkWidget* w,
73                       gint x,
74                       gint y,
75                       gpointer user_data)
76 {
77         Gtk::Notebook* nb = ARDOUR_UI::instance()->tab_window_root_drop (src, w, x, y, user_data);
78         if (nb) {
79                 return nb->gobj();
80         } else {
81                 return 0;
82         }
83 }
84
85 int
86 ARDOUR_UI::setup_windows ()
87 {
88         if (create_editor ()) {
89                 error << _("UI: cannot setup editor") << endmsg;
90                 return -1;
91         }
92
93         if (create_mixer ()) {
94                 error << _("UI: cannot setup mixer") << endmsg;
95                 return -1;
96         }
97
98         if (create_meterbridge ()) {
99                 error << _("UI: cannot setup meterbridge") << endmsg;
100                 return -1;
101         }
102
103         /* all other dialogs are created conditionally */
104
105         we_have_dependents ();
106
107 #ifdef TOP_MENUBAR
108         HBox* status_bar_packer = manage (new HBox);
109         EventBox* status_bar_event_box = manage (new EventBox);
110
111         status_bar_event_box->add (status_bar_label);
112         status_bar_event_box->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
113         status_bar_label.set_size_request (300, -1);
114         status_bar_packer->pack_start (*status_bar_event_box, true, true, 6);
115
116         status_bar_label.show ();
117         status_bar_event_box->show ();
118         status_bar_packer->show ();
119
120         status_bar_event_box->signal_button_press_event().connect (mem_fun (*this, &ARDOUR_UI::status_bar_button_press));
121
122         editor->get_status_bar_packer().pack_start (*status_bar_packer, true, true);
123         editor->get_status_bar_packer().pack_start (menu_bar_base, false, false, 2);
124 #else
125         top_packer.pack_start (menu_bar_base, false, false);
126 #endif
127
128         editor->add_toplevel_menu (top_packer);
129
130         editor->add_transport_frame (transport_frame);
131         editor->tabs().append_page (rc_option_editor_placeholder, _("Preferences"));
132
133         editor->tabs().signal_switch_page().connect (sigc::mem_fun (*this, &ARDOUR_UI::tabs_switch));
134
135         /* It would be nice if Gtkmm had wrapped this rather than just
136          * deprecating the old set_window_creation_hook() method, but oh well...
137          */
138         g_signal_connect (editor->tabs().gobj(), "create-window",
139                           (GCallback) ::tab_window_root_drop, this);
140
141         setup_transport();
142
143         build_menu_bar ();
144
145         setup_tooltips ();
146
147         return 0;
148 }
149
150 void
151 ARDOUR_UI::tabs_switch (GtkNotebookPage*, guint page_number)
152 {
153         if (page_number == 2) {
154                 if (!rc_option_editor) {
155                         rc_option_editor = new RCOptionEditor;
156                         rc_option_editor_placeholder.pack_start (*rc_option_editor, true, true);
157                         rc_option_editor_placeholder.show_all ();
158                 }
159         }
160 }
161
162 void
163 ARDOUR_UI::setup_tooltips ()
164 {
165         set_tip (roll_button, _("Play from playhead"));
166         set_tip (stop_button, _("Stop playback"));
167         set_tip (rec_button, _("Toggle record"));
168         set_tip (play_selection_button, _("Play range/selection"));
169         set_tip (goto_start_button, _("Go to start of session"));
170         set_tip (goto_end_button, _("Go to end of session"));
171         set_tip (auto_loop_button, _("Play loop range"));
172         set_tip (midi_panic_button, _("MIDI Panic\nSend note off and reset controller messages on all MIDI channels"));
173         set_tip (auto_return_button, _("Return to last playback start when stopped"));
174         set_tip (follow_edits_button, _("Playhead follows range selections and edits"));
175         set_tip (auto_input_button, _("Be sensible about input monitoring"));
176         set_tip (click_button, _("Enable/Disable audio click"));
177         set_tip (solo_alert_button, _("When active, something is soloed.\nClick to de-solo everything"));
178         set_tip (auditioning_alert_button, _("When active, auditioning is taking place.\nClick to stop the audition"));
179         set_tip (feedback_alert_button, _("When active, there is a feedback loop."));
180         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"));
181         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"));
182         set_tip (editor_meter_peak_display, _("Reset All Peak Indicators"));
183         set_tip (error_alert_button, _("Show Error Log and acknowledge warnings"));
184
185         synchronize_sync_source_and_video_pullup ();
186
187         editor->setup_tooltips ();
188 }
189
190 bool
191 ARDOUR_UI::status_bar_button_press (GdkEventButton* ev)
192 {
193         bool handled = false;
194
195         switch (ev->button) {
196         case 1:
197                 status_bar_label.set_text ("");
198                 handled = true;
199                 break;
200         default:
201                 break;
202         }
203
204         return handled;
205 }
206
207 void
208 ARDOUR_UI::display_message (const char *prefix, gint prefix_len, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg)
209 {
210         string text;
211
212         UI::display_message (prefix, prefix_len, ptag, mtag, msg);
213
214         ArdourLogLevel ll = LogLevelNone;
215
216         if (strcmp (prefix, _("[ERROR]: ")) == 0) {
217                 text = "<span color=\"red\" weight=\"bold\">";
218                 ll = LogLevelError;
219         } else if (strcmp (prefix, _("[WARNING]: ")) == 0) {
220                 text = "<span color=\"yellow\" weight=\"bold\">";
221                 ll = LogLevelWarning;
222         } else if (strcmp (prefix, _("[INFO]: ")) == 0) {
223                 text = "<span color=\"green\" weight=\"bold\">";
224                 ll = LogLevelInfo;
225         } else {
226                 text = "<span color=\"white\" weight=\"bold\">???";
227         }
228
229         _log_not_acknowledged = std::max(_log_not_acknowledged, ll);
230
231 #ifdef TOP_MENUBAR
232         text += prefix;
233         text += "</span>";
234         text += msg;
235
236         status_bar_label.set_markup (text);
237 #endif
238 }
239
240 XMLNode*
241 ARDOUR_UI::tearoff_settings (const char* name) const
242 {
243         XMLNode* ui_node = Config->extra_xml(X_("UI"));
244
245         if (ui_node) {
246                 XMLNode* tearoff_node = ui_node->child (X_("Tearoffs"));
247                 if (tearoff_node) {
248                         XMLNode* mnode = tearoff_node->child (name);
249                         return mnode;
250                 }
251         }
252
253         return 0;
254 }
255
256 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
257
258 void
259 ARDOUR_UI::setup_transport ()
260 {
261         RefPtr<Action> act;
262
263         transport_tearoff_hbox.set_border_width (PX_SCALE(3));
264         transport_tearoff_hbox.set_spacing (PX_SCALE(3));
265
266         transport_tearoff = manage (new TearOff (transport_tearoff_hbox));
267         transport_tearoff->set_name ("TransportBase");
268         transport_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &transport_tearoff->tearoff_window()), false);
269
270         if (Profile->get_sae() || Profile->get_mixbus()) {
271                 transport_tearoff->set_can_be_torn_off (false);
272         }
273
274         transport_hbox.pack_start (*transport_tearoff, true, false);
275
276         transport_base.set_name ("TransportBase");
277         transport_base.add (transport_hbox);
278
279         transport_frame.set_shadow_type (SHADOW_OUT);
280         transport_frame.set_name ("BaseFrame");
281         transport_frame.add (transport_base);
282
283         transport_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::detach_tearoff), static_cast<Box*>(&top_packer),
284                                                  static_cast<Widget*>(&transport_frame)));
285         transport_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::reattach_tearoff), static_cast<Box*> (&top_packer),
286                                                  static_cast<Widget*> (&transport_frame), 1));
287         transport_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::detach_tearoff), static_cast<Box*>(&top_packer),
288                                                  static_cast<Widget*>(&transport_frame)));
289         transport_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::reattach_tearoff), static_cast<Box*> (&top_packer),
290                                                   static_cast<Widget*> (&transport_frame), 1));
291
292         auto_return_button.set_text(_("Auto Return"));
293
294         follow_edits_button.set_text(_("Follow Edits"));
295
296 //      auto_input_button.set_text (_("Auto Input"));
297
298         click_button.set_icon (ArdourIcon::TransportMetronom);
299
300         act = ActionManager::get_action ("Transport", "ToggleClick");
301         click_button.set_related_action (act);
302         click_button.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::click_button_clicked), false);
303
304         auto_return_button.set_name ("transport option button");
305         follow_edits_button.set_name ("transport option button");
306         auto_input_button.set_name ("transport option button");
307
308         /* these have to provide a clear indication of active state */
309
310         click_button.set_name ("transport button");
311         sync_button.set_name ("transport active option button");
312
313         stop_button.set_active (true);
314
315         goto_start_button.set_icon (ArdourIcon::TransportStart);
316         goto_end_button.set_icon (ArdourIcon::TransportEnd);
317         roll_button.set_icon (ArdourIcon::TransportPlay);
318         stop_button.set_icon (ArdourIcon::TransportStop);
319         play_selection_button.set_icon (ArdourIcon::TransportRange);
320         auto_loop_button.set_icon (ArdourIcon::TransportLoop);
321         rec_button.set_icon (ArdourIcon::RecButton);
322         midi_panic_button.set_icon (ArdourIcon::TransportPanic);
323
324         act = ActionManager::get_action (X_("Transport"), X_("Stop"));
325         stop_button.set_related_action (act);
326         act = ActionManager::get_action (X_("Transport"), X_("Roll"));
327         roll_button.set_related_action (act);
328         act = ActionManager::get_action (X_("Transport"), X_("Record"));
329         rec_button.set_related_action (act);
330         act = ActionManager::get_action (X_("Transport"), X_("GotoStart"));
331         goto_start_button.set_related_action (act);
332         act = ActionManager::get_action (X_("Transport"), X_("GotoEnd"));
333         goto_end_button.set_related_action (act);
334         act = ActionManager::get_action (X_("Transport"), X_("Loop"));
335         auto_loop_button.set_related_action (act);
336         act = ActionManager::get_action (X_("Transport"), X_("PlaySelection"));
337         play_selection_button.set_related_action (act);
338         act = ActionManager::get_action (X_("MIDI"), X_("panic"));
339         midi_panic_button.set_related_action (act);
340         act = ActionManager::get_action (X_("Transport"), X_("ToggleExternalSync"));
341         sync_button.set_related_action (act);
342
343         /* clocks, etc. */
344
345         ARDOUR_UI::Clock.connect (sigc::mem_fun (primary_clock, &AudioClock::set));
346         ARDOUR_UI::Clock.connect (sigc::mem_fun (secondary_clock, &AudioClock::set));
347
348         primary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::primary_clock_value_changed));
349         secondary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::secondary_clock_value_changed));
350         big_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::big_clock_value_changed));
351
352         act = ActionManager::get_action ("Transport", "ToggleAutoReturn");
353         auto_return_button.set_related_action (act);
354         act = ActionManager::get_action (X_("Transport"), X_("ToggleFollowEdits"));
355         follow_edits_button.set_related_action (act);
356         act = ActionManager::get_action ("Transport", "ToggleAutoInput");
357         auto_input_button.set_related_action (act);
358
359         /* alerts */
360
361         /* CANNOT sigc::bind these to clicked or toggled, must use pressed or released */
362
363         solo_alert_button.set_name ("rude solo");
364         act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
365         solo_alert_button.set_related_action (act);
366         auditioning_alert_button.set_name ("rude audition");
367         auditioning_alert_button.signal_button_press_event().connect (sigc::mem_fun(*this,&ARDOUR_UI::audition_alert_press), false);
368         feedback_alert_button.set_name ("feedback alert");
369         feedback_alert_button.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::feedback_alert_press), false);
370         error_alert_button.set_name ("error alert");
371         error_alert_button.signal_button_release_event().connect (sigc::mem_fun(*this,&ARDOUR_UI::error_alert_press), false);
372         act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
373         error_alert_button.set_related_action(act);
374         error_alert_button.set_fallthrough_to_parent(true);
375
376         alert_box.set_homogeneous (true);
377         alert_box.set_spacing (PX_SCALE(2));
378         alert_box.pack_start (solo_alert_button, true, true);
379         alert_box.pack_start (auditioning_alert_button, true, true);
380         alert_box.pack_start (feedback_alert_button, true, true);
381
382         /* all transport buttons should be the same size vertically and
383          * horizontally
384          */
385
386         Glib::RefPtr<SizeGroup> transport_button_size_group = SizeGroup::create (SIZE_GROUP_BOTH);
387         transport_button_size_group->add_widget (goto_start_button);
388         transport_button_size_group->add_widget (goto_end_button);
389         transport_button_size_group->add_widget (auto_loop_button);
390         transport_button_size_group->add_widget (rec_button);
391         transport_button_size_group->add_widget (play_selection_button);
392         transport_button_size_group->add_widget (roll_button);
393         transport_button_size_group->add_widget (stop_button);
394
395         /* the icon for this has an odd aspect ratio, so fatten up the button */
396         midi_panic_button.set_size_request (PX_SCALE(25), -1);
397         goto_start_button.set_size_request (PX_SCALE(28), PX_SCALE(44));
398         click_button.set_size_request (PX_SCALE(32), PX_SCALE(44));
399
400
401         HBox* tbox1 = manage (new HBox);
402         HBox* tbox2 = manage (new HBox);
403         HBox* tbox = manage (new HBox);
404
405         VBox* vbox1 = manage (new VBox);
406         VBox* vbox2 = manage (new VBox);
407
408         Alignment* a1 = manage (new Alignment);
409         Alignment* a2 = manage (new Alignment);
410
411         tbox1->set_spacing (PX_SCALE(2));
412         tbox2->set_spacing (PX_SCALE(2));
413         tbox->set_spacing (PX_SCALE(2));
414
415         if (!Profile->get_trx()) {
416                 tbox1->pack_start (midi_panic_button, true, true, 5);
417                 tbox1->pack_start (click_button, true, true, 5);
418         }
419
420         tbox1->pack_start (goto_start_button, true, true);
421         tbox1->pack_start (goto_end_button, true, true);
422         tbox1->pack_start (auto_loop_button, true, true);
423
424         if (!Profile->get_trx()) {
425                 tbox2->pack_start (play_selection_button, true, true);
426         }
427         tbox2->pack_start (roll_button, true, true);
428         tbox2->pack_start (stop_button, true, true);
429         tbox2->pack_start (rec_button, true, true, 5);
430
431         vbox1->pack_start (*tbox1, true, true);
432         vbox2->pack_start (*tbox2, true, true);
433
434         a1->add (*vbox1);
435         a1->set (0.5, 0.5, 0.0, 1.0);
436         a2->add (*vbox2);
437         a2->set (0.5, 0.5, 0.0, 1.0);
438
439         tbox->pack_start (*a1, false, false);
440         tbox->pack_start (*a2, false, false);
441
442         HBox* clock_box = manage (new HBox);
443
444         clock_box->pack_start (*primary_clock, false, false);
445         if (!ARDOUR::Profile->get_small_screen() && !ARDOUR::Profile->get_trx()) {
446                 clock_box->pack_start (*secondary_clock, false, false);
447         }
448         clock_box->set_spacing (PX_SCALE(3));
449
450         shuttle_box = manage (new ShuttleControl);
451         shuttle_box->show ();
452
453         VBox* transport_vbox = manage (new VBox);
454         transport_vbox->set_name ("TransportBase");
455         transport_vbox->set_border_width (0);
456         transport_vbox->set_spacing (PX_SCALE(3));
457         transport_vbox->pack_start (*tbox, true, true, 0);
458
459         if (!Profile->get_trx()) {
460                 transport_vbox->pack_start (*shuttle_box, false, false, 0);
461         }
462
463         time_info_box = manage (new TimeInfoBox);
464
465         if (ARDOUR::Profile->get_trx()) {
466                 transport_tearoff_hbox.pack_start (*time_info_box, false, false);
467         }
468
469         transport_tearoff_hbox.pack_start (*transport_vbox, false, false);
470
471         /* transport related toggle controls */
472
473         VBox* auto_box = manage (new VBox);
474         auto_box->set_homogeneous (true);
475         auto_box->set_spacing (PX_SCALE(2));
476         auto_box->pack_start (sync_button, true, true);
477         if (!ARDOUR::Profile->get_trx()) {
478                 auto_box->pack_start (follow_edits_button, true, true);
479                 auto_box->pack_start (auto_return_button, true, true);
480         }
481
482         if (!ARDOUR::Profile->get_trx()) {
483                 transport_tearoff_hbox.pack_start (*auto_box, false, false);
484         }
485         transport_tearoff_hbox.pack_start (*clock_box, true, true);
486
487         if (ARDOUR::Profile->get_trx()) {
488                 transport_tearoff_hbox.pack_start (*auto_box, false, false);
489         }
490
491         if (!ARDOUR::Profile->get_trx()) {
492                 transport_tearoff_hbox.pack_start (*time_info_box, false, false);
493         }
494
495         if (!ARDOUR::Profile->get_trx()) {
496                 transport_tearoff_hbox.pack_start (alert_box, false, false);
497                 transport_tearoff_hbox.pack_start (meter_box, false, false);
498                 transport_tearoff_hbox.pack_start (editor_meter_peak_display, false, false);
499         }
500
501         if (Profile->get_sae()) {
502                 Image* img = manage (new Image ((::get_icon (X_("sae")))));
503                 transport_tearoff_hbox.pack_end (*img, false, false);
504         }
505
506         /* desensitize */
507
508         set_transport_sensitivity (false);
509
510         XMLNode* tnode = tearoff_settings ("transport");
511         if (tnode) {
512                 transport_tearoff->set_state (*tnode);
513         }
514 }
515 #undef PX_SCALE
516
517 void
518 ARDOUR_UI::detach_tearoff (Box* b, Widget* w)
519 {
520 //      editor->ensure_float (transport_tearoff->tearoff_window());
521         b->remove (*w);
522 }
523
524 void
525 ARDOUR_UI::reattach_tearoff (Box* b, Widget* w, int32_t n)
526 {
527         b->pack_start (*w);
528         b->reorder_child (*w, n);
529 }
530
531 void
532 ARDOUR_UI::reattach_all_tearoffs ()
533 {
534         if (transport_tearoff) transport_tearoff->put_it_back();
535         if (editor) editor->reattach_all_tearoffs ();
536 }
537
538 void
539 ARDOUR_UI::soloing_changed (bool onoff)
540 {
541         if (solo_alert_button.get_active() != onoff) {
542                 solo_alert_button.set_active (onoff);
543         }
544 }
545
546 void
547 ARDOUR_UI::_auditioning_changed (bool onoff)
548 {
549         auditioning_alert_button.set_active (onoff);
550         set_transport_sensitivity (!onoff);
551 }
552
553 void
554 ARDOUR_UI::auditioning_changed (bool onoff)
555 {
556         UI::instance()->call_slot (MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::_auditioning_changed, this, onoff));
557 }
558
559 bool
560 ARDOUR_UI::audition_alert_press (GdkEventButton*)
561 {
562         if (_session) {
563                 _session->cancel_audition();
564         }
565         return true;
566 }
567
568 bool
569 ARDOUR_UI::feedback_alert_press (GdkEventButton *)
570 {
571         return true;
572 }
573
574 bool
575 ARDOUR_UI::error_alert_press (GdkEventButton* ev)
576 {
577         bool do_toggle = true;
578         if (ev->button == 1) {
579                 if (_log_not_acknowledged == LogLevelError) {
580                         // just acknowledge the error, don't hide the log if it's already visible
581                         RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
582                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
583                         if (tact && tact->get_active()) {
584                                 do_toggle = false;
585                         }
586                 }
587                 _log_not_acknowledged = LogLevelNone;
588                 error_blink (false); // immediate acknowledge
589         }
590         // maybe fall through to to button toggle
591         return !do_toggle;
592 }
593
594 void
595 ARDOUR_UI::solo_blink (bool onoff)
596 {
597         if (_session == 0) {
598                 return;
599         }
600
601         if (_session->soloing() || _session->listening()) {
602                 if (onoff) {
603                         solo_alert_button.set_active (true);
604                 } else {
605                         solo_alert_button.set_active (false);
606                 }
607         } else {
608                 solo_alert_button.set_active (false);
609         }
610 }
611
612 void
613 ARDOUR_UI::sync_blink (bool onoff)
614 {
615         if (_session == 0 || !_session->config.get_external_sync()) {
616                 /* internal sync */
617                 sync_button.set_active (false);
618                 return;
619         }
620
621         if (!_session->transport_locked()) {
622                 /* not locked, so blink on and off according to the onoff argument */
623
624                 if (onoff) {
625                         sync_button.set_active (true);
626                 } else {
627                         sync_button.set_active (false);
628                 }
629         } else {
630                 /* locked */
631                 sync_button.set_active (true);
632         }
633 }
634
635 void
636 ARDOUR_UI::audition_blink (bool onoff)
637 {
638         if (_session == 0) {
639                 return;
640         }
641
642         if (_session->is_auditioning()) {
643                 if (onoff) {
644                         auditioning_alert_button.set_active (true);
645                 } else {
646                         auditioning_alert_button.set_active (false);
647                 }
648         } else {
649                 auditioning_alert_button.set_active (false);
650         }
651 }
652
653 void
654 ARDOUR_UI::feedback_blink (bool onoff)
655 {
656         if (_feedback_exists) {
657                 if (onoff) {
658                         feedback_alert_button.set_active (true);
659                 } else {
660                         feedback_alert_button.set_active (false);
661                 }
662         } else {
663                 feedback_alert_button.set_active (false);
664         }
665 }
666
667 void
668 ARDOUR_UI::error_blink (bool onoff)
669 {
670         switch (_log_not_acknowledged) {
671                 case LogLevelError:
672                         // blink
673                         if (onoff) {
674                                 error_alert_button.set_custom_led_color(0xff0000ff); // bright red
675                         } else {
676                                 error_alert_button.set_custom_led_color(0x880000ff); // dark red
677                         }
678                         break;
679                 case LogLevelWarning:
680                         error_alert_button.set_custom_led_color(0xccaa00ff); // yellow
681                         break;
682                 case LogLevelInfo:
683                         error_alert_button.set_custom_led_color(0x88cc00ff); // lime green
684                         break;
685                 default:
686                         error_alert_button.set_custom_led_color(0x333333ff); // gray
687                         break;
688         }
689 }
690
691
692
693 void
694 ARDOUR_UI::set_transport_sensitivity (bool yn)
695 {
696         ActionManager::set_sensitive (ActionManager::transport_sensitive_actions, yn);
697         shuttle_box->set_sensitive (yn);
698 }
699
700 void
701 ARDOUR_UI::editor_realized ()
702 {
703         boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
704         Config->map_parameters (pc);
705
706         UIConfiguration::instance().reset_dpi ();
707 }
708
709 void
710 ARDOUR_UI::update_tearoff_visibility ()
711 {
712         if (editor) {
713                 editor->update_tearoff_visibility ();
714         }
715 }
716
717 void
718 ARDOUR_UI::maximise_editing_space ()
719 {
720         if (editor) {
721                 editor->maximise_editing_space ();
722         }
723 }
724
725 void
726 ARDOUR_UI::restore_editing_space ()
727 {
728         if (editor) {
729                 editor->restore_editing_space ();
730         }
731 }
732
733 void
734 ARDOUR_UI::show_ui_prefs ()
735 {
736         tabs().set_current_page (2);
737         rc_option_editor->set_current_page (_("GUI"));
738 }
739
740
741 bool
742 ARDOUR_UI::click_button_clicked (GdkEventButton* ev)
743 {
744         if (ev->button != 3) {
745                 /* this handler is just for button-3 clicks */
746                 return false;
747         }
748
749         tabs().set_current_page (2);
750         rc_option_editor->set_current_page (_("Misc"));
751         return true;
752 }
753
754 void
755 ARDOUR_UI::toggle_follow_edits ()
756 {
757         RefPtr<Action> act = ActionManager::get_action (X_("Transport"), X_("ToggleFollowEdits"));
758         assert (act);
759
760         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic (act);
761         assert (tact);
762
763         UIConfiguration::instance().set_follow_edits (tact->get_active ());
764 }
765
766