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