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