d92b9e96a334a243f313ca033a2a60d48560855b
[ardour.git] / gtk2_ardour / monitor_section.cc
1 /*
2     Copyright (C) 2012 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 #include <gdkmm/pixbuf.h>
21
22 #include "pbd/compose.h"
23 #include "pbd/error.h"
24
25 #include "gtkmm2ext/bindable_button.h"
26 #include "gtkmm2ext/tearoff.h"
27 #include "gtkmm2ext/actions.h"
28 #include "gtkmm2ext/motionfeedback.h"
29
30 #include <gtkmm/menu.h>
31 #include <gtkmm/menuitem.h>
32
33 #include "ardour/monitor_processor.h"
34 #include "ardour/route.h"
35
36 #include "ardour_ui.h"
37 #include "gui_thread.h"
38 #include "monitor_section.h"
39 #include "public_editor.h"
40 #include "volume_controller.h"
41 #include "utils.h"
42
43 #include "i18n.h"
44
45 using namespace ARDOUR;
46 using namespace ARDOUR_UI_UTILS;
47 using namespace Gtk;
48 using namespace Gtkmm2ext;
49 using namespace PBD;
50 using namespace std;
51
52 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
53
54 MonitorSection::MonitorSection (Session* s)
55         : AxisView (s)
56         , RouteUI (s)
57         , _tearoff (0)
58         , channel_table_viewport (*channel_table_scroller.get_hadjustment()
59         , *channel_table_scroller.get_vadjustment ())
60         , gain_control (0)
61         , dim_control (0)
62         , solo_boost_control (0)
63         , solo_cut_control (0)
64         , gain_display (0)
65         , dim_display (0)
66         , solo_boost_display (0)
67         , solo_cut_display (0)
68         , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
69         , afl_button (_("AFL"), ArdourButton::led_default_elements)
70         , pfl_button (_("PFL"), ArdourButton::led_default_elements)
71         , exclusive_solo_button (ArdourButton::led_default_elements)
72         , solo_mute_override_button (ArdourButton::led_default_elements)
73         , _inhibit_solo_model_update (false)
74 {
75
76         using namespace Menu_Helpers;
77
78         Glib::RefPtr<Action> act;
79
80         if (!monitor_actions) {
81
82                 /* do some static stuff */
83
84                 register_actions ();
85
86         }
87
88         set_session (s);
89
90         VBox* spin_packer;
91         Label* spin_label;
92
93         /* Rude Solo */
94
95         rude_solo_button.set_text (_("Soloing"));
96         rude_solo_button.set_name ("rude solo");
97         rude_solo_button.show ();
98
99         rude_iso_button.set_text (_("Isolated"));
100         rude_iso_button.set_name ("rude isolate");
101         rude_iso_button.show ();
102
103         rude_audition_button.set_text (_("Auditioning"));
104         rude_audition_button.set_name ("rude audition");
105         rude_audition_button.show ();
106
107         ARDOUR_UI::Blink.connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
108
109         rude_solo_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_solo), false);
110         UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
111
112         rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
113         UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
114
115         rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
116         UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
117
118         solo_in_place_button.set_name ("monitor section solo model");
119         afl_button.set_name ("monitor section solo model");
120         pfl_button.set_name ("monitor section solo model");
121
122         solo_model_box.set_spacing (6);
123         solo_model_box.pack_start (solo_in_place_button, true, false);
124         solo_model_box.pack_start (afl_button, true, false);
125         solo_model_box.pack_start (pfl_button, true, false);
126
127         solo_in_place_button.show ();
128         afl_button.show ();
129         pfl_button.show ();
130         solo_model_box.show ();
131
132         act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
133         ARDOUR_UI::instance()->tooltips().set_tip (solo_in_place_button, _("Solo controls affect solo-in-place"));
134         if (act) {
135                 solo_in_place_button.set_related_action (act);
136         }
137
138         act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
139         ARDOUR_UI::instance()->tooltips().set_tip (afl_button, _("Solo controls toggle after-fader-listen"));
140         if (act) {
141                 afl_button.set_related_action (act);
142         }
143
144         act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
145         ARDOUR_UI::instance()->tooltips().set_tip (pfl_button, _("Solo controls toggle pre-fader-listen"));
146         if (act) {
147                 pfl_button.set_related_action (act);
148         }
149
150         /* Solo Boost */
151
152         solo_boost_control = new ArdourKnob ();
153         solo_boost_control->set_name("monitor knob");
154         solo_boost_control->set_size_request(40,40);
155         ARDOUR_UI::instance()->tooltips().set_tip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
156
157         solo_boost_display = new ArdourDisplay ();
158         solo_boost_display->set_name("monitor section cut");
159         solo_boost_display->set_size_request(80,20);
160         solo_boost_display->add_controllable_preset("0dB", 0.0);
161         solo_boost_display->add_controllable_preset("3 dB", 3.0);
162         solo_boost_display->add_controllable_preset("6 dB", 6.0);
163         solo_boost_display->add_controllable_preset("10 dB", 10.0);
164
165         HBox* solo_packer = manage (new HBox);
166         solo_packer->set_spacing (6);
167         solo_packer->show ();
168
169         spin_label = manage (new Label (_("Solo Boost")));
170         spin_packer = manage (new VBox);
171         spin_packer->show ();
172         spin_packer->set_spacing (3);
173         spin_packer->pack_start (*spin_label, false, false);
174         spin_packer->pack_start (*solo_boost_control, false, false);
175         spin_packer->pack_start (*solo_boost_display, false, false);
176
177         solo_packer->pack_start (*spin_packer, true, false);
178
179         /* Solo (SiP) cut */
180
181         solo_cut_control = new ArdourKnob ();
182         solo_cut_control->set_name ("monitor knob");
183         solo_cut_control->set_size_request (40,40);
184         ARDOUR_UI::instance()->tooltips().set_tip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
185
186         solo_cut_display = new ArdourDisplay ();
187         solo_cut_display->set_name("monitor section cut");
188         solo_cut_display->set_size_request(80,20);
189         solo_cut_display->add_controllable_preset("0dB", 0.0);
190         solo_cut_display->add_controllable_preset("-6 dB", -6.0);
191         solo_cut_display->add_controllable_preset("-12 dB", -12.0);
192         solo_cut_display->add_controllable_preset("-20 dB", -20.0);
193         solo_cut_display->add_controllable_preset("OFF", -1200.0);
194
195         spin_label = manage (new Label (_("SiP Cut")));
196         spin_packer = manage (new VBox);
197         spin_packer->show ();
198         spin_packer->set_spacing (3);
199         spin_packer->pack_start (*spin_label, false, false);
200         spin_packer->pack_start (*solo_cut_control, false, false);
201         spin_packer->pack_start (*solo_cut_display, false, false);
202
203         solo_packer->pack_start (*spin_packer, true, false);
204
205         /* Dim */
206
207         dim_control = new ArdourKnob ();
208         dim_control->set_name ("monitor knob");
209         dim_control->set_size_request (40,40);
210         ARDOUR_UI::instance()->tooltips().set_tip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
211
212         dim_display = new ArdourDisplay ();
213         dim_display->set_name("monitor section cut");
214         dim_display->set_size_request(80,20);
215         dim_display->add_controllable_preset("0dB", 0.0);
216         dim_display->add_controllable_preset("-3 dB", -3.0);
217         dim_display->add_controllable_preset("-6 dB", -6.0);
218         dim_display->add_controllable_preset("-12 dB", -12.0);
219         dim_display->add_controllable_preset("-20 dB", -20.0);
220
221         HBox* dim_packer = manage (new HBox);
222         dim_packer->show ();
223
224         spin_label = manage (new Label (_("Dim")));
225         spin_packer = manage (new VBox);
226         spin_packer->show ();
227         spin_packer->set_spacing (3);
228         spin_packer->pack_start (*spin_label, false, false);
229         spin_packer->pack_start (*dim_control, false, false);
230         spin_packer->pack_start (*dim_display, false, false);
231
232         dim_packer->pack_start (*spin_packer, true, false);
233
234         exclusive_solo_button.set_text (_("Excl. Solo"));
235         exclusive_solo_button.set_name (X_("monitor solo exclusive"));
236         ARDOUR_UI::instance()->set_tip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
237
238         act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
239         if (act) {
240                 exclusive_solo_button.set_related_action (act);
241         }
242
243         solo_mute_override_button.set_text (_("Solo Â» Mute"));
244         solo_mute_override_button.set_name (X_("monitor solo override"));
245         ARDOUR_UI::instance()->set_tip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
246
247         act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
248         if (act) {
249                 solo_mute_override_button.set_related_action (act);
250         }
251
252         HBox* solo_opt_box = manage (new HBox);
253         solo_opt_box->set_spacing (12);
254         solo_opt_box->set_homogeneous (true);
255         solo_opt_box->pack_start (exclusive_solo_button);
256         solo_opt_box->pack_start (solo_mute_override_button);
257         solo_opt_box->show ();
258
259         upper_packer.set_spacing (6);
260
261         Gtk::HBox* rude_box = manage (new HBox);
262         rude_box->pack_start (rude_solo_button, true, true);
263         rude_box->pack_start (rude_iso_button, true, true);
264
265         upper_packer.pack_start (*rude_box, false, false);
266         upper_packer.pack_start (rude_audition_button, false, false);
267         upper_packer.pack_start (solo_model_box, false, false, 12);
268         upper_packer.pack_start (*solo_opt_box, false, false);
269         upper_packer.pack_start (*solo_packer, false, false, 12);
270
271         cut_all_button.set_text (_("Mute"));
272         cut_all_button.set_name ("monitor section cut");
273         cut_all_button.set_name (X_("monitor section cut"));
274         cut_all_button.set_size_request (-1,50);
275         cut_all_button.show ();
276
277         act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
278         if (act) {
279                 cut_all_button.set_related_action (act);
280         }
281
282         dim_all_button.set_text (_("Dim"));
283         dim_all_button.set_name ("monitor section dim");
284         act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
285         if (act) {
286                 dim_all_button.set_related_action (act);
287         }
288
289         mono_button.set_text (_("Mono"));
290         mono_button.set_name ("monitor section mono");
291         act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
292         if (act) {
293                 mono_button.set_related_action (act);
294         }
295
296         HBox* bbox = manage (new HBox);
297
298         bbox->set_spacing (12);
299         bbox->pack_start (mono_button, true, true);
300         bbox->pack_start (dim_all_button, true, true);
301
302         lower_packer.set_spacing (12);
303         lower_packer.pack_start (*bbox, false, false);
304         lower_packer.pack_start (cut_all_button, false, false);
305
306         /* Gain */
307
308         gain_control = new ArdourKnob ();
309         gain_control->set_name("monitor knob");
310         gain_control->set_size_request(80,80);
311
312         gain_display = new ArdourDisplay ();
313         gain_display->set_name("monitor section cut");
314         gain_display->set_size_request(40,20);
315         gain_display->add_controllable_preset("0dB", 0.0);
316         gain_display->add_controllable_preset("-3 dB", -3.0);
317         gain_display->add_controllable_preset("-6 dB", -6.0);
318         gain_display->add_controllable_preset("-12 dB", -12.0);
319         gain_display->add_controllable_preset("-20 dB", -20.0);
320         gain_display->add_controllable_preset("-30 dB", -30.0);
321
322         spin_label = manage (new Label (_("Monitor")));
323         spin_packer = manage (new VBox);
324         spin_packer->show ();
325         spin_packer->set_spacing (3);
326         spin_packer->pack_start (*spin_label, false, false);
327         spin_packer->pack_start (*gain_control, false, false);
328         spin_packer->pack_start (*gain_display, false, false);
329
330         lower_packer.pack_start (*spin_packer, true, true);
331
332         channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
333         channel_table_scroller.set_size_request (-1, 150);
334         channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
335         channel_table_scroller.show ();
336         channel_table_scroller.add (channel_table_viewport);
337
338         channel_size_group  = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
339         channel_size_group->add_widget (channel_table_header);
340         channel_size_group->add_widget (channel_table);
341
342         channel_table_header.resize (1, 5);
343
344         Label* l1 = manage (new Label (X_("  ")));
345         l1->set_name (X_("MonitorSectionLabel"));
346         channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
347
348         l1 = manage (new Label (X_("Mute")));
349         l1->set_name (X_("MonitorSectionLabel"));
350         channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
351
352         l1 = manage (new Label (X_("Dim")));
353         l1->set_name (X_("MonitorSectionLabel"));
354         channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
355
356         l1 = manage (new Label (X_("Solo")));
357         l1->set_name (X_("MonitorSectionLabel"));
358         channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
359
360         l1 = manage (new Label (X_("Inv")));
361         l1->set_name (X_("MonitorSectionLabel"));
362         channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
363
364         channel_table_header.show ();
365
366         table_hpacker.pack_start (channel_table, true, true);
367
368         /* note that we don't pack the table_hpacker till later
369         */
370
371         vpacker.set_border_width (6);
372         vpacker.set_spacing (12);
373         vpacker.pack_start (upper_packer, false, false);
374         vpacker.pack_start (*dim_packer, false, false);
375         vpacker.pack_start (channel_table_header, false, false);
376         vpacker.pack_start (channel_table_packer, false, false);
377         vpacker.pack_start (lower_packer, false, false);
378
379         hpacker.pack_start (vpacker, true, true);
380
381         gain_control->show_all ();
382         gain_display->show_all ();
383         dim_control->show_all ();
384         dim_display->show_all();
385         solo_boost_control->show_all ();
386         solo_boost_display->show_all();
387
388         channel_table.show ();
389         hpacker.show ();
390         upper_packer.show ();
391         lower_packer.show ();
392         vpacker.show ();
393
394         populate_buttons ();
395         map_state ();
396         assign_controllables ();
397
398         _tearoff = new TearOff (hpacker);
399
400         /* if torn off, make this a normal window */
401         _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
402         _tearoff->tearoff_window().set_title (X_("Monitor"));
403         _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
404
405         /* catch changes that affect us */
406
407         Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
408 }
409
410 MonitorSection::~MonitorSection ()
411 {
412         for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
413                 delete *i;
414         }
415
416         _channel_buttons.clear ();
417
418         delete gain_control;
419         delete gain_display;
420         delete dim_control;
421         delete dim_display;
422         delete solo_boost_control;
423         delete solo_boost_display;
424         delete solo_cut_control;
425         delete solo_cut_display;
426         delete _tearoff;
427 }
428
429 void
430 MonitorSection::set_session (Session* s)
431 {
432         AxisView::set_session (s);
433
434         if (_session) {
435
436                 _route = _session->monitor_out ();
437
438                 if (_route) {
439                         /* session with monitor section */
440                         _monitor = _route->monitor_control ();
441                         assign_controllables ();
442                 } else {
443                         /* session with no monitor section */
444                         _monitor.reset ();
445                         _route.reset ();
446                 }
447
448                 if (channel_table_scroller.get_parent()) {
449                         /* scroller is packed, so remove it */
450                         channel_table_packer.remove (channel_table_scroller);
451                 }
452
453                 if (table_hpacker.get_parent () == &channel_table_packer) {
454                         /* this occurs when the table hpacker is directly
455                                  packed, so remove it.
456                                  */
457                         channel_table_packer.remove (table_hpacker);
458                 } else if (table_hpacker.get_parent()) {
459                         channel_table_viewport.remove ();
460                 }
461
462                 if (_monitor->output_streams().n_audio() > 7) {
463                         /* put the table into a scrolled window, and then put
464                          * that into the channel vpacker, after the table header
465                          */
466                         channel_table_viewport.add (table_hpacker);
467                         channel_table_packer.pack_start (channel_table_scroller, true, true);
468                         channel_table_viewport.show ();
469                         channel_table_scroller.show ();
470
471                 } else {
472                         /* just put the channel table itself into the channel
473                          * vpacker, after the table header
474                          */
475
476                         channel_table_packer.pack_start (table_hpacker, true, true);
477                         channel_table_scroller.hide ();
478                 }
479
480                 table_hpacker.show ();
481                 channel_table.show ();
482
483         } else {
484                 /* no session */
485
486                 _monitor.reset ();
487                 _route.reset ();
488                 control_connections.drop_connections ();
489                 rude_iso_button.unset_active_state ();
490                 rude_solo_button.unset_active_state ();
491
492                 assign_controllables ();
493         }
494 }
495
496 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
497 {
498         cut.set_name (X_("monitor section cut"));
499         dim.set_name (X_("monitor section dim"));
500         solo.set_name (X_("monitor section solo"));
501         invert.set_name (X_("monitor section invert"));
502
503         cut.unset_flags (Gtk::CAN_FOCUS);
504         dim.unset_flags (Gtk::CAN_FOCUS);
505         solo.unset_flags (Gtk::CAN_FOCUS);
506         invert.unset_flags (Gtk::CAN_FOCUS);
507 }
508
509         void
510 MonitorSection::populate_buttons ()
511 {
512         if (!_monitor) {
513                 return;
514         }
515
516         Glib::RefPtr<Action> act;
517         uint32_t nchans = _monitor->output_streams().n_audio();
518
519         channel_table.resize (nchans, 5);
520         channel_table.set_col_spacings (6);
521         channel_table.set_row_spacings (6);
522         channel_table.set_homogeneous (true);
523
524         const uint32_t row_offset = 0;
525
526         for (uint32_t i = 0; i < nchans; ++i) {
527
528                 string l;
529                 char buf[64];
530
531                 if (nchans == 2) {
532                         if (i == 0) {
533                                 l = "L";
534                         } else {
535                                 l = "R";
536                         }
537                 } else {
538                         char buf[32];
539                         snprintf (buf, sizeof (buf), "%d", i+1);
540                         l = buf;
541                 }
542
543                 Label* label = manage (new Label (l));
544                 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
545
546                 ChannelButtonSet* cbs = new ChannelButtonSet;
547
548                 _channel_buttons.push_back (cbs);
549
550                 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
551                 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
552                 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
553                 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
554
555                 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
556                 act = ActionManager::get_action (X_("Monitor"), buf);
557                 if (act) {
558                         cbs->cut.set_related_action (act);
559                 }
560
561                 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
562                 act = ActionManager::get_action (X_("Monitor"), buf);
563                 if (act) {
564                         cbs->dim.set_related_action (act);
565                 }
566
567                 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
568                 act = ActionManager::get_action (X_("Monitor"), buf);
569                 if (act) {
570                         cbs->solo.set_related_action (act);
571                 }
572
573                 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
574                 act = ActionManager::get_action (X_("Monitor"), buf);
575                 if (act) {
576                         cbs->invert.set_related_action (act);
577                 }
578         }
579
580         channel_table.show_all ();
581 }
582
583 void
584 MonitorSection::toggle_exclusive_solo ()
585 {
586         if (!_monitor) {
587                 return;
588         }
589
590         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
591         if (act) {
592                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
593                 Config->set_exclusive_solo (tact->get_active());
594         }
595
596 }
597
598 void
599 MonitorSection::toggle_mute_overrides_solo ()
600 {
601         if (!_monitor) {
602                 return;
603         }
604
605         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
606         if (act) {
607                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
608                 Config->set_solo_mute_override (tact->get_active());
609         }
610 }
611
612 void
613 MonitorSection::dim_all ()
614 {
615         if (!_monitor) {
616                 return;
617         }
618
619         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
620         if (act) {
621                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
622                 _monitor->set_dim_all (tact->get_active());
623         }
624
625 }
626
627 void
628 MonitorSection::cut_all ()
629 {
630         if (!_monitor) {
631                 return;
632         }
633
634         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
635         if (act) {
636                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
637                 _monitor->set_cut_all (tact->get_active());
638         }
639 }
640
641 void
642 MonitorSection::mono ()
643 {
644         if (!_monitor) {
645                 return;
646         }
647
648         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
649         if (act) {
650                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
651                 _monitor->set_mono (tact->get_active());
652         }
653 }
654
655 void
656 MonitorSection::cut_channel (uint32_t chn)
657 {
658         if (!_monitor) {
659                 return;
660         }
661
662         char buf[64];
663         snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
664
665         --chn; // 0-based in backend
666
667         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
668         if (act) {
669                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
670                 _monitor->set_cut (chn, tact->get_active());
671         }
672 }
673
674 void
675 MonitorSection::dim_channel (uint32_t chn)
676 {
677         if (!_monitor) {
678                 return;
679         }
680
681         char buf[64];
682         snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
683
684         --chn; // 0-based in backend
685
686         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
687         if (act) {
688                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
689                 _monitor->set_dim (chn, tact->get_active());
690         }
691
692 }
693
694 void
695 MonitorSection::solo_channel (uint32_t chn)
696 {
697         if (!_monitor) {
698                 return;
699         }
700
701         char buf[64];
702         snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
703
704         --chn; // 0-based in backend
705
706         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
707         if (act) {
708                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
709                 _monitor->set_solo (chn, tact->get_active());
710         }
711
712 }
713
714 void
715 MonitorSection::invert_channel (uint32_t chn)
716 {
717         if (!_monitor) {
718                 return;
719         }
720
721         char buf[64];
722         snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
723
724         --chn; // 0-based in backend
725
726         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
727         if (act) {
728                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
729                 _monitor->set_polarity (chn, tact->get_active());
730         }
731 }
732
733 void
734 MonitorSection::register_actions ()
735 {
736         string action_name;
737         string action_descr;
738         Glib::RefPtr<Action> act;
739
740         monitor_actions = ActionGroup::create (X_("Monitor"));
741         ActionManager::add_action_group (monitor_actions);
742
743         ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
744                         sigc::mem_fun (*this, &MonitorSection::mono));
745
746         ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
747                         sigc::mem_fun (*this, &MonitorSection::cut_all));
748
749         ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
750                         sigc::mem_fun (*this, &MonitorSection::dim_all));
751
752         act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
753                         sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
754
755         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
756         tact->set_active (Config->get_exclusive_solo());
757
758         act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
759                         sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
760
761         tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
762         tact->set_active (Config->get_solo_mute_override());
763
764
765         /* note the 1-based counting (for naming - backend uses 0-based) */
766
767         for (uint32_t chn = 1; chn <= 16; ++chn) {
768
769                 action_name = string_compose (X_("monitor-cut-%1"), chn);
770                 action_descr = string_compose (_("Cut monitor channel %1"), chn);
771                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
772                                 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
773
774                 action_name = string_compose (X_("monitor-dim-%1"), chn);
775                 action_descr = string_compose (_("Dim monitor channel %1"), chn);
776                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
777                                 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
778
779                 action_name = string_compose (X_("monitor-solo-%1"), chn);
780                 action_descr = string_compose (_("Solo monitor channel %1"), chn);
781                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
782                                 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
783
784                 action_name = string_compose (X_("monitor-invert-%1"), chn);
785                 action_descr = string_compose (_("Invert monitor channel %1"), chn);
786                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
787                                 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
788
789         }
790
791
792         Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
793         RadioAction::Group solo_group;
794
795         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
796                         sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
797         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
798                         sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
799         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
800                         sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
801
802         ActionManager::add_action_group (solo_actions);
803 }
804
805 void
806 MonitorSection::solo_use_in_place ()
807 {
808         /* this is driven by a toggle on a radio group, and so is invoked twice,
809                  once for the item that became inactive and once for the one that became
810                  active.
811                  */
812
813         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
814
815         if (act) {
816                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
817                 if (ract) {
818                         if (!ract->get_active ()) {
819                                 /* We are turning SiP off, which means that AFL or PFL will be turned on
820                                          shortly; don't update the solo model in the mean time, as if the currently
821                                          configured listen position is not the one that is about to be turned on,
822                                          things will go wrong.
823                                          */
824                                 _inhibit_solo_model_update = true;
825                         }
826                         Config->set_solo_control_is_listen_control (!ract->get_active());
827                         _inhibit_solo_model_update = false;
828                 }
829         }
830 }
831
832 void
833 MonitorSection::solo_use_afl ()
834 {
835         /* this is driven by a toggle on a radio group, and so is invoked twice,
836                  once for the item that became inactive and once for the one that became
837                  active.
838                  */
839
840         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
841         if (act) {
842                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
843                 if (ract) {
844                         if (ract->get_active()) {
845                                 Config->set_solo_control_is_listen_control (true);
846                                 Config->set_listen_position (AfterFaderListen);
847                         }
848                 }
849         }
850 }
851
852 void
853 MonitorSection::solo_use_pfl ()
854 {
855         /* this is driven by a toggle on a radio group, and so is invoked twice,
856                  once for the item that became inactive and once for the one that became
857                  active.
858                  */
859
860         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
861         if (act) {
862                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
863                 if (ract) {
864                         if (ract->get_active()) {
865                                 Config->set_solo_control_is_listen_control (true);
866                                 Config->set_listen_position (PreFaderListen);
867                         }
868                 }
869         }
870 }
871
872 void
873 MonitorSection::update_solo_model ()
874 {
875         if (_inhibit_solo_model_update) {
876                 return;
877         }
878
879         const char* action_name = 0;
880         Glib::RefPtr<Action> act;
881
882         if (Config->get_solo_control_is_listen_control()) {
883                 switch (Config->get_listen_position()) {
884                         case AfterFaderListen:
885                                 action_name = X_("solo-use-afl");
886                                 break;
887                         case PreFaderListen:
888                                 action_name = X_("solo-use-pfl");
889                                 break;
890                 }
891         } else {
892                 action_name = X_("solo-use-in-place");
893         }
894
895         act = ActionManager::get_action (X_("Solo"), action_name);
896         if (act) {
897
898                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
899                 if (ract) {
900                         /* because these are radio buttons, one of them will be
901                                  active no matter what. to trigger a change in the
902                                  action so that the view picks it up, toggle it.
903                                  */
904                         if (ract->get_active()) {
905                                 ract->set_active (false);
906                         }
907                         ract->set_active (true);
908                 }
909
910         }
911 }
912
913 void
914 MonitorSection::map_state ()
915 {
916         if (!_route || !_monitor) {
917                 return;
918         }
919
920         Glib::RefPtr<Action> act;
921
922         update_solo_model ();
923
924         act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
925         if (act) {
926                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
927                 if (tact) {
928                         tact->set_active (_monitor->cut_all());
929                 }
930         }
931
932         act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
933         if (act) {
934                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
935                 if (tact) {
936                         tact->set_active (_monitor->dim_all());
937                 }
938         }
939
940         act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
941         if (act) {
942                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
943                 if (tact) {
944                         tact->set_active (_monitor->mono());
945                 }
946         }
947
948         uint32_t nchans = _monitor->output_streams().n_audio();
949
950         assert (nchans == _channel_buttons.size ());
951
952         for (uint32_t n = 0; n < nchans; ++n) {
953
954                 char action_name[32];
955
956                 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
957                 act = ActionManager::get_action (X_("Monitor"), action_name);
958                 if (act) {
959                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
960                         if (tact) {
961                                 tact->set_active (_monitor->cut (n));
962                         }
963                 }
964
965                 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
966                 act = ActionManager::get_action (X_("Monitor"), action_name);
967                 if (act) {
968                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
969                         if (tact) {
970                                 tact->set_active (_monitor->dimmed (n));
971                         }
972                 }
973
974                 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
975                 act = ActionManager::get_action (X_("Monitor"), action_name);
976                 if (act) {
977                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
978                         if (tact) {
979                                 tact->set_active (_monitor->soloed (n));
980                         }
981                 }
982
983                 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
984                 act = ActionManager::get_action (X_("Monitor"), action_name);
985                 if (act) {
986                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
987                         if (tact) {
988                                 tact->set_active (_monitor->inverted (n));
989                         }
990                 }
991         }
992 }
993
994 void
995 MonitorSection::do_blink (bool onoff)
996 {
997         solo_blink (onoff);
998         audition_blink (onoff);
999 }
1000
1001 void
1002 MonitorSection::audition_blink (bool onoff)
1003 {
1004         if (_session == 0) {
1005                 return;
1006         }
1007
1008         if (_session->is_auditioning()) {
1009                 rude_audition_button.set_active (onoff);
1010         } else {
1011                 rude_audition_button.set_active (false);
1012         }
1013 }
1014
1015 void
1016 MonitorSection::solo_blink (bool onoff)
1017 {
1018         if (_session == 0) {
1019                 return;
1020         }
1021
1022         if (_session->soloing() || _session->listening()) {
1023                 rude_solo_button.set_active (onoff);
1024
1025                 if (_session->soloing()) {
1026                         if (_session->solo_isolated()) {
1027                                 rude_iso_button.set_active (false);
1028                         }
1029                 }
1030
1031         } else {
1032                 rude_solo_button.set_active (false);
1033                 rude_iso_button.set_active (false);
1034         }
1035 }
1036
1037 bool
1038 MonitorSection::cancel_solo (GdkEventButton*)
1039 {
1040         if (_session) {
1041                 if (_session->soloing()) {
1042                         _session->set_solo (_session->get_routes(), false);
1043                 } else if (_session->listening()) {
1044                         _session->set_listen (_session->get_routes(), false);
1045                 }
1046         }
1047
1048         return true;
1049 }
1050
1051 bool
1052 MonitorSection::cancel_isolate (GdkEventButton*)
1053 {
1054         if (_session) {
1055                 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1056                 _session->set_solo_isolated (rl, false, Session::rt_cleanup, true);
1057         }
1058
1059         return true;
1060 }
1061
1062 bool
1063 MonitorSection::cancel_audition (GdkEventButton*)
1064 {
1065         if (_session) {
1066                 _session->cancel_audition();
1067         }
1068         return true;
1069 }
1070
1071 void
1072 MonitorSection::parameter_changed (std::string name)
1073 {
1074         if (name == "solo-control-is-listen-control") {
1075                 update_solo_model ();
1076         } else if (name == "listen-position") {
1077                 update_solo_model ();
1078         }
1079 }
1080
1081 void
1082 MonitorSection::assign_controllables ()
1083 {
1084         boost::shared_ptr<Controllable> none;
1085
1086         if (!gain_control) {
1087                 /* too early - GUI controls not set up yet */
1088                 return;
1089         }
1090
1091         if (_session) {
1092                 solo_cut_control->set_controllable (_session->solo_cut_control());
1093                 solo_cut_display->set_controllable (_session->solo_cut_control());
1094         } else {
1095                 solo_cut_control->set_controllable (none);
1096                 solo_cut_display->set_controllable (none);
1097         }
1098
1099         if (_route) {
1100                 gain_control->set_controllable (_route->gain_control());
1101                 gain_display->set_controllable (_route->gain_control());
1102         } else {
1103                 gain_control->set_controllable (none);
1104         }
1105
1106         if (_monitor) {
1107
1108                 cut_all_button.set_controllable (_monitor->cut_control());
1109                 cut_all_button.watch ();
1110                 dim_all_button.set_controllable (_monitor->dim_control());
1111                 dim_all_button.watch ();
1112                 mono_button.set_controllable (_monitor->mono_control());
1113                 mono_button.watch ();
1114
1115                 dim_control->set_controllable (_monitor->dim_level_control ());
1116                 dim_display->set_controllable (_monitor->dim_level_control ());
1117                 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1118                 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1119
1120         } else {
1121
1122                 cut_all_button.set_controllable (none);
1123                 dim_all_button.set_controllable (none);
1124                 mono_button.set_controllable (none);
1125
1126                 dim_control->set_controllable (none);
1127                 dim_display->set_controllable (none);
1128                 solo_boost_control->set_controllable (none);
1129                 solo_boost_display->set_controllable (none);
1130         }
1131 }
1132
1133 string
1134 MonitorSection::state_id() const
1135 {
1136         return "monitor-section";
1137 }