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