Merge remote-tracking branch 'remotes/origin/cairocanvas' 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         /* 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
283         if (ARDOUR_UI::instance()->video_timeline) {
284                 ARDOUR_UI::instance()->video_timeline->close_session();
285         }
286
287         stop_blinking ();
288         stop_clocking ();
289
290         /* drop everything attached to the blink signal */
291
292         Blink.clear ();
293
294         delete _session;
295         _session = 0;
296
297         session_loaded = false;
298
299         update_buffer_load ();
300
301         return 0;
302 }
303
304 static bool
305 _hide_splash (gpointer arg)
306 {
307         ((ARDOUR_UI*)arg)->hide_splash();
308         return false;
309 }
310
311 void
312 ARDOUR_UI::goto_editor_window ()
313 {
314         if (splash && splash->is_visible()) {
315                 // in 2 seconds, hide the splash screen
316                 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 2000);
317         }
318
319         editor->show_window ();
320         editor->present ();
321         /* mixer should now be on top */
322         WM::Manager::instance().set_transient_for (editor);
323         _mixer_on_top = false;
324 }
325
326 void
327 ARDOUR_UI::goto_mixer_window ()
328 {
329         Glib::RefPtr<Gdk::Window> win;
330         Glib::RefPtr<Gdk::Screen> screen;
331         
332         if (editor) {
333                 win = editor->get_window ();
334         }
335
336         if (win) {
337                 screen = win->get_screen();
338         } else {
339                 screen = Gdk::Screen::get_default();
340         }
341         
342         if (screen && screen->get_height() < 700) {
343                 Gtk::MessageDialog msg (_("This screen is not tall enough to display the mixer window"));
344                 msg.run ();
345                 return;
346         }
347
348         mixer->show_window ();
349         mixer->present ();
350         /* mixer should now be on top */
351         WM::Manager::instance().set_transient_for (mixer);
352         _mixer_on_top = true;
353 }
354
355 void
356 ARDOUR_UI::toggle_mixer_window ()
357 {
358         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-mixer"));
359         if (!act) {
360                 return;
361         }
362
363         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
364
365         if (tact->get_active()) {
366                 goto_mixer_window ();
367         } else {
368                 mixer->hide ();
369         }
370 }
371
372 void
373 ARDOUR_UI::toggle_meterbridge ()
374 {
375         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-meterbridge"));
376         if (!act) {
377                 return;
378         }
379
380         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
381
382         if (tact->get_active()) {
383                 meterbridge->show_window ();
384         } else {
385                 meterbridge->hide_window (NULL);
386         }
387 }
388
389 void
390 ARDOUR_UI::toggle_editor_mixer ()
391 {
392         bool obscuring = false;
393         /* currently, if windows are on different
394            screens then we do nothing; but in the
395            future we may want to bring the window 
396            to the front or something, so I'm leaving this 
397            variable for future use
398         */
399         bool same_screen = true; 
400         
401         if (editor && mixer) {
402
403                 /* remeber: Screen != Monitor (Screen is a separately rendered
404                  * continuous geometry that make include 1 or more monitors.
405                  */
406                 
407                 if (editor->get_screen() != mixer->get_screen() && (mixer->get_screen() != 0) && (editor->get_screen() != 0)) {
408                         // different screens, so don't do anything
409                         same_screen = false;
410                 } else {
411                         // they are on the same screen, see if they are obscuring each other
412
413                         gint ex, ey, ew, eh;
414                         gint mx, my, mw, mh;
415
416                         editor->get_position (ex, ey);
417                         editor->get_size (ew, eh);
418
419                         mixer->get_position (mx, my);
420                         mixer->get_size (mw, mh);
421
422                         GdkRectangle e;
423                         GdkRectangle m;
424                         GdkRectangle r;
425
426                         e.x = ex;
427                         e.y = ey;
428                         e.width = ew;
429                         e.height = eh;
430
431                         m.x = mx;
432                         m.y = my;
433                         m.width = mw;
434                         m.height = mh;
435
436                         if (gdk_rectangle_intersect (&e, &m, &r)) {
437                                 obscuring = true;
438                         }
439                 }
440         }
441
442         if (mixer && !mixer->not_visible() && mixer->property_has_toplevel_focus()) {
443                 if (obscuring && same_screen) {
444                         goto_editor_window();
445                 }
446         } else if (editor && !editor->not_visible() && editor->property_has_toplevel_focus()) {
447                 if (obscuring && same_screen) {
448                         goto_mixer_window();
449                 }
450         } else if (mixer && mixer->not_visible()) {
451                 if (obscuring && same_screen) {
452                         goto_mixer_window ();
453                 }
454         } else if (editor && editor->not_visible()) {
455                 if (obscuring && same_screen) {
456                         goto_editor_window ();
457                 }
458         } else if (obscuring && same_screen) {
459                 //it's unclear what to do here, so just do the opposite of what we did last time  (old behavior)
460                 if (_mixer_on_top) {
461                         goto_editor_window ();
462                 } else {
463                         goto_mixer_window ();
464                 }
465         }
466 }
467
468 void
469 ARDOUR_UI::new_midi_tracer_window ()
470 {
471         RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("NewMIDITracer"));
472         if (!act) {
473                 return;
474         }
475
476         std::list<MidiTracer*>::iterator i = _midi_tracer_windows.begin ();
477         while (i != _midi_tracer_windows.end() && (*i)->get_visible() == true) {
478                 ++i;
479         }
480
481         if (i == _midi_tracer_windows.end()) {
482                 /* all our MIDITracer windows are visible; make a new one */
483                 MidiTracer* t = new MidiTracer ();
484                 t->show_all ();
485                 _midi_tracer_windows.push_back (t);
486         } else {
487                 /* re-use the hidden one */
488                 (*i)->show_all ();
489         }
490 }
491
492 BundleManager*
493 ARDOUR_UI::create_bundle_manager ()
494 {
495         return new BundleManager (_session);
496 }
497
498 AddVideoDialog*
499 ARDOUR_UI::create_add_video_dialog ()
500 {
501         return new AddVideoDialog (_session);
502 }
503
504 SessionOptionEditor*
505 ARDOUR_UI::create_session_option_editor ()
506 {
507         return new SessionOptionEditor (_session);
508 }
509
510 BigClockWindow*
511 ARDOUR_UI::create_big_clock_window ()
512 {
513         return new BigClockWindow (*big_clock);
514 }
515
516 void
517 ARDOUR_UI::handle_locations_change (Location *)
518 {
519         if (_session) {
520                 if (_session->locations()->num_range_markers()) {
521                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
522                 } else {
523                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
524                 }
525         }
526 }
527
528 bool
529 ARDOUR_UI::main_window_state_event_handler (GdkEventWindowState* ev, bool window_was_editor)
530 {
531         if (window_was_editor) {
532
533                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
534                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
535                         if (big_clock_window) {
536                                 big_clock_window->set_transient_for (*editor);
537                         }
538                 }
539
540         } else {
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 (*mixer);
546                         }
547                 }
548         }
549
550         return false;
551 }
552
553 bool
554 ARDOUR_UI::editor_meter_peak_button_release (GdkEventButton* ev)
555 {
556         if (ev->button == 1 && Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::PrimaryModifier|Gtkmm2ext::Keyboard::TertiaryModifier)) {
557                 ArdourMeter::ResetAllPeakDisplays ();
558         } else if (ev->button == 1 && Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::PrimaryModifier)) {
559                 if (_session->master_out()) {
560                         ArdourMeter::ResetGroupPeakDisplays (_session->master_out()->route_group());
561                 }
562         } else if (_session->master_out()) {
563                 ArdourMeter::ResetRoutePeakDisplays (_session->master_out().get());
564         }
565         return true;
566 }