break out window ops for Tabbables into show/hide/attach/detach
[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/audioengine.h"
27 #include "ardour/automation_watch.h"
28 #include "ardour/control_protocol_manager.h"
29 #include "ardour/profile.h"
30 #include "ardour/session.h"
31 #include "control_protocol/control_protocol.h"
32
33 #include "actions.h"
34 #include "add_route_dialog.h"
35 #include "add_video_dialog.h"
36 #include "ardour_ui.h"
37 #include "big_clock_window.h"
38 #include "bundle_manager.h"
39 #include "global_port_matrix.h"
40 #include "gui_object.h"
41 #include "gui_thread.h"
42 #include "keyeditor.h"
43 #include "location_ui.h"
44 #include "main_clock.h"
45 #include "meterbridge.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 #include "timers.h"
60
61 #include <gtkmm2ext/keyboard.h>
62
63 #include "i18n.h"
64
65 using namespace ARDOUR;
66 using namespace PBD;
67 using namespace Glib;
68 using namespace Gtk;
69 using namespace Gtkmm2ext;
70
71 void
72 ARDOUR_UI::set_session (Session *s)
73 {
74         SessionHandlePtr::set_session (s);
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_connection = Timers::blink_connect (sigc::mem_fun(*this, &ARDOUR_UI::blink_handler));
151
152         _session->SaveSessionRequested.connect (_session_connections, MISSING_INVALIDATOR, boost::bind (&ARDOUR_UI::save_session_at_its_request, this, _1), gui_context());
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::session_dirty_changed, 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         /* Clocks are on by default after we are connected to a session, so show that here.
166         */
167
168         connect_dependents_to_session (s);
169
170         /* listen to clock mode changes. don't do this earlier because otherwise as the clocks
171            restore their modes or are explicitly set, we will cause the "new" mode to be saved
172            back to the session XML ("Extra") state.
173          */
174
175         AudioClock::ModeChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::store_clock_modes));
176
177         Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::first_idle));
178
179         start_clocking ();
180
181         map_transport_state ();
182
183         second_connection = Timers::second_connect (sigc::mem_fun(*this, &ARDOUR_UI::every_second));
184         point_one_second_connection = Timers::rapid_connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_one_seconds));
185         point_zero_something_second_connection = Timers::super_rapid_connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_zero_something_seconds));
186         set_fps_timeout_connection();
187
188         update_format ();
189
190         if (meter_box.get_parent()) {
191                 transport_tearoff_hbox.remove (meter_box);
192                 transport_tearoff_hbox.remove (editor_meter_peak_display);
193         }
194
195         if (editor_meter) {
196                 meter_box.remove(*editor_meter);
197                 delete editor_meter;
198                 editor_meter = 0;
199                 editor_meter_peak_display.hide();
200         }
201
202         if (meter_box.get_parent()) {
203                 transport_tearoff_hbox.remove (meter_box);
204                 transport_tearoff_hbox.remove (editor_meter_peak_display);
205         }
206
207         if (_session &&
208             _session->master_out() &&
209             _session->master_out()->n_outputs().n(DataType::AUDIO) > 0) {
210
211                 if (!ARDOUR::Profile->get_trx()) {
212                         editor_meter = new LevelMeterHBox(_session);
213                         editor_meter->set_meter (_session->master_out()->shared_peak_meter().get());
214                         editor_meter->clear_meters();
215                         editor_meter->set_type (_session->master_out()->meter_type());
216                         editor_meter->setup_meters (30, 12, 6);
217                         editor_meter->show();
218                         meter_box.pack_start(*editor_meter);
219                 }
220
221                 ArdourMeter::ResetAllPeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_peak_display));
222                 ArdourMeter::ResetRoutePeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_route_peak_display));
223                 ArdourMeter::ResetGroupPeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_group_peak_display));
224
225                 editor_meter_peak_display.set_name ("meterbridge peakindicator");
226                 editor_meter_peak_display.unset_flags (Gtk::CAN_FOCUS);
227                 editor_meter_peak_display.set_size_request (std::max(9.f, rintf(8.f * UIConfiguration::instance().get_ui_scale())), -1);
228                 editor_meter_peak_display.set_corner_radius (3.0);
229
230                 editor_meter_max_peak = -INFINITY;
231                 editor_meter_peak_display.signal_button_release_event().connect (sigc::mem_fun(*this, &ARDOUR_UI::editor_meter_peak_button_release), false);
232
233                 if (UIConfiguration::instance().get_show_editor_meter() && !ARDOUR::Profile->get_trx()) {
234                         transport_tearoff_hbox.pack_start (meter_box, false, false);
235                         transport_tearoff_hbox.pack_start (editor_meter_peak_display, false, false);
236                         meter_box.show();
237                         editor_meter_peak_display.show();
238                 }
239         }
240
241         update_title ();
242 }
243
244 int
245 ARDOUR_UI::unload_session (bool hide_stuff)
246 {
247         if (_session) {
248                 ARDOUR_UI::instance()->video_timeline->sync_session_state();
249         }
250
251         if (_session && _session->dirty()) {
252                 std::vector<std::string> actions;
253                 actions.push_back (_("Don't close"));
254                 actions.push_back (_("Just close"));
255                 actions.push_back (_("Save and close"));
256                 switch (ask_about_saving_session (actions)) {
257                 case -1:
258                         // cancel
259                         return 1;
260
261                 case 1:
262                         _session->save_state ("");
263                         break;
264                 }
265         }
266
267         {
268                 // tear down session specific CPI (owned by rc_config_editor which can remain)
269                 ControlProtocolManager& m = ControlProtocolManager::instance ();
270                 for (std::list<ControlProtocolInfo*>::iterator i = m.control_protocol_info.begin(); i != m.control_protocol_info.end(); ++i) {
271                         if (*i && (*i)->protocol && (*i)->protocol->has_editor ()) {
272                                 (*i)->protocol->tear_down_gui ();
273                         }
274                 }
275         }
276
277         if (hide_stuff) {
278                 editor->hide ();
279                 mixer->hide ();
280                 meterbridge->hide ();
281                 audio_port_matrix->hide();
282                 midi_port_matrix->hide();
283                 route_params->hide();
284         }
285
286         second_connection.disconnect ();
287         point_one_second_connection.disconnect ();
288         point_zero_something_second_connection.disconnect();
289         fps_connection.disconnect();
290
291         if (editor_meter) {
292                 meter_box.remove(*editor_meter);
293                 delete editor_meter;
294                 editor_meter = 0;
295                 editor_meter_peak_display.hide();
296         }
297
298         ActionManager::set_sensitive (ActionManager::session_sensitive_actions, false);
299
300         rec_button.set_sensitive (false);
301
302         WM::Manager::instance().set_session ((ARDOUR::Session*) 0);
303
304         if (ARDOUR_UI::instance()->video_timeline) {
305                 ARDOUR_UI::instance()->video_timeline->close_session();
306         }
307
308         stop_clocking ();
309
310         /* drop everything attached to the blink signal */
311
312         blink_connection.disconnect ();
313
314         delete _session;
315         _session = 0;
316
317         session_loaded = false;
318
319         update_buffer_load ();
320         update_title ();
321         
322         return 0;
323 }
324
325 static bool
326 _hide_splash (gpointer arg)
327 {
328         ((ARDOUR_UI*)arg)->hide_splash();
329         return false;
330 }
331
332 void
333 ARDOUR_UI::show_tabbable (Tabbable* t)
334 {
335         if (splash && splash->is_visible()) {
336                 // in 2 seconds, hide the splash screen
337                 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 2000);
338         }
339
340         t->make_visible ();
341 }
342
343 void
344 ARDOUR_UI::hide_tabbable (Tabbable* t)
345 {
346         t->make_invisible ();
347 }
348
349 void
350 ARDOUR_UI::attach_tabbable (Tabbable* t)
351 {
352         if (splash && splash->is_visible()) {
353                 // in 2 seconds, hide the splash screen
354                 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 2000);
355         }
356
357         t->attach ();
358 }
359
360 void
361 ARDOUR_UI::detach_tabbable (Tabbable* t)
362 {
363         t->detach ();
364 }
365
366 void
367 ARDOUR_UI::toggle_meterbridge ()
368 {
369         assert (editor && mixer && meterbridge);
370
371         bool show = false;
372         bool obscuring = false;
373
374         if (meterbridge->not_visible ()) {
375                 show = true;
376         } else if ((editor->window_visible() && ARDOUR_UI_UTILS::windows_overlap (editor->own_window(), meterbridge)) ||
377                    (mixer->window_visible () && ARDOUR_UI_UTILS::windows_overlap (mixer->own_window(), meterbridge))) {
378                 obscuring = true;
379         }
380
381         if (obscuring && (editor->own_window()->property_has_toplevel_focus() || (mixer->own_window() && mixer->own_window()->property_has_toplevel_focus()))) {
382                 show = true;
383         }
384
385         if (show) {
386                 meterbridge->show_window ();
387                 meterbridge->present ();
388                 meterbridge->raise ();
389         } else {
390                 meterbridge->hide_window (NULL);
391         }
392 }
393
394 void
395 ARDOUR_UI::new_midi_tracer_window ()
396 {
397         RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("NewMIDITracer"));
398         if (!act) {
399                 return;
400         }
401
402         std::list<MidiTracer*>::iterator i = _midi_tracer_windows.begin ();
403         while (i != _midi_tracer_windows.end() && (*i)->get_visible() == true) {
404                 ++i;
405         }
406
407         if (i == _midi_tracer_windows.end()) {
408                 /* all our MIDITracer windows are visible; make a new one */
409                 MidiTracer* t = new MidiTracer ();
410                 t->show_all ();
411                 _midi_tracer_windows.push_back (t);
412         } else {
413                 /* re-use the hidden one */
414                 (*i)->show_all ();
415         }
416 }
417
418 BundleManager*
419 ARDOUR_UI::create_bundle_manager ()
420 {
421         return new BundleManager (_session);
422 }
423
424 AddVideoDialog*
425 ARDOUR_UI::create_add_video_dialog ()
426 {
427         return new AddVideoDialog (_session);
428 }
429
430 SessionOptionEditor*
431 ARDOUR_UI::create_session_option_editor ()
432 {
433         return new SessionOptionEditor (_session);
434 }
435
436 BigClockWindow*
437 ARDOUR_UI::create_big_clock_window ()
438 {
439         return new BigClockWindow (*big_clock);
440 }
441
442 void
443 ARDOUR_UI::handle_locations_change (Location *)
444 {
445         if (_session) {
446                 if (_session->locations()->num_range_markers()) {
447                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
448                 } else {
449                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
450                 }
451         }
452 }
453
454 bool
455 ARDOUR_UI::tabbed_window_state_event_handler (GdkEventWindowState* ev, void* object)
456 {
457         if (object == editor) {
458
459                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
460                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
461                         if (big_clock_window) {
462                                 big_clock_window->set_transient_for (*editor->own_window());
463                         }
464                 }
465
466         } else if (object == mixer) {
467
468                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
469                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
470                         if (big_clock_window) {
471                                 big_clock_window->set_transient_for (*mixer->own_window());
472                         }
473                 }
474         }
475
476         return false;
477 }
478
479 bool
480 ARDOUR_UI::editor_meter_peak_button_release (GdkEventButton* ev)
481 {
482         if (ev->button == 1 && Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::PrimaryModifier|Gtkmm2ext::Keyboard::TertiaryModifier)) {
483                 ArdourMeter::ResetAllPeakDisplays ();
484         } else if (ev->button == 1 && Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::PrimaryModifier)) {
485                 if (_session->master_out()) {
486                         ArdourMeter::ResetGroupPeakDisplays (_session->master_out()->route_group());
487                 }
488         } else if (_session->master_out()) {
489                 ArdourMeter::ResetRoutePeakDisplays (_session->master_out().get());
490         }
491         return false;
492 }
493
494 void
495 ARDOUR_UI::toggle_mixer_space()
496 {
497         Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMaximalMixer");
498
499         if (act) {
500                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
501                 if (tact->get_active()) {
502                         mixer->maximise_mixer_space ();
503                 } else {
504                         mixer->restore_mixer_space ();
505                 }
506         }
507 }
508
509 void
510 ARDOUR_UI::toggle_mixer_list()
511 {
512         Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMixerList");
513
514         if (act) {
515                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
516                         mixer->show_mixer_list (tact->get_active());
517         }
518 }
519
520 void
521 ARDOUR_UI::toggle_monitor_section_visibility ()
522 {
523         Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
524
525         if (act) {
526                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
527                         mixer->show_monitor_section (tact->get_active());
528         }
529 }