2 Copyright (C) 1999 Paul Davis
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.
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.
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.
21 #include "gtk2ardour-config.h"
31 #include <sigc++/bind.h>
32 #include "pbd/error.h"
33 #include "pbd/basename.h"
34 #include "pbd/fastlog.h"
36 #include "gtkmm2ext/cairocell.h"
37 #include "gtkmm2ext/utils.h"
38 #include "gtkmm2ext/click_box.h"
39 #include "gtkmm2ext/window_title.h"
41 #include "ardour/profile.h"
42 #include "ardour/session.h"
43 #include "ardour/types.h"
45 #include "ardour_ui.h"
47 #include "public_editor.h"
48 #include "audio_clock.h"
50 #include "main_clock.h"
53 #include "theme_manager.h"
54 #include "midi_tracer.h"
55 #include "shuttle_control.h"
56 #include "global_port_matrix.h"
57 #include "location_ui.h"
58 #include "rc_option_editor.h"
59 #include "time_info_box.h"
64 using namespace ARDOUR;
66 using namespace Gtkmm2ext;
69 using namespace ARDOUR_UI_UTILS;
73 ARDOUR_UI::setup_tooltips ()
75 set_tip (roll_button, _("Play from playhead"));
76 set_tip (stop_button, _("Stop playback"));
77 set_tip (rec_button, _("Toggle record"));
78 set_tip (play_selection_button, _("Play range/selection"));
79 set_tip (goto_start_button, _("Go to start of session"));
80 set_tip (goto_end_button, _("Go to end of session"));
81 set_tip (auto_loop_button, _("Play loop range"));
82 set_tip (midi_panic_button, _("MIDI Panic\nSend note off and reset controller messages on all MIDI channels"));
83 set_tip (auto_return_button, _("Return to last playback start when stopped"));
84 set_tip (follow_edits_button, _("Playhead follows range selections and edits"));
85 set_tip (auto_input_button, _("Be sensible about input monitoring"));
86 set_tip (click_button, _("Enable/Disable audio click"));
87 set_tip (solo_alert_button, _("When active, something is soloed.\nClick to de-solo everything"));
88 set_tip (auditioning_alert_button, _("When active, auditioning is taking place.\nClick to stop the audition"));
89 set_tip (feedback_alert_button, _("When active, there is a feedback loop."));
90 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"));
91 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"));
92 set_tip (editor_meter_peak_display, _("Reset All Peak Indicators"));
93 set_tip (error_alert_button, _("Show Error Log and acknowledge warnings"));
95 synchronize_sync_source_and_video_pullup ();
97 editor->setup_tooltips ();
101 ARDOUR_UI::status_bar_button_press (GdkEventButton* ev)
103 bool handled = false;
105 switch (ev->button) {
107 status_bar_label.set_text ("");
118 ARDOUR_UI::display_message (const char *prefix, gint prefix_len, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg)
122 UI::display_message (prefix, prefix_len, ptag, mtag, msg);
124 ArdourLogLevel ll = LogLevelNone;
126 if (strcmp (prefix, _("[ERROR]: ")) == 0) {
127 text = "<span color=\"red\" weight=\"bold\">";
129 } else if (strcmp (prefix, _("[WARNING]: ")) == 0) {
130 text = "<span color=\"yellow\" weight=\"bold\">";
131 ll = LogLevelWarning;
132 } else if (strcmp (prefix, _("[INFO]: ")) == 0) {
133 text = "<span color=\"green\" weight=\"bold\">";
136 text = "<span color=\"white\" weight=\"bold\">???";
139 _log_not_acknowledged = std::max(_log_not_acknowledged, ll);
146 status_bar_label.set_markup (text);
151 ARDOUR_UI::tearoff_settings (const char* name) const
153 XMLNode* ui_node = Config->extra_xml(X_("UI"));
156 XMLNode* tearoff_node = ui_node->child (X_("Tearoffs"));
158 XMLNode* mnode = tearoff_node->child (name);
166 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
169 bool drag_failed (const Glib::RefPtr<Gdk::DragContext>& context, DragResult result, Tabbable* tab)
171 if (result == Gtk::DRAG_RESULT_NO_TARGET) {
179 ARDOUR_UI::setup_transport ()
183 transport_hbox.set_border_width (PX_SCALE(3));
184 transport_hbox.set_spacing (PX_SCALE(3));
186 transport_base.set_name ("TransportBase");
187 transport_base.add (transport_hbox);
189 transport_frame.set_shadow_type (SHADOW_OUT);
190 transport_frame.set_name ("BaseFrame");
191 transport_frame.add (transport_base);
193 auto_return_button.set_text(_("Auto Return"));
195 follow_edits_button.set_text(_("Follow Edits"));
197 // auto_input_button.set_text (_("Auto Input"));
199 click_button.set_icon (ArdourIcon::TransportMetronom);
201 act = ActionManager::get_action ("Transport", "ToggleClick");
202 click_button.set_related_action (act);
203 click_button.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::click_button_clicked), false);
205 auto_return_button.set_name ("transport option button");
206 follow_edits_button.set_name ("transport option button");
207 auto_input_button.set_name ("transport option button");
209 /* these have to provide a clear indication of active state */
211 click_button.set_name ("transport button");
212 sync_button.set_name ("transport active option button");
214 stop_button.set_active (true);
216 goto_start_button.set_icon (ArdourIcon::TransportStart);
217 goto_end_button.set_icon (ArdourIcon::TransportEnd);
218 roll_button.set_icon (ArdourIcon::TransportPlay);
219 stop_button.set_icon (ArdourIcon::TransportStop);
220 play_selection_button.set_icon (ArdourIcon::TransportRange);
221 auto_loop_button.set_icon (ArdourIcon::TransportLoop);
222 rec_button.set_icon (ArdourIcon::RecButton);
223 midi_panic_button.set_icon (ArdourIcon::TransportPanic);
225 act = ActionManager::get_action (X_("Transport"), X_("Stop"));
226 stop_button.set_related_action (act);
227 act = ActionManager::get_action (X_("Transport"), X_("Roll"));
228 roll_button.set_related_action (act);
229 act = ActionManager::get_action (X_("Transport"), X_("Record"));
230 rec_button.set_related_action (act);
231 act = ActionManager::get_action (X_("Transport"), X_("GotoStart"));
232 goto_start_button.set_related_action (act);
233 act = ActionManager::get_action (X_("Transport"), X_("GotoEnd"));
234 goto_end_button.set_related_action (act);
235 act = ActionManager::get_action (X_("Transport"), X_("Loop"));
236 auto_loop_button.set_related_action (act);
237 act = ActionManager::get_action (X_("Transport"), X_("PlaySelection"));
238 play_selection_button.set_related_action (act);
239 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
240 midi_panic_button.set_related_action (act);
241 act = ActionManager::get_action (X_("Transport"), X_("ToggleExternalSync"));
242 sync_button.set_related_action (act);
246 ARDOUR_UI::Clock.connect (sigc::mem_fun (primary_clock, &AudioClock::set));
247 ARDOUR_UI::Clock.connect (sigc::mem_fun (secondary_clock, &AudioClock::set));
249 primary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::primary_clock_value_changed));
250 secondary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::secondary_clock_value_changed));
251 big_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::big_clock_value_changed));
253 act = ActionManager::get_action ("Transport", "ToggleAutoReturn");
254 auto_return_button.set_related_action (act);
255 act = ActionManager::get_action (X_("Transport"), X_("ToggleFollowEdits"));
256 follow_edits_button.set_related_action (act);
257 act = ActionManager::get_action ("Transport", "ToggleAutoInput");
258 auto_input_button.set_related_action (act);
262 /* CANNOT sigc::bind these to clicked or toggled, must use pressed or released */
264 solo_alert_button.set_name ("rude solo");
265 act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
266 solo_alert_button.set_related_action (act);
267 auditioning_alert_button.set_name ("rude audition");
268 auditioning_alert_button.signal_button_press_event().connect (sigc::mem_fun(*this,&ARDOUR_UI::audition_alert_press), false);
269 feedback_alert_button.set_name ("feedback alert");
270 feedback_alert_button.signal_button_press_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::feedback_alert_press), false);
271 error_alert_button.set_name ("error alert");
272 error_alert_button.signal_button_release_event().connect (sigc::mem_fun(*this,&ARDOUR_UI::error_alert_press), false);
273 act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
274 error_alert_button.set_related_action(act);
275 error_alert_button.set_fallthrough_to_parent(true);
277 alert_box.set_homogeneous (true);
278 alert_box.set_spacing (PX_SCALE(2));
279 alert_box.pack_start (solo_alert_button, true, true);
280 alert_box.pack_start (auditioning_alert_button, true, true);
281 alert_box.pack_start (feedback_alert_button, true, true);
283 /* all transport buttons should be the same size vertically and
287 Glib::RefPtr<SizeGroup> transport_button_size_group = SizeGroup::create (SIZE_GROUP_BOTH);
288 transport_button_size_group->add_widget (goto_start_button);
289 transport_button_size_group->add_widget (goto_end_button);
290 transport_button_size_group->add_widget (auto_loop_button);
291 transport_button_size_group->add_widget (rec_button);
292 transport_button_size_group->add_widget (play_selection_button);
293 transport_button_size_group->add_widget (roll_button);
294 transport_button_size_group->add_widget (stop_button);
296 /* the icon for this has an odd aspect ratio, so fatten up the button */
297 midi_panic_button.set_size_request (PX_SCALE(25), -1);
298 goto_start_button.set_size_request (PX_SCALE(28), PX_SCALE(44));
299 click_button.set_size_request (PX_SCALE(32), PX_SCALE(44));
302 HBox* tbox1 = manage (new HBox);
303 HBox* tbox2 = manage (new HBox);
304 HBox* tbox = manage (new HBox);
306 VBox* vbox1 = manage (new VBox);
307 VBox* vbox2 = manage (new VBox);
309 Alignment* a1 = manage (new Alignment);
310 Alignment* a2 = manage (new Alignment);
312 tbox1->set_spacing (PX_SCALE(2));
313 tbox2->set_spacing (PX_SCALE(2));
314 tbox->set_spacing (PX_SCALE(2));
316 if (!Profile->get_trx()) {
317 tbox1->pack_start (midi_panic_button, true, true, 5);
318 tbox1->pack_start (click_button, true, true, 5);
321 tbox1->pack_start (goto_start_button, true, true);
322 tbox1->pack_start (goto_end_button, true, true);
323 tbox1->pack_start (auto_loop_button, true, true);
325 if (!Profile->get_trx()) {
326 tbox2->pack_start (play_selection_button, true, true);
328 tbox2->pack_start (roll_button, true, true);
329 tbox2->pack_start (stop_button, true, true);
330 tbox2->pack_start (rec_button, true, true, 5);
332 vbox1->pack_start (*tbox1, true, true);
333 vbox2->pack_start (*tbox2, true, true);
336 a1->set (0.5, 0.5, 0.0, 1.0);
338 a2->set (0.5, 0.5, 0.0, 1.0);
340 tbox->pack_start (*a1, false, false);
341 tbox->pack_start (*a2, false, false);
343 HBox* clock_box = manage (new HBox);
345 clock_box->pack_start (*primary_clock, false, false);
346 if (!ARDOUR::Profile->get_small_screen() && !ARDOUR::Profile->get_trx()) {
347 clock_box->pack_start (*secondary_clock, false, false);
349 clock_box->set_spacing (PX_SCALE(3));
351 shuttle_box = manage (new ShuttleControl);
352 shuttle_box->show ();
354 VBox* transport_vbox = manage (new VBox);
355 transport_vbox->set_name ("TransportBase");
356 transport_vbox->set_border_width (0);
357 transport_vbox->set_spacing (PX_SCALE(3));
358 transport_vbox->pack_start (*tbox, true, true, 0);
360 if (!Profile->get_trx()) {
361 transport_vbox->pack_start (*shuttle_box, false, false, 0);
364 time_info_box = manage (new TimeInfoBox);
366 transport_hbox.pack_start (*transport_vbox, false, true);
368 /* transport related toggle controls */
370 VBox* auto_box = manage (new VBox);
371 auto_box->set_homogeneous (true);
372 auto_box->set_spacing (PX_SCALE(2));
373 auto_box->pack_start (sync_button, true, true);
374 if (!ARDOUR::Profile->get_trx()) {
375 auto_box->pack_start (follow_edits_button, true, true);
376 auto_box->pack_start (auto_return_button, true, true);
379 if (!ARDOUR::Profile->get_trx()) {
380 transport_hbox.pack_start (*auto_box, false, false);
382 transport_hbox.pack_start (*clock_box, true, true);
384 if (ARDOUR::Profile->get_trx()) {
385 transport_hbox.pack_start (*auto_box, false, false);
388 if (!ARDOUR::Profile->get_trx()) {
389 transport_hbox.pack_start (*time_info_box, false, false);
392 if (!ARDOUR::Profile->get_trx()) {
393 transport_hbox.pack_start (alert_box, false, false);
394 transport_hbox.pack_start (meter_box, false, false);
395 transport_hbox.pack_start (editor_meter_peak_display, false, false);
398 Gtk::VBox* window_button_box = manage (new Gtk::VBox);
400 editor_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), editor));
401 mixer_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), mixer));
402 prefs_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), rc_option_editor));
404 /* catch context clicks so that we can show a menu on these buttons */
406 editor_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("editor")), false);
407 mixer_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("mixer")), false);
408 prefs_visibility_button.signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbable_visibility_button_press), X_("preferences")), false);
410 editor_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-editor-visibility")));
411 editor_visibility_button.set_name (X_("page switch button"));
412 mixer_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-mixer-visibility")));
413 mixer_visibility_button.set_name (X_("page switch button"));
414 prefs_visibility_button.set_related_action (ActionManager::get_action (X_("Common"), X_("change-preferences-visibility")));
415 prefs_visibility_button.set_name (X_("page switch button"));
417 Gtkmm2ext::UI::instance()->set_tip (editor_visibility_button,
418 string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
419 "To put the window back, use the Window > %1 > Attach menu action"), editor->name()));
421 Gtkmm2ext::UI::instance()->set_tip (mixer_visibility_button,
422 string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
423 "To put the window back, use the Window > %1 > Attach menu action"), mixer->name()));
425 Gtkmm2ext::UI::instance()->set_tip (prefs_visibility_button,
426 string_compose (_("Drag this tab to the desktop to show %1 in its own window\n\n"
427 "To put the window back, use the Window > %1 > Attach menu action"), rc_option_editor->name()));
429 window_button_box->pack_start (editor_visibility_button, true, false);
430 window_button_box->pack_start (mixer_visibility_button, true, false);
431 window_button_box->pack_start (prefs_visibility_button, true, false);
433 transport_hbox.pack_end (*window_button_box, false, false);
437 set_transport_sensitivity (false);
442 ARDOUR_UI::soloing_changed (bool onoff)
444 if (solo_alert_button.get_active() != onoff) {
445 solo_alert_button.set_active (onoff);
450 ARDOUR_UI::_auditioning_changed (bool onoff)
452 auditioning_alert_button.set_active (onoff);
453 set_transport_sensitivity (!onoff);
457 ARDOUR_UI::auditioning_changed (bool onoff)
459 UI::instance()->call_slot (MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::_auditioning_changed, this, onoff));
463 ARDOUR_UI::audition_alert_press (GdkEventButton*)
466 _session->cancel_audition();
472 ARDOUR_UI::feedback_alert_press (GdkEventButton *)
478 ARDOUR_UI::error_alert_press (GdkEventButton* ev)
480 bool do_toggle = true;
481 if (ev->button == 1) {
482 if (_log_not_acknowledged == LogLevelError) {
483 // just acknowledge the error, don't hide the log if it's already visible
484 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-log-window"));
485 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
486 if (tact && tact->get_active()) {
490 _log_not_acknowledged = LogLevelNone;
491 error_blink (false); // immediate acknowledge
493 // maybe fall through to to button toggle
498 ARDOUR_UI::solo_blink (bool onoff)
504 if (_session->soloing() || _session->listening()) {
506 solo_alert_button.set_active (true);
508 solo_alert_button.set_active (false);
511 solo_alert_button.set_active (false);
516 ARDOUR_UI::sync_blink (bool onoff)
518 if (_session == 0 || !_session->config.get_external_sync()) {
520 sync_button.set_active (false);
524 if (!_session->transport_locked()) {
525 /* not locked, so blink on and off according to the onoff argument */
528 sync_button.set_active (true);
530 sync_button.set_active (false);
534 sync_button.set_active (true);
539 ARDOUR_UI::audition_blink (bool onoff)
545 if (_session->is_auditioning()) {
547 auditioning_alert_button.set_active (true);
549 auditioning_alert_button.set_active (false);
552 auditioning_alert_button.set_active (false);
557 ARDOUR_UI::feedback_blink (bool onoff)
559 if (_feedback_exists) {
561 feedback_alert_button.set_active (true);
563 feedback_alert_button.set_active (false);
566 feedback_alert_button.set_active (false);
571 ARDOUR_UI::error_blink (bool onoff)
573 switch (_log_not_acknowledged) {
577 error_alert_button.set_custom_led_color(0xff0000ff); // bright red
579 error_alert_button.set_custom_led_color(0x880000ff); // dark red
582 case LogLevelWarning:
583 error_alert_button.set_custom_led_color(0xccaa00ff); // yellow
586 error_alert_button.set_custom_led_color(0x88cc00ff); // lime green
589 error_alert_button.set_custom_led_color(0x333333ff); // gray
597 ARDOUR_UI::set_transport_sensitivity (bool yn)
599 ActionManager::set_sensitive (ActionManager::transport_sensitive_actions, yn);
600 shuttle_box->set_sensitive (yn);
604 ARDOUR_UI::editor_realized ()
606 boost::function<void (string)> pc (boost::bind (&ARDOUR_UI::parameter_changed, this, _1));
607 Config->map_parameters (pc);
609 UIConfiguration::instance().reset_dpi ();
613 ARDOUR_UI::maximise_editing_space ()
616 editor->maximise_editing_space ();
621 ARDOUR_UI::restore_editing_space ()
624 editor->restore_editing_space ();
629 ARDOUR_UI::show_ui_prefs ()
631 if (rc_option_editor) {
632 show_tabbable (rc_option_editor);
633 rc_option_editor->set_current_page (_("GUI"));
638 ARDOUR_UI::click_button_clicked (GdkEventButton* ev)
640 if (ev->button != 3) {
641 /* this handler is just for button-3 clicks */
645 show_tabbable (rc_option_editor);
646 rc_option_editor->set_current_page (_("Misc"));
651 ARDOUR_UI::toggle_follow_edits ()
653 RefPtr<Action> act = ActionManager::get_action (X_("Transport"), X_("ToggleFollowEdits"));
656 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic (act);
659 UIConfiguration::instance().set_follow_edits (tact->get_active ());
663 ARDOUR_UI::update_title ()
666 bool dirty = _session->dirty();
670 if (_session->snap_name() != _session->name()) {
671 session_name = _session->snap_name();
673 session_name = _session->name();
677 session_name = "*" + session_name;
680 WindowTitle title (session_name);
681 title += Glib::get_application_name();
682 _main_window.set_title (title.get_string());
684 WindowTitle title (Glib::get_application_name());
685 _main_window.set_title (title.get_string());