4ed563e2017bb650b329d7c31809a16167b9f1e9
[ardour.git] / gtk2_ardour / ardour_ui_dialogs.cc
1 /*
2     Copyright (C) 2000 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 /* 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.
24 */
25
26 #include "ardour/session.h"
27 #include "ardour/audioengine.h"
28 #include "ardour/automation_watch.h"
29
30 #include "actions.h"
31 #include "add_route_dialog.h"
32 #include "add_video_dialog.h"
33 #include "ardour_ui.h"
34 #include "big_clock_window.h"
35 #include "bundle_manager.h"
36 #include "global_port_matrix.h"
37 #include "gui_object.h"
38 #include "gui_thread.h"
39 #include "keyeditor.h"
40 #include "location_ui.h"
41 #include "main_clock.h"
42 #include "midi_tracer.h"
43 #include "mixer_ui.h"
44 #include "public_editor.h"
45 #include "rc_option_editor.h"
46 #include "route_params_ui.h"
47 #include "shuttle_control.h"
48 #include "session_option_editor.h"
49 #include "speaker_dialog.h"
50 #include "splash.h"
51 #include "sfdb_ui.h"
52 #include "theme_manager.h"
53 #include "time_info_box.h"
54
55 #include "i18n.h"
56
57 using namespace ARDOUR;
58 using namespace PBD;
59 using namespace Glib;
60 using namespace Gtk;
61 using namespace Gtkmm2ext;
62
63 void
64 ARDOUR_UI::set_session (Session *s)
65 {
66         SessionHandlePtr::set_session (s);
67
68         WM::Manager::instance().set_session (s);
69
70         if (!_session) {
71                 /* Session option editor cannot exist across change-of-session */
72                 session_option_editor.drop_window ();
73                 /* Ditto for AddVideoDialog */
74                 add_video_dialog.drop_window ();
75                 return;
76         }
77
78         const XMLNode* node = _session->extra_xml (X_("UI"));
79
80         if (node) {
81                 const XMLNodeList& children = node->children();
82                 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
83                         if ((*i)->name() == GUIObjectState::xml_node_name) {
84                                 gui_object_state->load (**i);
85                                 break;
86                         }
87                 }
88         }
89
90         AutomationWatch::instance().set_session (s);
91
92         if (shuttle_box) {
93                 shuttle_box->set_session (s);
94         }
95
96         primary_clock->set_session (s);
97         secondary_clock->set_session (s);
98         big_clock->set_session (s);
99         time_info_box->set_session (s);
100         video_timeline->set_session (s);
101
102         /* sensitize menu bar options that are now valid */
103
104         ActionManager::set_sensitive (ActionManager::session_sensitive_actions, true);
105         ActionManager::set_sensitive (ActionManager::write_sensitive_actions, _session->writable());
106
107         if (_session->locations()->num_range_markers()) {
108                 ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
109         } else {
110                 ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
111         }
112
113         if (!_session->monitor_out()) {
114                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("options"), X_("SoloViaBus"));
115                 if (act) {
116                         act->set_sensitive (false);
117                 }
118         }
119
120         /* allow wastebasket flush again */
121
122         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Main"), X_("FlushWastebasket"));
123         if (act) {
124                 act->set_sensitive (true);
125         }
126
127         /* there are never any selections on startup */
128
129         ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
130         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, false);
131         ActionManager::set_sensitive (ActionManager::line_selection_sensitive_actions, false);
132         ActionManager::set_sensitive (ActionManager::point_selection_sensitive_actions, false);
133         ActionManager::set_sensitive (ActionManager::playlist_selection_sensitive_actions, false);
134
135         rec_button.set_sensitive (true);
136
137         solo_alert_button.set_active (_session->soloing());
138
139         setup_session_options ();
140
141         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::transport_rec_enable_blink));
142         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::solo_blink));
143         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::sync_blink));
144         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::audition_blink));
145         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::feedback_blink));
146
147         _session->RecordStateChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::record_state_changed, this), gui_context());
148         _session->StepEditStatusChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::step_edit_status_change, this, _1), gui_context());
149         _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::map_transport_state, this), gui_context());
150         _session->DirtyChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_autosave, this), gui_context());
151
152         _session->Xrun.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::xrun_handler, this, _1), gui_context());
153         _session->SoloActive.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::soloing_changed, this, _1), gui_context());
154         _session->AuditionActive.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::auditioning_changed, this, _1), gui_context());
155         _session->locations()->added.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context());
156         _session->locations()->removed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context());
157         _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_parameter_changed, this, _1), gui_context ());
158
159 #ifdef HAVE_JACK_SESSION
160         engine->JackSessionEvent.connect (*_session, MISSING_INVALIDATOR, boost::bind (&Session::jack_session_event, _session, _1), gui_context());
161 #endif
162
163         /* Clocks are on by default after we are connected to a session, so show that here.
164         */
165
166         connect_dependents_to_session (s);
167
168         /* listen to clock mode changes. don't do this earlier because otherwise as the clocks
169            restore their modes or are explicitly set, we will cause the "new" mode to be saved
170            back to the session XML ("Extra") state.
171          */
172
173         AudioClock::ModeChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::store_clock_modes));
174
175         Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::first_idle));
176
177         start_clocking ();
178         start_blinking ();
179
180         map_transport_state ();
181
182         second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_second), 1000);
183         point_one_second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_one_seconds), 100);
184         point_zero_one_second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_zero_one_seconds), 40);
185
186         update_format ();
187 }
188
189 int
190 ARDOUR_UI::unload_session (bool hide_stuff)
191 {
192         if (_session) {
193                 ARDOUR_UI::instance()->video_timeline->sync_session_state();
194         }
195
196         if (_session && _session->dirty()) {
197                 std::vector<std::string> actions;
198                 actions.push_back (_("Don't close"));
199                 actions.push_back (_("Just close"));
200                 actions.push_back (_("Save and close"));
201                 switch (ask_about_saving_session (actions)) {
202                 case -1:
203                         // cancel
204                         return 1;
205
206                 case 1:
207                         _session->save_state ("");
208                         break;
209                 }
210         }
211
212         if (hide_stuff) {
213                 editor->hide ();
214                 mixer->hide ();
215                 theme_manager->hide ();
216                 audio_port_matrix->hide();
217                 midi_port_matrix->hide();
218                 route_params->hide();
219         }
220
221         second_connection.disconnect ();
222         point_one_second_connection.disconnect ();
223         point_oh_five_second_connection.disconnect ();
224         point_zero_one_second_connection.disconnect();
225
226         ActionManager::set_sensitive (ActionManager::session_sensitive_actions, false);
227
228         rec_button.set_sensitive (false);
229
230         ARDOUR_UI::instance()->video_timeline->close_session();
231
232         stop_blinking ();
233         stop_clocking ();
234
235         /* drop everything attached to the blink signal */
236
237         Blink.clear ();
238
239         delete _session;
240         _session = 0;
241
242         session_loaded = false;
243
244         update_buffer_load ();
245
246         return 0;
247 }
248
249 static bool
250 _hide_splash (gpointer arg)
251 {
252         ((ARDOUR_UI*)arg)->hide_splash();
253         return false;
254 }
255
256 void
257 ARDOUR_UI::goto_editor_window ()
258 {
259         if (splash && splash->is_visible()) {
260                 // in 2 seconds, hide the splash screen
261                 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 2000);
262         }
263
264         editor->show_window ();
265         editor->present ();
266         /* mixer should now be on top */
267         WM::Manager::instance().set_transient_for (editor);
268         _mixer_on_top = false;
269 }
270
271 void
272 ARDOUR_UI::goto_mixer_window ()
273 {
274         Glib::RefPtr<Gdk::Window> win;
275         Glib::RefPtr<Gdk::Screen> screen;
276         
277         if (editor) {
278                 win = editor->get_window ();
279         }
280
281         if (win) {
282                 screen = win->get_screen();
283         } else {
284                 screen = Gdk::Screen::get_default();
285         }
286         
287         if (screen && screen->get_height() < 700) {
288                 Gtk::MessageDialog msg (_("This screen is not tall enough to display the mixer window"));
289                 msg.run ();
290                 return;
291         }
292
293         mixer->show_window ();
294         mixer->present ();
295         /* mixer should now be on top */
296         WM::Manager::instance().set_transient_for (mixer);
297         _mixer_on_top = true;
298 }
299
300 void
301 ARDOUR_UI::toggle_mixer_window ()
302 {
303         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-mixer"));
304         if (!act) {
305                 return;
306         }
307
308         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
309
310         if (tact->get_active()) {
311                 goto_mixer_window ();
312         } else {
313                 mixer->hide ();
314         }
315 }
316
317 void
318 ARDOUR_UI::toggle_meterbridge ()
319 {
320         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-meterbridge"));
321         if (!act) {
322                 return;
323         }
324
325         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
326
327         if (tact->get_active()) {
328                 meterbridge->show_window ();
329         } else {
330                 meterbridge->hide ();
331         }
332 }
333
334 void
335 ARDOUR_UI::toggle_editor_mixer ()
336 {
337         bool obscuring = false;
338         /* currently, if windows are on different
339            screens then we do nothing; but in the
340            future we may want to bring the window 
341            to the front or something, so I'm leaving this 
342            variable for future use
343         */
344         bool same_screen = true; 
345         
346         if (editor && mixer) {
347
348                 /* remeber: Screen != Monitor (Screen is a separately rendered
349                  * continuous geometry that make include 1 or more monitors.
350                  */
351                 
352                 if (editor->get_screen() != mixer->get_screen() && (mixer->get_screen() != 0) && (editor->get_screen() != 0)) {
353                         // different screens, so don't do anything
354                         same_screen = false;
355                 } else {
356                         // they are on the same screen, see if they are obscuring each other
357
358                         gint ex, ey, ew, eh;
359                         gint mx, my, mw, mh;
360
361                         editor->get_position (ex, ey);
362                         editor->get_size (ew, eh);
363
364                         mixer->get_position (mx, my);
365                         mixer->get_size (mw, mh);
366
367                         GdkRectangle e;
368                         GdkRectangle m;
369                         GdkRectangle r;
370
371                         e.x = ex;
372                         e.y = ey;
373                         e.width = ew;
374                         e.height = eh;
375
376                         m.x = mx;
377                         m.y = my;
378                         m.width = mw;
379                         m.height = mh;
380
381                         if (gdk_rectangle_intersect (&e, &m, &r)) {
382                                 obscuring = true;
383                         }
384                 }
385         }
386
387         if (mixer && !mixer->not_visible() && mixer->property_has_toplevel_focus()) {
388                 if (obscuring && same_screen) {
389                         goto_editor_window();
390                 }
391         } else if (editor && !editor->not_visible() && editor->property_has_toplevel_focus()) {
392                 if (obscuring && same_screen) {
393                         goto_mixer_window();
394                 }
395         } else if (mixer && mixer->not_visible()) {
396                 if (obscuring && same_screen) {
397                         goto_mixer_window ();
398                 }
399         } else if (editor && editor->not_visible()) {
400                 if (obscuring && same_screen) {
401                         goto_editor_window ();
402                 }
403         } else if (obscuring && same_screen) {
404                 //it's unclear what to do here, so just do the opposite of what we did last time  (old behavior)
405                 if (_mixer_on_top) {
406                         goto_editor_window ();
407                 } else {
408                         goto_mixer_window ();
409                 }
410         }
411 }
412
413 void
414 ARDOUR_UI::new_midi_tracer_window ()
415 {
416         RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("NewMIDITracer"));
417         if (!act) {
418                 return;
419         }
420
421         std::list<MidiTracer*>::iterator i = _midi_tracer_windows.begin ();
422         while (i != _midi_tracer_windows.end() && (*i)->get_visible() == true) {
423                 ++i;
424         }
425
426         if (i == _midi_tracer_windows.end()) {
427                 /* all our MIDITracer windows are visible; make a new one */
428                 MidiTracer* t = new MidiTracer ();
429                 t->show_all ();
430                 _midi_tracer_windows.push_back (t);
431         } else {
432                 /* re-use the hidden one */
433                 (*i)->show_all ();
434         }
435 }
436
437 BundleManager*
438 ARDOUR_UI::create_bundle_manager ()
439 {
440         return new BundleManager (_session);
441 }
442
443 AddVideoDialog*
444 ARDOUR_UI::create_add_video_dialog ()
445 {
446         return new AddVideoDialog (_session);
447 }
448
449 SessionOptionEditor*
450 ARDOUR_UI::create_session_option_editor ()
451 {
452         return new SessionOptionEditor (_session);
453 }
454
455 BigClockWindow*
456 ARDOUR_UI::create_big_clock_window ()
457 {
458         return new BigClockWindow (*big_clock);
459 }
460
461 void
462 ARDOUR_UI::handle_locations_change (Location *)
463 {
464         if (_session) {
465                 if (_session->locations()->num_range_markers()) {
466                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
467                 } else {
468                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
469                 }
470         }
471 }
472
473 bool
474 ARDOUR_UI::main_window_state_event_handler (GdkEventWindowState* ev, bool window_was_editor)
475 {
476         if (window_was_editor) {
477
478                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
479                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
480                         if (big_clock_window) {
481                                 big_clock_window->set_transient_for (*editor);
482                         }
483                 }
484
485         } else {
486
487                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
488                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
489                         if (big_clock_window) {
490                                 big_clock_window->set_transient_for (*mixer);
491                         }
492                 }
493         }
494
495         return false;
496 }