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