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