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 #ifdef HAVE_JACK_SESSION
169         engine->JackSessionEvent.connect (*_session, MISSING_INVALIDATOR, boost::bind (&Session::jack_session_event, _session, _1), gui_context());
170 #endif
171
172         /* Clocks are on by default after we are connected to a session, so show that here.
173         */
174
175         connect_dependents_to_session (s);
176
177         /* listen to clock mode changes. don't do this earlier because otherwise as the clocks
178            restore their modes or are explicitly set, we will cause the "new" mode to be saved
179            back to the session XML ("Extra") state.
180          */
181
182         AudioClock::ModeChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::store_clock_modes));
183
184         Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::first_idle));
185
186         start_clocking ();
187         start_blinking ();
188
189         map_transport_state ();
190
191         second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_second), 1000);
192         point_one_second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_one_seconds), 100);
193         point_zero_something_second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_zero_something_seconds), 40);
194
195         update_format ();
196
197         if (editor_meter) {
198                 meter_box.remove(*editor_meter);
199                 delete editor_meter;
200                 editor_meter = 0;
201                 editor_meter_peak_display.hide();
202         }
203
204         if (_session && _session->master_out()) {
205                 editor_meter = new LevelMeterHBox(_session);
206                 editor_meter->set_meter (_session->master_out()->shared_peak_meter().get());
207                 editor_meter->clear_meters();
208                 editor_meter->set_type (_session->master_out()->meter_type());
209                 editor_meter->setup_meters (30, 12, 6);
210                 editor_meter->show();
211                 meter_box.pack_start(*editor_meter);
212
213                 ArdourMeter::ResetAllPeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_peak_display));
214                 ArdourMeter::ResetRoutePeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_route_peak_display));
215                 ArdourMeter::ResetGroupPeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_group_peak_display));
216
217                 editor_meter_peak_display.set_name ("meterbridge peakindicator");
218                 editor_meter_peak_display.set_elements((ArdourButton::Element) (ArdourButton::Edge|ArdourButton::Body));
219                 editor_meter_peak_display.unset_flags (Gtk::CAN_FOCUS);
220                 editor_meter_peak_display.set_size_request(6, -1);
221                 editor_meter_peak_display.set_corner_radius(2);
222
223                 editor_meter_max_peak = -INFINITY;
224                 editor_meter_peak_display.signal_button_release_event().connect (sigc::mem_fun(*this, &ARDOUR_UI::editor_meter_peak_button_release), false);
225
226                 if (Config->get_show_editor_meter()) {
227                         meter_box.show();
228                         editor_meter_peak_display.show();
229                 } else {
230                         meter_box.hide();
231                         editor_meter_peak_display.hide();
232                 }
233         }
234
235 }
236
237 int
238 ARDOUR_UI::unload_session (bool hide_stuff)
239 {
240         if (_session) {
241                 ARDOUR_UI::instance()->video_timeline->sync_session_state();
242         }
243
244         if (_session && _session->dirty()) {
245                 std::vector<std::string> actions;
246                 actions.push_back (_("Don't close"));
247                 actions.push_back (_("Just close"));
248                 actions.push_back (_("Save and close"));
249                 switch (ask_about_saving_session (actions)) {
250                 case -1:
251                         // cancel
252                         return 1;
253
254                 case 1:
255                         _session->save_state ("");
256                         break;
257                 }
258         }
259
260         if (hide_stuff) {
261                 editor->hide ();
262                 mixer->hide ();
263                 meterbridge->hide ();
264                 theme_manager->hide ();
265                 audio_port_matrix->hide();
266                 midi_port_matrix->hide();
267                 route_params->hide();
268         }
269
270         second_connection.disconnect ();
271         point_one_second_connection.disconnect ();
272         point_zero_something_second_connection.disconnect();
273
274         if (editor_meter) {
275                 meter_box.remove(*editor_meter);
276                 delete editor_meter;
277                 editor_meter = 0;
278                 editor_meter_peak_display.hide();
279         }
280
281         ActionManager::set_sensitive (ActionManager::session_sensitive_actions, false);
282
283         rec_button.set_sensitive (false);
284
285         WM::Manager::instance().set_session ((ARDOUR::Session*) 0);
286         ARDOUR_UI::instance()->video_timeline->close_session();
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 (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 }