Work around badly chosen argument name in giomm
[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 "midi_tracer.h"
47 #include "mixer_ui.h"
48 #include "public_editor.h"
49 #include "rc_option_editor.h"
50 #include "route_params_ui.h"
51 #include "shuttle_control.h"
52 #include "session_option_editor.h"
53 #include "speaker_dialog.h"
54 #include "splash.h"
55 #include "sfdb_ui.h"
56 #include "theme_manager.h"
57 #include "time_info_box.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
73         if (!_session) {
74                 WM::Manager::instance().set_session (s);
75                 /* Session option editor cannot exist across change-of-session */
76                 session_option_editor.drop_window ();
77                 /* Ditto for AddVideoDialog */
78                 add_video_dialog.drop_window ();
79                 return;
80         }
81
82         const XMLNode* node = _session->extra_xml (X_("UI"));
83
84         if (node) {
85                 const XMLNodeList& children = node->children();
86                 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
87                         if ((*i)->name() == GUIObjectState::xml_node_name) {
88                                 gui_object_state->load (**i);
89                                 break;
90                         }
91                 }
92         }
93
94         WM::Manager::instance().set_session (s);
95
96         AutomationWatch::instance().set_session (s);
97
98         if (shuttle_box) {
99                 shuttle_box->set_session (s);
100         }
101
102         primary_clock->set_session (s);
103         secondary_clock->set_session (s);
104         big_clock->set_session (s);
105         time_info_box->set_session (s);
106         video_timeline->set_session (s);
107
108         /* sensitize menu bar options that are now valid */
109
110         ActionManager::set_sensitive (ActionManager::session_sensitive_actions, true);
111         ActionManager::set_sensitive (ActionManager::write_sensitive_actions, _session->writable());
112
113         if (_session->locations()->num_range_markers()) {
114                 ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
115         } else {
116                 ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
117         }
118
119         if (!_session->monitor_out()) {
120                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("options"), X_("SoloViaBus"));
121                 if (act) {
122                         act->set_sensitive (false);
123                 }
124         }
125
126         /* allow wastebasket flush again */
127
128         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Main"), X_("FlushWastebasket"));
129         if (act) {
130                 act->set_sensitive (true);
131         }
132
133         /* there are never any selections on startup */
134
135         ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
136         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, false);
137         ActionManager::set_sensitive (ActionManager::line_selection_sensitive_actions, false);
138         ActionManager::set_sensitive (ActionManager::point_selection_sensitive_actions, false);
139         ActionManager::set_sensitive (ActionManager::playlist_selection_sensitive_actions, false);
140
141         rec_button.set_sensitive (true);
142
143         solo_alert_button.set_active (_session->soloing());
144
145         setup_session_options ();
146
147         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::transport_rec_enable_blink));
148         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::solo_blink));
149         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::sync_blink));
150         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::audition_blink));
151         Blink.connect (sigc::mem_fun(*this, &ARDOUR_UI::feedback_blink));
152
153         _session->RecordStateChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::record_state_changed, this), gui_context());
154         _session->StepEditStatusChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::step_edit_status_change, this, _1), gui_context());
155         _session->TransportStateChange.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::map_transport_state, this), gui_context());
156         _session->DirtyChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::update_autosave, this), gui_context());
157
158         _session->Xrun.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::xrun_handler, this, _1), gui_context());
159         _session->SoloActive.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::soloing_changed, this, _1), gui_context());
160         _session->AuditionActive.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::auditioning_changed, this, _1), gui_context());
161         _session->locations()->added.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context());
162         _session->locations()->removed.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::handle_locations_change, this, _1), gui_context());
163         _session->config.ParameterChanged.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::session_parameter_changed, this, _1), gui_context ());
164
165 #ifdef HAVE_JACK_SESSION
166         engine->JackSessionEvent.connect (*_session, MISSING_INVALIDATOR, boost::bind (&Session::jack_session_event, _session, _1), gui_context());
167 #endif
168
169         /* Clocks are on by default after we are connected to a session, so show that here.
170         */
171
172         connect_dependents_to_session (s);
173
174         /* listen to clock mode changes. don't do this earlier because otherwise as the clocks
175            restore their modes or are explicitly set, we will cause the "new" mode to be saved
176            back to the session XML ("Extra") state.
177          */
178
179         AudioClock::ModeChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::store_clock_modes));
180
181         Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::first_idle));
182
183         start_clocking ();
184         start_blinking ();
185
186         map_transport_state ();
187
188         second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_second), 1000);
189         point_one_second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_one_seconds), 100);
190         point_zero_something_second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_zero_something_seconds), 40);
191
192         update_format ();
193 }
194
195 int
196 ARDOUR_UI::unload_session (bool hide_stuff)
197 {
198         if (_session) {
199                 ARDOUR_UI::instance()->video_timeline->sync_session_state();
200         }
201
202         if (_session && _session->dirty()) {
203                 std::vector<std::string> actions;
204                 actions.push_back (_("Don't close"));
205                 actions.push_back (_("Just close"));
206                 actions.push_back (_("Save and close"));
207                 switch (ask_about_saving_session (actions)) {
208                 case -1:
209                         // cancel
210                         return 1;
211
212                 case 1:
213                         _session->save_state ("");
214                         break;
215                 }
216         }
217
218         if (hide_stuff) {
219                 editor->hide ();
220                 mixer->hide ();
221                 meterbridge->hide ();
222                 theme_manager->hide ();
223                 audio_port_matrix->hide();
224                 midi_port_matrix->hide();
225                 route_params->hide();
226         }
227
228         second_connection.disconnect ();
229         point_one_second_connection.disconnect ();
230         point_zero_something_second_connection.disconnect();
231
232         ActionManager::set_sensitive (ActionManager::session_sensitive_actions, false);
233
234         rec_button.set_sensitive (false);
235
236         WM::Manager::instance().set_session ((ARDOUR::Session*) 0);
237         ARDOUR_UI::instance()->video_timeline->close_session();
238
239         stop_blinking ();
240         stop_clocking ();
241
242         /* drop everything attached to the blink signal */
243
244         Blink.clear ();
245
246         delete _session;
247         _session = 0;
248
249         session_loaded = false;
250
251         update_buffer_load ();
252
253         return 0;
254 }
255
256 static bool
257 _hide_splash (gpointer arg)
258 {
259         ((ARDOUR_UI*)arg)->hide_splash();
260         return false;
261 }
262
263 void
264 ARDOUR_UI::goto_editor_window ()
265 {
266         if (splash && splash->is_visible()) {
267                 // in 2 seconds, hide the splash screen
268                 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 2000);
269         }
270
271         editor->show_window ();
272         editor->present ();
273         /* mixer should now be on top */
274         WM::Manager::instance().set_transient_for (editor);
275         _mixer_on_top = false;
276 }
277
278 void
279 ARDOUR_UI::goto_mixer_window ()
280 {
281         Glib::RefPtr<Gdk::Window> win;
282         Glib::RefPtr<Gdk::Screen> screen;
283         
284         if (editor) {
285                 win = editor->get_window ();
286         }
287
288         if (win) {
289                 screen = win->get_screen();
290         } else {
291                 screen = Gdk::Screen::get_default();
292         }
293         
294         if (screen && screen->get_height() < 700) {
295                 Gtk::MessageDialog msg (_("This screen is not tall enough to display the mixer window"));
296                 msg.run ();
297                 return;
298         }
299
300         mixer->show_window ();
301         mixer->present ();
302         /* mixer should now be on top */
303         WM::Manager::instance().set_transient_for (mixer);
304         _mixer_on_top = true;
305 }
306
307 void
308 ARDOUR_UI::toggle_mixer_window ()
309 {
310         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-mixer"));
311         if (!act) {
312                 return;
313         }
314
315         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
316
317         if (tact->get_active()) {
318                 goto_mixer_window ();
319         } else {
320                 mixer->hide ();
321         }
322 }
323
324 void
325 ARDOUR_UI::toggle_meterbridge ()
326 {
327         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-meterbridge"));
328         if (!act) {
329                 return;
330         }
331
332         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
333
334         if (tact->get_active()) {
335                 meterbridge->show_window ();
336         } else {
337                 meterbridge->hide_window (NULL);
338         }
339 }
340
341 void
342 ARDOUR_UI::toggle_editor_mixer ()
343 {
344         bool obscuring = false;
345         /* currently, if windows are on different
346            screens then we do nothing; but in the
347            future we may want to bring the window 
348            to the front or something, so I'm leaving this 
349            variable for future use
350         */
351         bool same_screen = true; 
352         
353         if (editor && mixer) {
354
355                 /* remeber: Screen != Monitor (Screen is a separately rendered
356                  * continuous geometry that make include 1 or more monitors.
357                  */
358                 
359                 if (editor->get_screen() != mixer->get_screen() && (mixer->get_screen() != 0) && (editor->get_screen() != 0)) {
360                         // different screens, so don't do anything
361                         same_screen = false;
362                 } else {
363                         // they are on the same screen, see if they are obscuring each other
364
365                         gint ex, ey, ew, eh;
366                         gint mx, my, mw, mh;
367
368                         editor->get_position (ex, ey);
369                         editor->get_size (ew, eh);
370
371                         mixer->get_position (mx, my);
372                         mixer->get_size (mw, mh);
373
374                         GdkRectangle e;
375                         GdkRectangle m;
376                         GdkRectangle r;
377
378                         e.x = ex;
379                         e.y = ey;
380                         e.width = ew;
381                         e.height = eh;
382
383                         m.x = mx;
384                         m.y = my;
385                         m.width = mw;
386                         m.height = mh;
387
388                         if (gdk_rectangle_intersect (&e, &m, &r)) {
389                                 obscuring = true;
390                         }
391                 }
392         }
393
394         if (mixer && !mixer->not_visible() && mixer->property_has_toplevel_focus()) {
395                 if (obscuring && same_screen) {
396                         goto_editor_window();
397                 }
398         } else if (editor && !editor->not_visible() && editor->property_has_toplevel_focus()) {
399                 if (obscuring && same_screen) {
400                         goto_mixer_window();
401                 }
402         } else if (mixer && mixer->not_visible()) {
403                 if (obscuring && same_screen) {
404                         goto_mixer_window ();
405                 }
406         } else if (editor && editor->not_visible()) {
407                 if (obscuring && same_screen) {
408                         goto_editor_window ();
409                 }
410         } else if (obscuring && same_screen) {
411                 //it's unclear what to do here, so just do the opposite of what we did last time  (old behavior)
412                 if (_mixer_on_top) {
413                         goto_editor_window ();
414                 } else {
415                         goto_mixer_window ();
416                 }
417         }
418 }
419
420 void
421 ARDOUR_UI::new_midi_tracer_window ()
422 {
423         RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("NewMIDITracer"));
424         if (!act) {
425                 return;
426         }
427
428         std::list<MidiTracer*>::iterator i = _midi_tracer_windows.begin ();
429         while (i != _midi_tracer_windows.end() && (*i)->get_visible() == true) {
430                 ++i;
431         }
432
433         if (i == _midi_tracer_windows.end()) {
434                 /* all our MIDITracer windows are visible; make a new one */
435                 MidiTracer* t = new MidiTracer ();
436                 t->show_all ();
437                 _midi_tracer_windows.push_back (t);
438         } else {
439                 /* re-use the hidden one */
440                 (*i)->show_all ();
441         }
442 }
443
444 BundleManager*
445 ARDOUR_UI::create_bundle_manager ()
446 {
447         return new BundleManager (_session);
448 }
449
450 AddVideoDialog*
451 ARDOUR_UI::create_add_video_dialog ()
452 {
453         return new AddVideoDialog (_session);
454 }
455
456 SessionOptionEditor*
457 ARDOUR_UI::create_session_option_editor ()
458 {
459         return new SessionOptionEditor (_session);
460 }
461
462 BigClockWindow*
463 ARDOUR_UI::create_big_clock_window ()
464 {
465         return new BigClockWindow (*big_clock);
466 }
467
468 void
469 ARDOUR_UI::handle_locations_change (Location *)
470 {
471         if (_session) {
472                 if (_session->locations()->num_range_markers()) {
473                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
474                 } else {
475                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
476                 }
477         }
478 }
479
480 bool
481 ARDOUR_UI::main_window_state_event_handler (GdkEventWindowState* ev, bool window_was_editor)
482 {
483         if (window_was_editor) {
484
485                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
486                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
487                         if (big_clock_window) {
488                                 big_clock_window->set_transient_for (*editor);
489                         }
490                 }
491
492         } else {
493
494                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
495                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
496                         if (big_clock_window) {
497                                 big_clock_window->set_transient_for (*mixer);
498                         }
499                 }
500         }
501
502         return false;
503 }