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