Rationalize ArdourButton and lots of associated theming issues.
[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/profile.h"
29 #include "ardour/session.h"
30
31 #include "actions.h"
32 #include "add_route_dialog.h"
33 #include "add_video_dialog.h"
34 #include "ardour_ui.h"
35 #include "big_clock_window.h"
36 #include "bundle_manager.h"
37 #include "global_port_matrix.h"
38 #include "gui_object.h"
39 #include "gui_thread.h"
40 #include "keyeditor.h"
41 #include "location_ui.h"
42 #include "main_clock.h"
43 #include "meter_patterns.h"
44 #include "midi_tracer.h"
45 #include "mixer_ui.h"
46 #include "public_editor.h"
47 #include "rc_option_editor.h"
48 #include "route_params_ui.h"
49 #include "shuttle_control.h"
50 #include "session_option_editor.h"
51 #include "speaker_dialog.h"
52 #include "splash.h"
53 #include "sfdb_ui.h"
54 #include "theme_manager.h"
55 #include "time_info_box.h"
56
57 #include <gtkmm2ext/keyboard.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         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         /* Clocks are on by default after we are connected to a session, so show that here.
165         */
166
167         connect_dependents_to_session (s);
168
169         /* listen to clock mode changes. don't do this earlier because otherwise as the clocks
170            restore their modes or are explicitly set, we will cause the "new" mode to be saved
171            back to the session XML ("Extra") state.
172          */
173
174         AudioClock::ModeChanged.connect (sigc::mem_fun (*this, &ARDOUR_UI::store_clock_modes));
175
176         Glib::signal_idle().connect (sigc::mem_fun (*this, &ARDOUR_UI::first_idle));
177
178         start_clocking ();
179         start_blinking ();
180
181         map_transport_state ();
182
183         second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_second), 1000);
184         point_one_second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_one_seconds), 100);
185         point_zero_something_second_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &ARDOUR_UI::every_point_zero_something_seconds), 40);
186
187         update_format ();
188
189         if (meter_box.get_parent()) {
190                 transport_tearoff_hbox.remove (meter_box);
191                 transport_tearoff_hbox.remove (editor_meter_peak_display);
192         }
193
194         if (editor_meter) {
195                 meter_box.remove(*editor_meter);
196                 delete editor_meter;
197                 editor_meter = 0;
198                 editor_meter_peak_display.hide();
199         }
200
201         if (meter_box.get_parent()) {
202                 transport_tearoff_hbox.remove (meter_box);
203                 transport_tearoff_hbox.remove (editor_meter_peak_display);
204         }
205
206         if (_session && 
207             _session->master_out() && 
208             _session->master_out()->n_outputs().n(DataType::AUDIO) > 0) {
209
210                 if (!ARDOUR::Profile->get_trx()) {
211                         editor_meter = new LevelMeterHBox(_session);
212                         editor_meter->set_meter (_session->master_out()->shared_peak_meter().get());
213                         editor_meter->clear_meters();
214                         editor_meter->set_type (_session->master_out()->meter_type());
215                         editor_meter->setup_meters (30, 12, 6);
216                         editor_meter->show();
217                         meter_box.pack_start(*editor_meter);
218                 }
219
220                 ArdourMeter::ResetAllPeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_peak_display));
221                 ArdourMeter::ResetRoutePeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_route_peak_display));
222                 ArdourMeter::ResetGroupPeakDisplays.connect (sigc::mem_fun(*this, &ARDOUR_UI::reset_group_peak_display));
223
224                 editor_meter_peak_display.set_name ("meterbridge peakindicator");
225                 editor_meter_peak_display.unset_flags (Gtk::CAN_FOCUS);
226                 editor_meter_peak_display.set_size_request(10, -1);
227                 editor_meter_peak_display.set_corner_radius(1);
228
229                 editor_meter_max_peak = -INFINITY;
230                 editor_meter_peak_display.signal_button_release_event().connect (sigc::mem_fun(*this, &ARDOUR_UI::editor_meter_peak_button_release), false);
231
232                 if (Config->get_show_editor_meter() && !ARDOUR::Profile->get_trx()) {
233                         transport_tearoff_hbox.pack_start (meter_box, false, false);
234                         transport_tearoff_hbox.pack_start (editor_meter_peak_display, false, false);
235                         meter_box.show();
236                         editor_meter_peak_display.show();
237                 }
238         }
239 }
240
241 int
242 ARDOUR_UI::unload_session (bool hide_stuff)
243 {
244         if (_session) {
245                 ARDOUR_UI::instance()->video_timeline->sync_session_state();
246         }
247
248         if (_session && _session->dirty()) {
249                 std::vector<std::string> actions;
250                 actions.push_back (_("Don't close"));
251                 actions.push_back (_("Just close"));
252                 actions.push_back (_("Save and close"));
253                 switch (ask_about_saving_session (actions)) {
254                 case -1:
255                         // cancel
256                         return 1;
257
258                 case 1:
259                         _session->save_state ("");
260                         break;
261                 }
262         }
263
264         if (hide_stuff) {
265                 editor->hide ();
266                 mixer->hide ();
267                 meterbridge->hide ();
268                 theme_manager->hide ();
269                 audio_port_matrix->hide();
270                 midi_port_matrix->hide();
271                 route_params->hide();
272         }
273
274         second_connection.disconnect ();
275         point_one_second_connection.disconnect ();
276         point_zero_something_second_connection.disconnect();
277
278         if (editor_meter) {
279                 meter_box.remove(*editor_meter);
280                 delete editor_meter;
281                 editor_meter = 0;
282                 editor_meter_peak_display.hide();
283         }
284
285         ActionManager::set_sensitive (ActionManager::session_sensitive_actions, false);
286
287         rec_button.set_sensitive (false);
288
289         WM::Manager::instance().set_session ((ARDOUR::Session*) 0);
290
291         if (ARDOUR_UI::instance()->video_timeline) {
292                 ARDOUR_UI::instance()->video_timeline->close_session();
293         }
294
295         stop_blinking ();
296         stop_clocking ();
297
298         /* drop everything attached to the blink signal */
299
300         Blink.clear ();
301
302         delete _session;
303         _session = 0;
304
305         session_loaded = false;
306
307         update_buffer_load ();
308
309         return 0;
310 }
311
312 static bool
313 _hide_splash (gpointer arg)
314 {
315         ((ARDOUR_UI*)arg)->hide_splash();
316         return false;
317 }
318
319 void
320 ARDOUR_UI::goto_editor_window ()
321 {
322         if (splash && splash->is_visible()) {
323                 // in 2 seconds, hide the splash screen
324                 Glib::signal_timeout().connect (sigc::bind (sigc::ptr_fun (_hide_splash), this), 2000);
325         }
326
327         editor->show_window ();
328         editor->present ();
329         /* mixer should now be on top */
330         WM::Manager::instance().set_transient_for (editor);
331         _mixer_on_top = false;
332 }
333
334 void
335 ARDOUR_UI::goto_mixer_window ()
336 {
337         Glib::RefPtr<Gdk::Window> win;
338         Glib::RefPtr<Gdk::Screen> screen;
339         
340         if (editor) {
341                 win = editor->get_window ();
342         }
343
344         if (win) {
345                 screen = win->get_screen();
346         } else {
347                 screen = Gdk::Screen::get_default();
348         }
349         
350         if (g_getenv ("ARDOUR_LOVES_STUPID_TINY_SCREENS") == 0 && screen && screen->get_height() < 700) {
351                 Gtk::MessageDialog msg (_("This screen is not tall enough to display the mixer window"));
352                 msg.run ();
353                 return;
354         }
355
356         mixer->show_window ();
357         mixer->present ();
358         /* mixer should now be on top */
359         WM::Manager::instance().set_transient_for (mixer);
360         _mixer_on_top = true;
361 }
362
363 void
364 ARDOUR_UI::toggle_mixer_window ()
365 {
366         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-mixer"));
367         if (!act) {
368                 return;
369         }
370
371         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
372
373         if (tact->get_active()) {
374                 goto_mixer_window ();
375         } else {
376                 mixer->hide ();
377         }
378 }
379
380 void
381 ARDOUR_UI::toggle_meterbridge ()
382 {
383         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("toggle-meterbridge"));
384         if (!act) {
385                 return;
386         }
387
388         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
389
390         if (tact->get_active()) {
391                 meterbridge->show_window ();
392         } else {
393                 meterbridge->hide_window (NULL);
394         }
395 }
396
397 void
398 ARDOUR_UI::toggle_editor_mixer ()
399 {
400         bool obscuring = false;
401         /* currently, if windows are on different
402            screens then we do nothing; but in the
403            future we may want to bring the window 
404            to the front or something, so I'm leaving this 
405            variable for future use
406         */
407         bool same_screen = true; 
408         
409         if (editor && mixer) {
410
411                 /* remeber: Screen != Monitor (Screen is a separately rendered
412                  * continuous geometry that make include 1 or more monitors.
413                  */
414                 
415                 if (editor->get_screen() != mixer->get_screen() && (mixer->get_screen() != 0) && (editor->get_screen() != 0)) {
416                         // different screens, so don't do anything
417                         same_screen = false;
418                 } else {
419                         // they are on the same screen, see if they are obscuring each other
420
421                         gint ex, ey, ew, eh;
422                         gint mx, my, mw, mh;
423
424                         editor->get_position (ex, ey);
425                         editor->get_size (ew, eh);
426
427                         mixer->get_position (mx, my);
428                         mixer->get_size (mw, mh);
429
430                         GdkRectangle e;
431                         GdkRectangle m;
432                         GdkRectangle r;
433
434                         e.x = ex;
435                         e.y = ey;
436                         e.width = ew;
437                         e.height = eh;
438
439                         m.x = mx;
440                         m.y = my;
441                         m.width = mw;
442                         m.height = mh;
443
444                         if (gdk_rectangle_intersect (&e, &m, &r)) {
445                                 obscuring = true;
446                         }
447                 }
448         }
449
450         if (mixer && !mixer->not_visible() && mixer->property_has_toplevel_focus()) {
451                 if (obscuring && same_screen) {
452                         goto_editor_window();
453                 }
454         } else if (editor && !editor->not_visible() && editor->property_has_toplevel_focus()) {
455                 if (obscuring && same_screen) {
456                         goto_mixer_window();
457                 }
458         } else if (mixer && mixer->not_visible()) {
459                 if (obscuring && same_screen) {
460                         goto_mixer_window ();
461                 }
462         } else if (editor && editor->not_visible()) {
463                 if (obscuring && same_screen) {
464                         goto_editor_window ();
465                 }
466         } else if (obscuring && same_screen) {
467                 //it's unclear what to do here, so just do the opposite of what we did last time  (old behavior)
468                 if (_mixer_on_top) {
469                         goto_editor_window ();
470                 } else {
471                         goto_mixer_window ();
472                 }
473         }
474 }
475
476 void
477 ARDOUR_UI::new_midi_tracer_window ()
478 {
479         RefPtr<Action> act = ActionManager::get_action (X_("Common"), X_("NewMIDITracer"));
480         if (!act) {
481                 return;
482         }
483
484         std::list<MidiTracer*>::iterator i = _midi_tracer_windows.begin ();
485         while (i != _midi_tracer_windows.end() && (*i)->get_visible() == true) {
486                 ++i;
487         }
488
489         if (i == _midi_tracer_windows.end()) {
490                 /* all our MIDITracer windows are visible; make a new one */
491                 MidiTracer* t = new MidiTracer ();
492                 t->show_all ();
493                 _midi_tracer_windows.push_back (t);
494         } else {
495                 /* re-use the hidden one */
496                 (*i)->show_all ();
497         }
498 }
499
500 BundleManager*
501 ARDOUR_UI::create_bundle_manager ()
502 {
503         return new BundleManager (_session);
504 }
505
506 AddVideoDialog*
507 ARDOUR_UI::create_add_video_dialog ()
508 {
509         return new AddVideoDialog (_session);
510 }
511
512 SessionOptionEditor*
513 ARDOUR_UI::create_session_option_editor ()
514 {
515         return new SessionOptionEditor (_session);
516 }
517
518 BigClockWindow*
519 ARDOUR_UI::create_big_clock_window ()
520 {
521         return new BigClockWindow (*big_clock);
522 }
523
524 void
525 ARDOUR_UI::handle_locations_change (Location *)
526 {
527         if (_session) {
528                 if (_session->locations()->num_range_markers()) {
529                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, true);
530                 } else {
531                         ActionManager::set_sensitive (ActionManager::range_sensitive_actions, false);
532                 }
533         }
534 }
535
536 bool
537 ARDOUR_UI::main_window_state_event_handler (GdkEventWindowState* ev, bool window_was_editor)
538 {
539         if (window_was_editor) {
540
541                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
542                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
543                         if (big_clock_window) {
544                                 big_clock_window->set_transient_for (*editor);
545                         }
546                 }
547
548         } else {
549
550                 if ((ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) &&
551                     (ev->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
552                         if (big_clock_window) {
553                                 big_clock_window->set_transient_for (*mixer);
554                         }
555                 }
556         }
557
558         return false;
559 }
560
561 bool
562 ARDOUR_UI::editor_meter_peak_button_release (GdkEventButton* ev)
563 {
564         if (ev->button == 1 && Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::PrimaryModifier|Gtkmm2ext::Keyboard::TertiaryModifier)) {
565                 ArdourMeter::ResetAllPeakDisplays ();
566         } else if (ev->button == 1 && Gtkmm2ext::Keyboard::modifier_state_equals (ev->state, Gtkmm2ext::Keyboard::PrimaryModifier)) {
567                 if (_session->master_out()) {
568                         ArdourMeter::ResetGroupPeakDisplays (_session->master_out()->route_group());
569                 }
570         } else if (_session->master_out()) {
571                 ArdourMeter::ResetRoutePeakDisplays (_session->master_out().get());
572         }
573         return false;
574 }
575
576 void
577 ARDOUR_UI::toggle_mixer_space()
578 {
579         Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMaximalMixer");
580
581         if (act) {
582                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
583                 if (tact->get_active()) {
584                         mixer->maximise_mixer_space ();
585                 } else {
586                         mixer->restore_mixer_space ();
587                 }
588         }
589 }