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