2 Copyright (C) 2000 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.
20 /* This file contains any ARDOUR_UI methods that require knowledge of
21 the various dialog boxes, and exists so that no compilation dependency
22 exists between the main ARDOUR_UI modules and their respective classes.
23 This is to cut down on the compile times. It also helps with my sanity.
26 #include "ardour/audioengine.h"
27 #include "ardour/automation_watch.h"
28 #include "ardour/control_protocol_manager.h"
29 #include "ardour/profile.h"
30 #include "ardour/session.h"
31 #include "control_protocol/control_protocol.h"
34 #include "add_route_dialog.h"
35 #include "add_video_dialog.h"
36 #include "ardour_ui.h"
37 #include "big_clock_window.h"
38 #include "bundle_manager.h"
39 #include "global_port_matrix.h"
40 #include "gui_object.h"
41 #include "gui_thread.h"
42 #include "keyeditor.h"
43 #include "location_ui.h"
44 #include "main_clock.h"
45 #include "meter_patterns.h"
46 #include "midi_tracer.h"
48 #include "public_editor.h"
49 #include "rc_option_editor.h"
50 #include "route_params_ui.h"
51 #include "shuttle_control.h"
52 #include "session_option_editor.h"
53 #include "speaker_dialog.h"
56 #include "theme_manager.h"
57 #include "time_info_box.h"
60 #include <gtkmm2ext/keyboard.h>
64 using namespace ARDOUR;
68 using namespace Gtkmm2ext;
71 ARDOUR_UI::set_session (Session *s)
73 SessionHandlePtr::set_session (s);
76 WM::Manager::instance().set_session (s);
77 /* Session option editor cannot exist across change-of-session */
78 session_option_editor.drop_window ();
79 /* Ditto for AddVideoDialog */
80 add_video_dialog.drop_window ();
84 const XMLNode* node = _session->extra_xml (X_("UI"));
87 const XMLNodeList& children = node->children();
88 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
89 if ((*i)->name() == GUIObjectState::xml_node_name) {
90 gui_object_state->load (**i);
96 WM::Manager::instance().set_session (s);
98 AutomationWatch::instance().set_session (s);
101 shuttle_box->set_session (s);
104 primary_clock->set_session (s);
105 secondary_clock->set_session (s);
106 big_clock->set_session (s);
107 time_info_box->set_session (s);
108 video_timeline->set_session (s);
110 /* sensitize menu bar options that are now valid */
112 ActionManager::set_sensitive (ActionManager::session_sensitive_actions, true);
113 ActionManager::set_sensitive (ActionManager::write_sensitive_actions, _session->writable());
115 if (_session->locations()->num_range_markers()) {
116 ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
118 ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
121 if (!_session->monitor_out()) {
122 Glib::RefPtr<Action> act = ActionManager::get_action (X_("options"), X_("SoloViaBus"));
124 act->set_sensitive (false);
128 /* allow wastebasket flush again */
130 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Main"), X_("FlushWastebasket"));
132 act->set_sensitive (true);
135 /* there are never any selections on startup */
137 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
138 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, false);
139 ActionManager::set_sensitive (ActionManager::line_selection_sensitive_actions, false);
140 ActionManager::set_sensitive (ActionManager::point_selection_sensitive_actions, false);
141 ActionManager::set_sensitive (ActionManager::playlist_selection_sensitive_actions, false);
143 rec_button.set_sensitive (true);
145 solo_alert_button.set_active (_session->soloing());
147 setup_session_options ();
149 blink_connection = Timers::blink_connect (sigc::mem_fun(*this, &ARDOUR_UI::blink_handler));
151 _session->SaveSessionRequested.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::save_session_at_its_request, this, _1), gui_context());
152 _session->RecordStateChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::record_state_changed, this), gui_context());
153 _session->StepEditStatusChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::step_edit_status_change, this, _1), gui_context());
154 _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::map_transport_state, this), gui_context());
155 _session->DirtyChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_autosave, this), gui_context());
157 _session->Xrun.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::xrun_handler, this, _1), gui_context());
158 _session->SoloActive.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::soloing_changed, this, _1), gui_context());
159 _session->AuditionActive.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::auditioning_changed, this, _1), gui_context());
160 _session->locations()->added.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context());
161 _session->locations()->removed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context());
162 _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_parameter_changed, this, _1), gui_context ());
164 /* Clocks are on by default after we are connected to a session, so show that here.
167 connect_dependents_to_session (s);
169 /* listen to clock mode changes. don't do this earlier because otherwise as the clocks
170 restore their modes or are explicitly set, we will cause the "new" mode to be saved
171 back to the session XML ("Extra") state.
174 AudioClock::ModeChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::store_clock_modes));
176 Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::first_idle));
180 map_transport_state ();
182 second_connection = Timers::second_connect (sigc::mem_fun(*this, &ARDOUR_UI::every_second));
183 point_one_second_connection = Timers::rapid_connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_one_seconds));
184 point_zero_something_second_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_zero_something_seconds));
185 set_fps_timeout_connection();
189 if (meter_box.get_parent()) {
190 transport_tearoff_hbox.remove (meter_box);
191 transport_tearoff_hbox.remove (editor_meter_peak_display);
195 meter_box.remove(*editor_meter);
198 editor_meter_peak_display.hide();
201 if (meter_box.get_parent()) {
202 transport_tearoff_hbox.remove (meter_box);
203 transport_tearoff_hbox.remove (editor_meter_peak_display);
207 _session->master_out() &&
208 _session->master_out()->n_outputs().n(DataType::AUDIO) > 0) {
210 if (!ARDOUR::Profile->get_trx()) {
211 editor_meter = new LevelMeterHBox(_session);
212 editor_meter->set_meter (_session->master_out()->shared_peak_meter().get());
213 editor_meter->clear_meters();
214 editor_meter->set_type (_session->master_out()->meter_type());
215 editor_meter->setup_meters (30, 12, 6);
216 editor_meter->show();
217 meter_box.pack_start(*editor_meter);
220 ArdourMeter::ResetAllPeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_peak_display));
221 ArdourMeter::ResetRoutePeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_route_peak_display));
222 ArdourMeter::ResetGroupPeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_group_peak_display));
224 editor_meter_peak_display.set_name ("meterbridge peakindicator");
225 editor_meter_peak_display.unset_flags (Gtk::CAN_FOCUS);
226 editor_meter_peak_display.set_size_request (std::max(9.f, rintf(8.f * ui_scale)), -1);
227 editor_meter_peak_display.set_corner_radius (3.0);
229 editor_meter_max_peak = -INFINITY;
230 editor_meter_peak_display.signal_button_release_event().connect (sigc::mem_fun(*this, &ARDOUR_UI::editor_meter_peak_button_release), false);
232 if (ARDOUR_UI::config()->get_show_editor_meter() && !ARDOUR::Profile->get_trx()) {
233 transport_tearoff_hbox.pack_start (meter_box, false, false);
234 transport_tearoff_hbox.pack_start (editor_meter_peak_display, false, false);
236 editor_meter_peak_display.show();
242 ARDOUR_UI::unload_session (bool hide_stuff)
245 ARDOUR_UI::instance()->video_timeline->sync_session_state();
248 if (_session && _session->dirty()) {
249 std::vector<std::string> actions;
250 actions.push_back (_("Don't close"));
251 actions.push_back (_("Just close"));
252 actions.push_back (_("Save and close"));
253 switch (ask_about_saving_session (actions)) {
259 _session->save_state ("");
265 // tear down session specific CPI (owned by rc_config_editor which can remain)
266 ControlProtocolManager& m = ControlProtocolManager::instance ();
267 for (std::list<ControlProtocolInfo*>::iterator i = m.control_protocol_info.begin(); i != m.control_protocol_info.end(); ++i) {
268 if (*i && (*i)->protocol && (*i)->protocol->has_editor ()) {
269 (*i)->protocol->tear_down_gui ();
277 meterbridge->hide ();
278 audio_port_matrix->hide();
279 midi_port_matrix->hide();
280 route_params->hide();
283 second_connection.disconnect ();
284 point_one_second_connection.disconnect ();
285 point_zero_something_second_connection.disconnect();
286 fps_connection.disconnect();
289 meter_box.remove(*editor_meter);
292 editor_meter_peak_display.hide();
295 ActionManager::set_sensitive (ActionManager::session_sensitive_actions, false);
297 rec_button.set_sensitive (false);
299 WM::Manager::instance().set_session ((ARDOUR::Session*) 0);
301 if (ARDOUR_UI::instance()->video_timeline) {
302 ARDOUR_UI::instance()->video_timeline->close_session();
307 /* drop everything attached to the blink signal */
309 blink_connection.disconnect ();
314 session_loaded = false;
316 update_buffer_load ();
322 _hide_splash (gpointer arg)
324 ((ARDOUR_UI*)arg)->hide_splash();
329 ARDOUR_UI::goto_editor_window ()
331 if (splash && splash->is_visible()) {
332 // in 2 seconds, hide the splash screen
333 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 2000);
336 editor->show_window ();
338 /* mixer should now be on top */
339 if (ARDOUR_UI::config()->get_transients_follow_front()) {
340 WM::Manager::instance().set_transient_for (editor);
342 _mixer_on_top = false;
346 ARDOUR_UI::goto_mixer_window ()
348 Glib::RefPtr<Gdk::Window> win;
349 Glib::RefPtr<Gdk::Screen> screen;
352 win = editor->get_window ();
356 screen = win->get_screen();
358 screen = Gdk::Screen::get_default();
361 if (g_getenv ("ARDOUR_LOVES_STUPID_TINY_SCREENS") == 0 && screen && screen->get_height() < 700) {
362 Gtk::MessageDialog msg (_("This screen is not tall enough to display the mixer window"));
367 mixer->show_window ();
369 /* mixer should now be on top */
370 if (ARDOUR_UI::config()->get_transients_follow_front()) {
371 WM::Manager::instance().set_transient_for (mixer);
373 _mixer_on_top = true;
377 ARDOUR_UI::toggle_mixer_window ()
379 if (!editor || !mixer) {
380 /* can this really happen?
381 * keyboard shortcut during session close, maybe?
384 /* one way to find out: */
385 printf("ARDOUR_UI::toggle_mixer_window: Editor: %p Mixer: %p\n", editor, mixer);
386 PBD::stacktrace (std::cerr, 20);
393 bool obscuring = false;
395 if (mixer->not_visible ()) {
398 else if (editor->get_screen() == mixer->get_screen()) {
402 editor->get_position (ex, ey);
403 editor->get_size (ew, eh);
404 mixer->get_position (mx, my);
405 mixer->get_size (mw, mh);
421 if (gdk_rectangle_intersect (&e, &m, &r)) {
426 if (obscuring && editor->property_has_toplevel_focus()) {
431 goto_mixer_window ();
438 ARDOUR_UI::toggle_meterbridge ()
440 if (!editor || !meterbridge) {
441 /* can this really happen?
442 * keyboard shortcut during session close, maybe?
445 /* one way to find out: */
446 printf("ARDOUR_UI::toggle_meterbridge: Editor: %p MB: %p\n", editor, meterbridge);
447 PBD::stacktrace (std::cerr, 20);
454 bool obscuring = false;
456 if (meterbridge->not_visible ()) {
459 else if (editor->get_screen() == meterbridge->get_screen()) {
463 editor->get_position (ex, ey);
464 editor->get_size (ew, eh);
465 meterbridge->get_position (mx, my);
466 meterbridge->get_size (mw, mh);
482 if (gdk_rectangle_intersect (&e, &m, &r)) {
487 if (obscuring && editor->property_has_toplevel_focus()) {
492 meterbridge->show_window ();
493 meterbridge->present ();
494 meterbridge->raise ();
496 meterbridge->hide_window (NULL);
501 ARDOUR_UI::toggle_editor_mixer ()
503 bool obscuring = false;
504 /* currently, if windows are on different
505 screens then we do nothing; but in the
506 future we may want to bring the window
507 to the front or something, so I'm leaving this
508 variable for future use
510 bool same_screen = true;
512 if (editor && mixer) {
514 /* remeber: Screen != Monitor (Screen is a separately rendered
515 * continuous geometry that make include 1 or more monitors.
518 if (editor->get_screen() != mixer->get_screen() && (mixer->get_screen() != 0) && (editor->get_screen() != 0)) {
519 // different screens, so don't do anything
522 // they are on the same screen, see if they are obscuring each other
527 editor->get_position (ex, ey);
528 editor->get_size (ew, eh);
530 mixer->get_position (mx, my);
531 mixer->get_size (mw, mh);
547 if (gdk_rectangle_intersect (&e, &m, &r)) {
553 if (mixer && !mixer->not_visible() && mixer->property_has_toplevel_focus()) {
554 if (obscuring && same_screen) {
555 goto_editor_window();
557 } else if (editor && !editor->not_visible() && editor->property_has_toplevel_focus()) {
558 if (obscuring && same_screen) {
561 } else if (mixer && mixer->not_visible()) {
562 if (obscuring && same_screen) {
563 goto_mixer_window ();
565 } else if (editor && editor->not_visible()) {
566 if (obscuring && same_screen) {
567 goto_editor_window ();
569 } else if (obscuring && same_screen) {
570 //it's unclear what to do here, so just do the opposite of what we did last time (old behavior)
572 goto_editor_window ();
574 goto_mixer_window ();
580 ARDOUR_UI::new_midi_tracer_window ()
582 RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("NewMIDITracer"));
587 std::list<MidiTracer*>::iterator i = _midi_tracer_windows.begin ();
588 while (i != _midi_tracer_windows.end() && (*i)->get_visible() == true) {
592 if (i == _midi_tracer_windows.end()) {
593 /* all our MIDITracer windows are visible; make a new one */
594 MidiTracer* t = new MidiTracer ();
596 _midi_tracer_windows.push_back (t);
598 /* re-use the hidden one */
604 ARDOUR_UI::create_bundle_manager ()
606 return new BundleManager (_session);
610 ARDOUR_UI::create_add_video_dialog ()
612 return new AddVideoDialog (_session);
616 ARDOUR_UI::create_session_option_editor ()
618 return new SessionOptionEditor (_session);
622 ARDOUR_UI::create_big_clock_window ()
624 return new BigClockWindow (*big_clock);
628 ARDOUR_UI::handle_locations_change (Location *)
631 if (_session->locations()->num_range_markers()) {
632 ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
634 ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
640 ARDOUR_UI::main_window_state_event_handler (GdkEventWindowState* ev, bool window_was_editor)
642 if (window_was_editor) {
644 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
645 (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
646 if (big_clock_window) {
647 big_clock_window->set_transient_for (*editor);
653 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
654 (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
655 if (big_clock_window) {
656 big_clock_window->set_transient_for (*mixer);
665 ARDOUR_UI::editor_meter_peak_button_release (GdkEventButton* ev)
667 if (ev->button == 1 && Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::PrimaryModifier|Gtkmm2ext::Keyboard::TertiaryModifier)) {
668 ArdourMeter::ResetAllPeakDisplays ();
669 } else if (ev->button == 1 && Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::PrimaryModifier)) {
670 if (_session->master_out()) {
671 ArdourMeter::ResetGroupPeakDisplays (_session->master_out()->route_group());
673 } else if (_session->master_out()) {
674 ArdourMeter::ResetRoutePeakDisplays (_session->master_out().get());
680 ARDOUR_UI::toggle_mixer_space()
682 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMaximalMixer");
685 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
686 if (tact->get_active()) {
687 mixer->maximise_mixer_space ();
689 mixer->restore_mixer_space ();