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