MIDI/Controllables for monitor section, and related fixes
[ardour.git] / gtk2_ardour / monitor_section.cc
1 #include <gdkmm/pixbuf.h>
2
3 #include "pbd/compose.h"
4 #include "pbd/error.h"
5
6 #include "gtkmm2ext/bindable_button.h"
7 #include "gtkmm2ext/tearoff.h"
8 #include "gtkmm2ext/actions.h"
9
10 #include "ardour/dB.h"
11 #include "ardour/monitor_processor.h"
12 #include "ardour/route.h"
13 #include "ardour/utils.h"
14
15 #include "ardour_ui.h"
16 #include "gui_thread.h"
17 #include "monitor_section.h"
18 #include "public_editor.h"
19 #include "utils.h"
20 #include "volume_controller.h"
21
22 #include "i18n.h"
23
24 using namespace ARDOUR;
25 using namespace Gtk;
26 using namespace Gtkmm2ext;
27 using namespace PBD;
28 using namespace std;
29
30 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
31 Glib::RefPtr<Gdk::Pixbuf> MonitorSection::big_knob_pixbuf;
32 Glib::RefPtr<Gdk::Pixbuf> MonitorSection::little_knob_pixbuf;
33
34 MonitorSection::MonitorSection (Session* s)
35         : AxisView (s)
36         , RouteUI (s)
37         , main_table (2, 3)
38         , _tearoff (0)
39         , gain_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1) // initial value is unity gain
40         , gain_control (0)
41         , dim_adjustment (0.2, 0.0, 1.0, 0.01, 0.1) // upper+lower will be reset to match model
42         , dim_control (0)
43         , solo_boost_adjustment (1.0, 1.0, 3.0, 0.01, 0.1)  // upper and lower will be reset to match model
44         , solo_boost_control (0)
45         , solo_cut_adjustment (0.0, 0.0, 1.0, 0.01, 0.1) // upper and lower will be reset to match model
46         , solo_cut_control (0)
47         , solo_in_place_button (solo_model_group, _("SiP"))
48         , afl_button (solo_model_group, _("AFL"))
49         , pfl_button (solo_model_group, _("PFL"))
50         , cut_all_button (_("MUTE"))
51         , dim_all_button (_("dim"))
52         , mono_button (_("mono"))
53         , rude_solo_button (_("soloing"))
54
55 {
56         Glib::RefPtr<Action> act;
57
58         if (!monitor_actions) {
59
60                 /* do some static stuff */
61
62                 register_actions ();
63
64         }
65         
66         set_session (s);
67
68         VBox* spin_packer;
69         Label* spin_label;
70
71         /* Dim */
72
73         dim_control = new VolumeController (little_knob_pixbuf, &dim_adjustment, false, 30, 30);
74
75         HBox* dim_packer = manage (new HBox);
76         dim_packer->show ();
77
78         spin_label = manage (new Label (_("Dim Cut")));
79         spin_packer = manage (new VBox);
80         spin_packer->show ();
81         spin_packer->set_spacing (6);
82         spin_packer->pack_start (*dim_control, false, false);
83         spin_packer->pack_start (*spin_label, false, false); 
84
85         dim_packer->set_spacing (12);
86         dim_packer->pack_start (*spin_packer, true, true);
87
88         /* Rude Solo */
89
90         rude_solo_button.set_name ("TransportSoloAlert");
91         rude_solo_button.show ();
92
93         ARDOUR_UI::Blink.connect (sigc::mem_fun (*this, &MonitorSection::solo_blink));
94         rude_solo_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_solo), false);
95         UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
96
97
98         solo_model_box.set_spacing (6);
99         solo_model_box.pack_start (solo_in_place_button, false, false);
100         solo_model_box.pack_start (afl_button, false, false);
101         solo_model_box.pack_start (pfl_button, false, false);
102
103         solo_in_place_button.show ();
104         afl_button.show ();
105         pfl_button.show ();
106         solo_model_box.show ();
107
108         act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
109         if (act) {
110                 act->connect_proxy (solo_in_place_button);
111         } 
112
113         act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
114         if (act) {
115                 act->connect_proxy (afl_button);
116         } 
117
118         act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
119         if (act) {
120                 act->connect_proxy (pfl_button);
121         } 
122
123
124         /* Solo Boost */
125
126         solo_boost_control = new VolumeController (little_knob_pixbuf, &solo_boost_adjustment, false, 30, 30);
127
128         HBox* solo_packer = manage (new HBox);
129         solo_packer->set_spacing (12);
130         solo_packer->show ();
131
132         spin_label = manage (new Label (_("Solo Boost")));
133         spin_packer = manage (new VBox);
134         spin_packer->show ();
135         spin_packer->set_spacing (6);
136         spin_packer->pack_start (*solo_boost_control, false, false);
137         spin_packer->pack_start (*spin_label, false, false); 
138
139         solo_packer->pack_start (*spin_packer, true, true);
140
141         /* Solo (SiP) cut */
142
143         solo_cut_control = new VolumeController (little_knob_pixbuf, &solo_cut_adjustment, false, 30, 30);
144
145         spin_label = manage (new Label (_("SiP Cut")));
146         spin_packer = manage (new VBox);
147         spin_packer->show ();
148         spin_packer->set_spacing (6);
149         spin_packer->pack_start (*solo_cut_control, false, false);
150         spin_packer->pack_start (*spin_label, false, false); 
151
152         solo_packer->pack_start (*spin_packer, true, true);
153
154         upper_packer.set_spacing (12);
155         upper_packer.pack_start (rude_solo_button, false, false);
156         upper_packer.pack_start (solo_model_box, false, false);
157         upper_packer.pack_start (*solo_packer, false, false);
158
159         act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
160         if (act) {
161                 act->connect_proxy (cut_all_button);
162         } 
163
164         act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
165         if (act) {
166                 act->connect_proxy (dim_all_button);
167         } 
168
169         act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
170         if (act) {
171                 act->connect_proxy (mono_button);
172         } 
173
174         cut_all_button.set_name (X_("MonitorMuteButton"));
175         cut_all_button.unset_flags (Gtk::CAN_FOCUS);
176         cut_all_button.set_size_request (50,50);
177         cut_all_button.show ();
178
179         HBox* bbox = manage (new HBox);
180
181         bbox->set_spacing (12);
182         bbox->pack_start (mono_button, true, true);
183         bbox->pack_start (dim_all_button, true, true);
184
185         dim_all_button.set_name (X_("MonitorDimButton"));
186         dim_all_button.unset_flags (Gtk::CAN_FOCUS);
187         mono_button.set_name (X_("MonitorMonoButton"));
188         mono_button.unset_flags (Gtk::CAN_FOCUS);
189
190         lower_packer.set_spacing (12);
191         lower_packer.pack_start (*bbox, false, false);
192         lower_packer.pack_start (cut_all_button, false, false);
193
194         /* Gain */
195
196         gain_control = new VolumeController (big_knob_pixbuf, &gain_adjustment, false, 80, 80);
197
198         spin_label = manage (new Label (_("Gain")));
199         spin_packer = manage (new VBox);
200         spin_packer->show ();
201         spin_packer->set_spacing (6);
202         spin_packer->pack_start (*gain_control, false, false);
203         spin_packer->pack_start (*spin_label, false, false);
204
205         lower_packer.pack_start (*spin_packer, true, true);
206
207         vpacker.set_border_width (12);
208         vpacker.set_spacing (12);
209         vpacker.pack_start (upper_packer, false, false);
210         vpacker.pack_start (*dim_packer, false, false);
211         vpacker.pack_start (main_table, false, false);
212         vpacker.pack_start (lower_packer, false, false);
213
214         hpacker.set_border_width (12);
215         hpacker.set_spacing (12);
216         hpacker.pack_start (vpacker, true, true);
217
218         gain_control->show_all ();
219         dim_control->show_all ();
220         solo_boost_control->show_all ();
221
222         main_table.show ();
223         hpacker.show ();
224         upper_packer.show ();
225         lower_packer.show ();
226         vpacker.show ();
227
228         populate_buttons ();
229         map_state ();
230         assign_controllables ();
231
232         _tearoff = new TearOff (hpacker);
233
234         /* if torn off, make this a normal window */
235         _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
236         _tearoff->tearoff_window().set_title (X_("Monitor"));
237         _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
238
239         /* catch changes that affect us */
240
241         Config->ParameterChanged.connect (config_connection, invalidator (*this), ui_bind (&MonitorSection::parameter_changed, this, _1), gui_context());
242 }
243
244 MonitorSection::~MonitorSection ()
245 {
246         for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
247                 delete *i;
248         }
249
250         _channel_buttons.clear ();
251
252         delete gain_control;
253         delete dim_control;
254         delete solo_boost_control;
255         delete _tearoff;
256 }
257
258 void
259 MonitorSection::set_session (Session* s)
260 {
261         AxisView::set_session (s);
262
263         if (_session) {
264
265                 _route = _session->monitor_out ();
266
267                 if (_route) {
268                         /* session with control outs */
269                         _monitor = _route->monitor_control ();
270                         assign_controllables ();
271                 } else { 
272                         /* session with no control outs */
273                         _monitor.reset ();
274                         _route.reset ();
275                 }
276                 
277                         
278         } else {
279                 /* no session */
280                 _monitor.reset ();
281                 _route.reset ();
282                 control_connections.drop_connections ();
283         }
284
285         /* both might be null */
286 }
287
288 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
289         : cut (X_(""))
290         , dim (X_(""))
291         , solo (X_(""))
292         , invert (X_(""))
293 {
294         cut.set_name (X_("MonitorMuteButton"));
295         dim.set_name (X_("MonitorDimButton"));
296         solo.set_name (X_("MixerSoloButton"));
297         invert.set_name (X_("MonitorInvertButton"));
298
299         gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (cut.gobj()), false);
300         gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (dim.gobj()), false);
301         gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (invert.gobj()), false);
302         gtk_activatable_set_use_action_appearance (GTK_ACTIVATABLE (solo.gobj()), false);
303
304         cut.unset_flags (Gtk::CAN_FOCUS);
305         dim.unset_flags (Gtk::CAN_FOCUS);
306         solo.unset_flags (Gtk::CAN_FOCUS);
307         invert.unset_flags (Gtk::CAN_FOCUS);
308 }
309
310 void
311 MonitorSection::populate_buttons ()
312 {
313         if (!_monitor) {
314                 return;
315         }
316
317         Glib::RefPtr<Action> act;
318         uint32_t nchans = _monitor->output_streams().n_audio();
319         
320         main_table.resize (nchans+1, 5);
321         main_table.set_col_spacings (6);
322         main_table.set_row_spacings (6);
323         main_table.set_homogeneous (true);
324
325         Label* l1 = manage (new Label (X_("out")));
326         main_table.attach (*l1, 0, 1, 0, 1, SHRINK|FILL, SHRINK|FILL);
327         l1 = manage (new Label (X_("cut")));
328         main_table.attach (*l1, 1, 2, 0, 1, SHRINK|FILL, SHRINK|FILL);
329         l1 = manage (new Label (X_("dim")));
330         main_table.attach (*l1, 2, 3, 0, 1, SHRINK|FILL, SHRINK|FILL);
331         l1 = manage (new Label (X_("solo")));
332         main_table.attach (*l1, 3, 4, 0, 1, SHRINK|FILL, SHRINK|FILL);
333         l1 = manage (new Label (X_("inv")));
334         main_table.attach (*l1, 4, 5, 0, 1, SHRINK|FILL, SHRINK|FILL);
335
336         const uint32_t row_offset = 1;
337
338         for (uint32_t i = 0; i < nchans; ++i) {
339                 
340                 string l;
341                 char buf[64];
342
343                 if (nchans == 2) {
344                         if (i == 0) {
345                                 l = "L";
346                         } else {
347                                 l = "R";
348                         }
349                 } else {
350                         char buf[32];
351                         snprintf (buf, sizeof (buf), "%d", i+1);
352                         l = buf;
353                 }
354
355                 Label* label = manage (new Label (l));
356                 main_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
357
358                 ChannelButtonSet* cbs = new ChannelButtonSet;
359
360                 _channel_buttons.push_back (cbs);
361
362                 main_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
363                 main_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL); 
364                 main_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
365                 main_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, SHRINK|FILL, SHRINK|FILL);
366                
367                 snprintf (buf, sizeof (buf), "monitor-cut-%u", i+1);
368                 act = ActionManager::get_action (X_("Monitor"), buf);
369                 if (act) {
370                         act->connect_proxy (cbs->cut);
371                 } 
372
373                 snprintf (buf, sizeof (buf), "monitor-dim-%u", i+1);
374                 act = ActionManager::get_action (X_("Monitor"), buf);
375                 if (act) {
376                         act->connect_proxy (cbs->dim);
377                 }
378
379                 snprintf (buf, sizeof (buf), "monitor-solo-%u", i+1);
380                 act = ActionManager::get_action (X_("Monitor"), buf);
381                 if (act) {
382                         act->connect_proxy (cbs->solo);
383                 }
384
385                 snprintf (buf, sizeof (buf), "monitor-invert-%u", i+1);
386                 act = ActionManager::get_action (X_("Monitor"), buf);
387                 if (act) {
388                         act->connect_proxy (cbs->invert);
389                 }
390         }
391
392         main_table.show_all ();
393 }
394
395 void 
396 MonitorSection::set_button_names ()
397 {
398         rec_enable_button_label.set_text ("rec");
399         mute_button_label.set_text ("rec");
400         solo_button_label.set_text ("rec");
401 }
402
403 void
404 MonitorSection::dim_all ()
405 {
406         if (!_monitor) {
407                 return;
408         }
409
410         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
411         if (act) {
412                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
413                 _monitor->set_dim_all (tact->get_active());
414         }
415
416 }
417
418 void
419 MonitorSection::cut_all ()
420 {
421         if (!_monitor) {
422                 return;
423         }
424
425         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
426         if (act) {
427                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
428                 _monitor->set_cut_all (tact->get_active());
429         }
430 }
431
432 void
433 MonitorSection::mono ()
434 {
435         if (!_monitor) {
436                 return;
437         }
438
439         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
440         if (act) {
441                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
442                 _monitor->set_mono (tact->get_active());
443         }
444 }
445
446 void
447 MonitorSection::cut_channel (uint32_t chn)
448 {
449         if (!_monitor) {
450                 return;
451         }
452
453         char buf[64];
454         snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
455
456         --chn; // 0-based in backend
457
458         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
459         if (act) {
460                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
461                 _monitor->set_cut (chn, tact->get_active());
462         }
463 }
464
465 void
466 MonitorSection::dim_channel (uint32_t chn)
467 {
468         if (!_monitor) {
469                 return;
470         }
471
472         char buf[64];
473         snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
474
475         --chn; // 0-based in backend
476
477         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
478         if (act) {
479                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
480                 _monitor->set_dim (chn, tact->get_active());
481         }
482
483 }
484
485 void
486 MonitorSection::solo_channel (uint32_t chn)
487 {
488         if (!_monitor) {
489                 return;
490         }
491
492         char buf[64];
493         snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
494
495         --chn; // 0-based in backend
496
497         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
498         if (act) {
499                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
500                 _monitor->set_solo (chn, tact->get_active());
501         }
502
503 }
504
505 void
506 MonitorSection::invert_channel (uint32_t chn)
507 {
508         if (!_monitor) {
509                 return;
510         }
511
512         char buf[64];
513         snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
514
515         --chn; // 0-based in backend
516
517         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
518         if (act) {
519                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
520                 _monitor->set_polarity (chn, tact->get_active());
521         } 
522 }
523
524 void
525 MonitorSection::register_actions ()
526 {
527         string action_name;
528         string action_descr;
529
530         monitor_actions = ActionGroup::create (X_("Monitor"));
531         ActionManager::add_action_group (monitor_actions);
532
533         ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", 
534                                                sigc::mem_fun (*this, &MonitorSection::mono));
535
536         ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", 
537                                                sigc::mem_fun (*this, &MonitorSection::cut_all));
538
539         ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", 
540                                                sigc::mem_fun (*this, &MonitorSection::dim_all));
541
542         /* note the 1-based counting (for naming - backend uses 0-based) */
543
544         for (uint32_t chn = 1; chn <= 16; ++chn) {
545
546                 /* for the time being, do not use the action description because it always
547                    shows up in the buttons, which is undesirable.
548                 */
549
550                 action_name = string_compose (X_("monitor-cut-%1"), chn);
551                 action_descr = string_compose (_("Cut Monitor Chn %1"), chn);
552                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", 
553                                                        sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
554
555                 action_name = string_compose (X_("monitor-dim-%1"), chn);
556                 action_descr = string_compose (_("Dim Monitor Chn %1"), chn+1);
557                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", 
558                                                        sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
559
560                 action_name = string_compose (X_("monitor-solo-%1"), chn);
561                 action_descr = string_compose (_("Solo Monitor Chn %1"), chn);
562                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", 
563                                                        sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
564
565                 action_name = string_compose (X_("monitor-invert-%1"), chn);
566                 action_descr = string_compose (_("Invert Monitor Chn %1"), chn);
567                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", 
568                                                        sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
569
570         }
571
572
573         Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
574         RadioAction::Group solo_group;
575
576         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "",
577                                               sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
578         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "",
579                                               sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
580         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "",
581                                               sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
582
583         ActionManager::add_action_group (solo_actions);
584 }
585
586 void
587 MonitorSection::solo_use_in_place ()
588 {
589         /* this is driven by a toggle on a radio group, and so is invoked twice,
590            once for the item that became inactive and once for the one that became
591            active.
592         */
593
594         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
595
596         if (act) {
597                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
598                 if (ract) {
599                         Config->set_solo_control_is_listen_control (!ract->get_active());
600                 }
601         }
602 }
603
604 void
605 MonitorSection::solo_use_afl ()
606 {
607         /* this is driven by a toggle on a radio group, and so is invoked twice,
608            once for the item that became inactive and once for the one that became
609            active.
610         */
611         
612         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
613         if (act) {
614                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
615                 if (ract) {
616                         if (ract->get_active()) {
617                                 Config->set_listen_position (AfterFaderListen);
618                                 Config->set_solo_control_is_listen_control (true);
619                         }
620                 }
621         }
622 }
623
624 void
625 MonitorSection::solo_use_pfl ()
626 {
627         /* this is driven by a toggle on a radio group, and so is invoked twice,
628            once for the item that became inactive and once for the one that became
629            active.
630         */
631
632         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
633         if (act) {
634                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
635                 if (ract) {
636                         if (ract->get_active()) {
637                                 Config->set_listen_position (PreFaderListen);
638                                 Config->set_solo_control_is_listen_control (true);
639                         }
640                 }
641         }
642 }
643
644 void
645 MonitorSection::setup_knob_images ()
646 {
647         try {
648                 
649                 big_knob_pixbuf = ::get_icon ("bigknob");
650                 
651         }  catch (...) {
652                 
653                 error << "No knob image found (or not loadable) at "
654                       << " .... "
655                       << endmsg;
656                 throw failed_constructor ();
657         }
658         
659         try {
660                 
661                 little_knob_pixbuf = ::get_icon ("littleknob");
662                 
663         }  catch (...) {
664                 
665                 error << "No knob image found (or not loadable) at "
666                       << " .... "
667                       << endmsg;
668                 throw failed_constructor ();
669         }
670 }
671
672 bool
673 MonitorSection::nonlinear_gain_printer (SpinButton* button)
674 {
675         double val = button->get_adjustment()->get_value();
676         char buf[16];
677         snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (slider_position_to_gain (val)));
678         button->set_text (buf);
679         return true;
680 }
681
682 bool
683 MonitorSection::linear_gain_printer (SpinButton* button)
684 {
685         double val = button->get_adjustment()->get_value();
686         char buf[16];
687         snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (val));
688         button->set_text (buf);
689         return true;
690 }
691
692 void
693 MonitorSection::update_solo_model ()
694 {
695         const char* action_name;
696         Glib::RefPtr<Action> act;
697
698         if (Config->get_solo_control_is_listen_control()) {
699                 switch (Config->get_listen_position()) {
700                 case AfterFaderListen:
701                         action_name = X_("solo-use-afl");
702                         break;
703                 case PreFaderListen:
704                         action_name = X_("solo-use-pfl");
705                         break;
706                 }
707         } else {
708                 action_name = X_("solo-use-in-place");
709         }
710
711         act = ActionManager::get_action (X_("Solo"), action_name);
712         if (act) {
713                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
714                 if (ract) {
715                         ract->set_active (true);
716                 }
717         }
718 }
719
720 void
721 MonitorSection::map_state ()
722 {
723         if (!_route || !_monitor) {
724                 return;
725         }
726
727         gain_control->get_adjustment()->set_value (gain_to_slider_position (_route->gain_control()->get_value()));
728         dim_control->get_adjustment()->set_value (_monitor->dim_level());
729         solo_boost_control->get_adjustment()->set_value (_monitor->solo_boost_level());
730
731         Glib::RefPtr<Action> act;
732
733         update_solo_model ();
734         
735         act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
736         if (act) {
737                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
738                 if (tact) {
739                         cerr << "Set monitor cut all action to " << _monitor->cut_all () << endl;
740                         tact->set_active (_monitor->cut_all());
741                 } else {
742                         cerr << " no global cut action\n";
743                 }
744         } else {
745                 cerr << " no global cut action2\n";
746         }
747
748         act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
749         if (act) {
750                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
751                 if (tact) {
752                         tact->set_active (_monitor->dim_all());
753                 }
754         }
755         
756         act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
757         if (act) {
758                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
759                 if (tact) {
760                         tact->set_active (_monitor->mono());
761                 }
762         }
763
764         uint32_t nchans = _monitor->output_streams().n_audio();
765
766         assert (nchans == _channel_buttons.size ());
767
768         for (uint32_t n = 0; n < nchans; ++n) {
769
770                 char action_name[32];
771
772                 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n+1);
773                 act = ActionManager::get_action (X_("Monitor"), action_name);
774                 if (act) {
775                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
776                         if (tact) {
777                                 tact->set_active (_monitor->cut (n));
778                         }
779                 }
780
781                 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n+1);
782                 act = ActionManager::get_action (X_("Monitor"), action_name);
783                 if (act) {
784                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
785                         if (tact) {
786                                 tact->set_active (_monitor->dimmed (n));
787                         }
788                 }
789
790                 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n+1);
791                 act = ActionManager::get_action (X_("Monitor"), action_name);
792                 if (act) {
793                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
794                         if (tact) {
795                                 tact->set_active (_monitor->soloed (n));
796                         }
797                 }
798
799                 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n+1);
800                 act = ActionManager::get_action (X_("Monitor"), action_name);
801                 if (act) {
802                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
803                         if (tact) {
804                                 tact->set_active (_monitor->inverted (n));
805                         }
806                 }
807         }
808 }
809
810 void
811 MonitorSection::solo_blink (bool onoff)
812 {
813         if (_session == 0) {
814                 return;
815         }
816
817         if (_session->soloing() || _session->listening()) {
818                 if (onoff) {
819                         rude_solo_button.set_state (STATE_ACTIVE);
820                 } else {
821                         rude_solo_button.set_state (STATE_NORMAL);
822                 }
823         } else {
824                 // rude_solo_button.set_active (false);
825                 rude_solo_button.set_state (STATE_NORMAL);
826         }
827 }
828
829 bool
830 MonitorSection::cancel_solo (GdkEventButton* ev)
831 {
832         if (_session) {
833                 if (_session->soloing()) {
834                         _session->set_solo (_session->get_routes(), false);
835                 } else if (_session->listening()) {
836                         _session->set_listen (_session->get_routes(), false);
837                 }
838         }
839
840         return true;
841 }
842
843 void
844 MonitorSection::solo_cut_changed ()
845 {
846         Config->set_solo_mute_gain (slider_position_to_gain (solo_cut_adjustment.get_value()));
847 }
848
849 void
850 MonitorSection::parameter_changed (std::string name)
851 {
852         if (name == "solo-control-is-listen-control" ||
853             name == "listen-position") {
854                 update_solo_model ();
855         } else if (name == "solo-mute-gain") {
856                 solo_cut_adjustment.set_value (gain_to_slider_position (Config->get_solo_mute_gain()));
857         }
858 }
859
860 void
861 MonitorSection::assign_controllables ()
862 {
863         boost::shared_ptr<Controllable> none;
864
865         if (!gain_control) {
866                 /* too early - GUI controls not set up yet */
867                 return;
868         }
869
870         if (_route) {
871                 gain_control->set_controllable (_route->gain_control());
872                 control_link (control_connections, _route->gain_control(), gain_adjustment);
873         } else {
874                 gain_control->set_controllable (none);
875         }
876
877         if (_monitor) {
878
879                 cut_all_button.set_controllable (_monitor->cut_control());
880                 cut_all_button.watch ();
881                 dim_all_button.set_controllable (_monitor->dim_control());
882                 dim_all_button.watch ();
883                 mono_button.set_controllable (_monitor->mono_control());
884                 mono_button.watch ();
885
886                 boost::shared_ptr<Controllable> c (_monitor->dim_level_control ());
887
888                 dim_control->set_controllable (c);
889                 dim_adjustment.set_lower (c->lower());
890                 dim_adjustment.set_upper (c->upper());
891                 control_link (control_connections, c, dim_adjustment);
892                 
893                 c = _monitor->solo_boost_control ();
894                 solo_boost_control->set_controllable (c);
895                 solo_boost_adjustment.set_lower (c->lower());
896                 solo_boost_adjustment.set_upper (c->upper());
897                 control_link (control_connections, c, solo_boost_adjustment);
898
899         } else {
900
901                 cut_all_button.set_controllable (none);
902                 dim_all_button.set_controllable (none);
903                 mono_button.set_controllable (none);
904
905                 dim_control->set_controllable (none);
906                 solo_boost_control->set_controllable (none);
907         }
908 }