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