d940258011e5af6ab927a38d7341eff4e4ea84de
[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 #include "pbd/replace_all.h"
25
26 #include "gtkmm2ext/bindable_button.h"
27 #include "gtkmm2ext/tearoff.h"
28 #include "gtkmm2ext/actions.h"
29 #include "gtkmm2ext/utils.h"
30
31 #include <gtkmm/menu.h>
32 #include <gtkmm/menuitem.h>
33
34 #include "ardour/amp.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/monitor_processor.h"
37 #include "ardour/port.h"
38 #include "ardour/route.h"
39 #include "ardour/user_bundle.h"
40 #include "ardour/plugin_manager.h"
41
42 #include "gui_thread.h"
43 #include "monitor_section.h"
44 #include "public_editor.h"
45 #include "timers.h"
46 #include "tooltips.h"
47 #include "ui_config.h"
48 #include "utils.h"
49
50 #include "i18n.h"
51
52 using namespace ARDOUR;
53 using namespace ARDOUR_UI_UTILS;
54 using namespace Gtk;
55 using namespace Gtkmm2ext;
56 using namespace PBD;
57 using namespace std;
58
59 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
60
61 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
62
63 MonitorSection::MonitorSection (Session* s)
64         : AxisView (s)
65         , RouteUI (s)
66         , _tearoff (0)
67         , channel_table_viewport (*channel_table_scroller.get_hadjustment()
68         , *channel_table_scroller.get_vadjustment ())
69         , gain_control (0)
70         , dim_control (0)
71         , solo_boost_control (0)
72         , solo_cut_control (0)
73         , gain_display (0)
74         , dim_display (0)
75         , solo_boost_display (0)
76         , solo_cut_display (0)
77         , _output_selector (0)
78         , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
79         , afl_button (_("AFL"), ArdourButton::led_default_elements)
80         , pfl_button (_("PFL"), ArdourButton::led_default_elements)
81         , exclusive_solo_button (ArdourButton::led_default_elements)
82         , solo_mute_override_button (ArdourButton::led_default_elements)
83         , toggle_processorbox_button (ArdourButton::default_elements)
84         , _inhibit_solo_model_update (false)
85         , _ui_initialized (false)
86 {
87
88         using namespace Menu_Helpers;
89
90         Glib::RefPtr<Action> act;
91
92         if (!monitor_actions) {
93                 register_actions ();
94         } else {
95                 connect_actions ();
96         }
97
98         _plugin_selector = new PluginSelector (PluginManager::instance());
99         insert_box = new ProcessorBox (_session, boost::bind (&MonitorSection::plugin_selector, this), _rr_selection, 0);
100         insert_box->set_no_show_all ();
101         insert_box->show ();
102         // TODO allow keyboard shortcuts in ProcessorBox
103
104         set_session (s);
105
106         /* Rude Solo  & Solo Isolated */
107         rude_solo_button.set_text (_("Soloing"));
108         rude_solo_button.set_name ("rude solo");
109         rude_solo_button.show ();
110
111         rude_iso_button.set_text (_("Isolated"));
112         rude_iso_button.set_name ("rude isolate");
113         rude_iso_button.show ();
114
115         rude_audition_button.set_text (_("Auditioning"));
116         rude_audition_button.set_name ("rude audition");
117         rude_audition_button.show ();
118
119         Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
120
121         act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
122         rude_solo_button.set_related_action (act);
123         UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
124
125         rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
126         UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
127
128         rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
129         UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
130
131         /* SIP, AFL, PFL radio */
132
133         solo_in_place_button.set_name ("monitor section solo model");
134         afl_button.set_name ("monitor section solo model");
135         pfl_button.set_name ("monitor section solo model");
136
137         solo_in_place_button.set_led_left (true);
138         afl_button.set_led_left (true);
139         pfl_button.set_led_left (true);
140
141         solo_in_place_button.show ();
142         afl_button.show ();
143         pfl_button.show ();
144
145         act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
146         set_tooltip (solo_in_place_button, _("Solo controls affect solo-in-place"));
147         if (act) {
148                 solo_in_place_button.set_related_action (act);
149         }
150
151         act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
152         set_tooltip (afl_button, _("Solo controls toggle after-fader-listen"));
153         if (act) {
154                 afl_button.set_related_action (act);
155         }
156
157         act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
158         set_tooltip (pfl_button, _("Solo controls toggle pre-fader-listen"));
159         if (act) {
160                 pfl_button.set_related_action (act);
161         }
162
163         /* Solo option buttons */
164         exclusive_solo_button.set_text (_("Excl. Solo"));
165         exclusive_solo_button.set_name (X_("monitor section solo option"));
166         set_tooltip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
167
168         act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
169         if (act) {
170                 exclusive_solo_button.set_related_action (act);
171         }
172
173         solo_mute_override_button.set_text (_("Solo ยป Mute"));
174         solo_mute_override_button.set_name (X_("monitor section solo option"));
175         set_tooltip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
176
177         act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
178         if (act) {
179                 solo_mute_override_button.set_related_action (act);
180         }
181
182         /* Processor Box hide/shos */
183         toggle_processorbox_button.set_text (_("Processors"));
184         toggle_processorbox_button.set_name (X_("monitor section processors toggle"));
185         set_tooltip (&toggle_processorbox_button, _("Allow to add monitor effect processors"));
186
187         proctoggle = ToggleAction::create ();
188         toggle_processorbox_button.set_related_action (proctoggle);
189         proctoggle->signal_toggled().connect (sigc::mem_fun(*this, &MonitorSection::update_processor_box), false);
190
191         /* Knobs */
192         Label* solo_boost_label;
193         Label* solo_cut_label;
194         Label* dim_label;
195
196         /* Solo Boost Knob */
197
198         solo_boost_control = new ArdourKnob ();
199         solo_boost_control->set_name("monitor section knob");
200         solo_boost_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
201         set_tooltip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
202
203         solo_boost_display = new ArdourDisplay ();
204         solo_boost_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
205         solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
206         solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
207         solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
208         solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
209
210         solo_boost_label = manage (new Label (_("Solo Boost")));
211
212         /* Solo (SiP) cut */
213
214         solo_cut_control = new ArdourKnob ();
215         solo_cut_control->set_name ("monitor section knob");
216         solo_cut_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
217         set_tooltip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
218
219         solo_cut_display = new ArdourDisplay ();
220         solo_cut_display->set_name("monitor section dropdown"); // XXX
221         solo_cut_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
222         solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
223         solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
224         solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
225         solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
226         solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
227
228         solo_cut_label = manage (new Label (_("SiP Cut")));
229
230         /* Dim */
231
232         dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
233         dim_control->set_name ("monitor section knob");
234         dim_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
235         set_tooltip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
236
237         dim_display = new ArdourDisplay ();
238         dim_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
239         dim_display->add_controllable_preset(_("0 dB"), 0.0);
240         dim_display->add_controllable_preset(_("-3 dB"), -3.0);
241         dim_display->add_controllable_preset(_("-6 dB"), -6.0);
242         dim_display->add_controllable_preset(_("-12 dB"), -12.0);
243         dim_display->add_controllable_preset(_("-20 dB"), -20.0);
244
245         dim_label = manage (new Label (_("Dim")));
246
247         // mute button
248         cut_all_button.set_text (_("Mute"));
249         cut_all_button.set_name ("mute button");
250         cut_all_button.set_size_request (-1, PX_SCALE(30));
251         cut_all_button.show ();
252
253         act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
254         if (act) {
255                 cut_all_button.set_related_action (act);
256         }
257
258         // dim button
259         dim_all_button.set_text (_("Dim"));
260         dim_all_button.set_name ("monitor section dim");
261         dim_all_button.set_size_request (-1, PX_SCALE(25));
262         act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
263         if (act) {
264                 dim_all_button.set_related_action (act);
265         }
266
267         // mono button
268         mono_button.set_text (_("Mono"));
269         mono_button.set_name ("monitor section mono");
270         mono_button.set_size_request (-1, PX_SCALE(25));
271         act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
272         if (act) {
273                 mono_button.set_related_action (act);
274         }
275
276         /* Gain */
277
278         gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
279         gain_control->set_name("monitor section knob");
280         gain_control->set_size_request (PX_SCALE(60), PX_SCALE(60));
281
282         gain_display = new ArdourDisplay ();
283         gain_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
284         gain_display->add_controllable_preset(_("0 dB"), 0.0);
285         gain_display->add_controllable_preset(_("-3 dB"), -3.0);
286         gain_display->add_controllable_preset(_("-6 dB"), -6.0);
287         gain_display->add_controllable_preset(_("-12 dB"), -12.0);
288         gain_display->add_controllable_preset(_("-20 dB"), -20.0);
289         gain_display->add_controllable_preset(_("-30 dB"), -30.0);
290
291         Label* output_label = manage (new Label (_("Output")));
292         output_label->set_name (X_("MonitorSectionLabel"));
293
294         output_button = new ArdourButton ();
295         output_button->set_text (_("Output"));
296         output_button->set_name (X_("monitor section cut")); // XXX
297         output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
298         output_button->set_layout_ellipsize_width (PX_SCALE(128) * PANGO_SCALE);
299
300         channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
301         channel_table_scroller.set_size_request (-1, PX_SCALE(150));
302         channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
303         channel_table_scroller.show ();
304         channel_table_scroller.add (channel_table_viewport);
305
306         channel_size_group  = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
307         channel_size_group->add_widget (channel_table_header);
308         channel_size_group->add_widget (channel_table);
309
310         channel_table_header.resize (1, 5);
311
312         Label* l1 = manage (new Label (X_("  ")));
313         l1->set_name (X_("MonitorSectionLabel"));
314         channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
315
316         l1 = manage (new Label (_("Mute")));
317         l1->set_name (X_("MonitorSectionLabel"));
318         channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
319
320         l1 = manage (new Label (_("Dim")));
321         l1->set_name (X_("MonitorSectionLabel"));
322         channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
323
324         l1 = manage (new Label (_("Solo")));
325         l1->set_name (X_("MonitorSectionLabel"));
326         channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
327
328         l1 = manage (new Label (_("Inv")));
329         l1->set_name (X_("MonitorSectionLabel"));
330         channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
331
332         channel_table_header.show ();
333
334
335         /****************************************************************************
336          * LAYOUT  top to bottom
337          */
338
339         // solo, iso information
340         HBox* rude_box = manage (new HBox);
341         rude_box->set_spacing (PX_SCALE(4));
342         rude_box->set_homogeneous (true);
343         rude_box->pack_start (rude_solo_button, true, true);
344         rude_box->pack_start (rude_iso_button, true, true);
345
346         // solo options (right align)
347         HBox* tbx1 = manage (new HBox);
348         tbx1->pack_end (exclusive_solo_button, false, false);
349
350         HBox* tbx2 = manage (new HBox);
351         tbx2->pack_end (solo_mute_override_button, false, false);
352
353         HBox* tbx3 = manage (new HBox);
354         tbx3->pack_end (toggle_processorbox_button, false, false);
355
356         HBox* tbx0 = manage (new HBox); // space
357
358         // combined solo mode (Sip, AFL, PFL) & solo options
359         Table *solo_tbl = manage (new Table);
360         solo_tbl->attach (solo_in_place_button,   0, 1, 0, 1, EXPAND|FILL, SHRINK, 0, 2);
361         solo_tbl->attach (pfl_button,             0, 1, 1, 2, EXPAND|FILL, SHRINK, 0, 2);
362         solo_tbl->attach (afl_button,             0, 1, 2, 3, EXPAND|FILL, SHRINK, 0, 2);
363         solo_tbl->attach (*tbx0,                  1, 2, 0, 3, EXPAND|FILL, SHRINK, 2, 2);
364         solo_tbl->attach (*tbx1,                  2, 3, 0, 1, EXPAND|FILL, SHRINK, 0, 2);
365         solo_tbl->attach (*tbx2,                  2, 3, 1, 2, EXPAND|FILL, SHRINK, 0, 2);
366         solo_tbl->attach (*tbx3,                  2, 3, 2, 3, EXPAND|FILL, SHRINK, 0, 2);
367
368         // boost, cut, dim  volume control
369         Table *level_tbl = manage (new Table);
370         level_tbl->attach (*solo_boost_label,    0, 2, 0, 1, EXPAND|FILL, SHRINK, 1, 2);
371         level_tbl->attach (*solo_boost_control,  0, 2, 1, 2, EXPAND|FILL, SHRINK, 1, 2);
372         level_tbl->attach (*solo_boost_display,  0, 2, 2, 3, EXPAND     , SHRINK, 1, 2);
373
374         level_tbl->attach (*solo_cut_label,      2, 4, 0, 1, EXPAND|FILL, SHRINK, 1, 2);
375         level_tbl->attach (*solo_cut_control,    2, 4, 1, 2, EXPAND|FILL, SHRINK, 1, 2);
376         level_tbl->attach (*solo_cut_display,    2, 4, 2, 3, EXPAND     , SHRINK, 1, 2);
377
378         level_tbl->attach (*dim_label,           1, 3, 3, 4, EXPAND|FILL, SHRINK, 1, 2);
379         level_tbl->attach (*dim_control,         1, 3, 4, 5, EXPAND|FILL, SHRINK, 1, 2);
380         level_tbl->attach (*dim_display,         1, 3, 5, 6, EXPAND     , SHRINK, 1, 2);
381
382         /* note that we don't pack the table_hpacker till later
383          * -> top level channel_table_packer */
384         table_hpacker.pack_start (channel_table, true, true);
385
386         // mono, dim
387         HBox* mono_dim_box = manage (new HBox);
388         mono_dim_box->set_spacing (PX_SCALE(4));
389         mono_dim_box->set_homogeneous (true);
390         mono_dim_box->pack_start (mono_button, true, true);
391         mono_dim_box->pack_end (dim_all_button, true, true);
392
393         // master gain 
394         Label* spin_label = manage (new Label (_("Monitor")));
395         VBox* spin_packer = manage (new VBox);
396         spin_packer->set_spacing (PX_SCALE(2));
397         spin_packer->pack_start (*spin_label, false, false);
398         spin_packer->pack_start (*gain_control, false, false);
399         spin_packer->pack_start (*gain_display, false, false);
400
401         master_packer.pack_start (*spin_packer, true, false);
402
403         // combined gain section (channels, mute, dim)
404         VBox* lower_packer = manage (new VBox);
405         lower_packer->pack_start (channel_table_header, false, false, PX_SCALE(0));
406         lower_packer->pack_start (channel_table_packer, false, false, PX_SCALE(8));
407         lower_packer->pack_start (*mono_dim_box,        false, false, PX_SCALE(2));
408         lower_packer->pack_start (cut_all_button,       false, false, PX_SCALE(2));
409
410                 // output port select
411         VBox* out_packer = manage (new VBox);
412         out_packer->set_spacing (PX_SCALE(2));
413         out_packer->pack_start (*output_label, false, false);
414         out_packer->pack_start (*output_button, false, false);
415
416         /****************************************************************************
417          * TOP LEVEL LAYOUT
418          */
419         vpacker.set_border_width (PX_SCALE(3));
420         vpacker.pack_start (*rude_box,            false, false, PX_SCALE(3));
421         vpacker.pack_start (rude_audition_button, false, false, 0);
422         vpacker.pack_start (*solo_tbl,            false, false, PX_SCALE(8));
423         vpacker.pack_start (*insert_box,          true,  true,  PX_SCALE(8));
424         vpacker.pack_start (*level_tbl,           false, false, PX_SCALE(8));
425         vpacker.pack_start (*lower_packer,        false, false, PX_SCALE(8));
426         vpacker.pack_start (master_packer,        false, false, PX_SCALE(10));
427         vpacker.pack_end   (*out_packer,          false, false, PX_SCALE(3));
428
429         hpacker.set_spacing (0);
430         hpacker.pack_start (vpacker, true, true);
431
432         gain_control->show_all ();
433         gain_display->show_all ();
434         dim_control->show_all ();
435         dim_display->show_all();
436         solo_boost_control->show_all ();
437         solo_boost_display->show_all();
438
439         mono_dim_box->show ();
440         spin_packer->show ();
441         master_packer.show ();
442         channel_table.show ();
443
444         rude_box->show();
445         solo_tbl->show_all();
446         level_tbl->show();
447         lower_packer->show ();
448         out_packer->show ();
449
450         vpacker.show ();
451         hpacker.show ();
452
453         populate_buttons ();
454         map_state ();
455         assign_controllables ();
456
457         output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
458         output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
459
460         _tearoff = new TearOff (hpacker);
461
462         if (!UIConfiguration::instance().get_floating_monitor_section()) {
463                 /* if torn off, make this a normal window
464                  * (default is WINDOW_TYPE_HINT_UTILITY in libs/gtkmm2ext/tearoff.cc)
465                  */
466                 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
467         }
468         _tearoff->tearoff_window().set_title (X_("Monitor"));
469         _tearoff->tearoff_window().signal_key_press_event().connect (sigc::ptr_fun (forward_key_press), false);
470
471         update_output_display ();
472         update_processor_box ();
473         _ui_initialized = true;
474
475         /* catch changes that affect us */
476         AudioEngine::instance()->PortConnectedOrDisconnected.connect (
477                 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
478                 );
479         Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
480 }
481
482 MonitorSection::~MonitorSection ()
483 {
484         for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
485                 delete *i;
486         }
487
488         _channel_buttons.clear ();
489         _output_changed_connection.disconnect ();
490
491         delete insert_box;
492         delete output_button;
493         delete gain_control;
494         delete gain_display;
495         delete dim_control;
496         delete dim_display;
497         delete solo_boost_control;
498         delete solo_boost_display;
499         delete solo_cut_control;
500         delete solo_cut_display;
501         delete _tearoff;
502         delete _output_selector;
503         _output_selector = 0;
504 }
505
506
507 void
508 MonitorSection::update_processor_box ()
509 {
510         bool show_processor_box = proctoggle->get_active ();
511
512         if (count_processors () > 0 && !show_processor_box) {
513                 toggle_processorbox_button.set_name (X_("monitor section processors present"));
514         } else {
515                 toggle_processorbox_button.set_name (X_("monitor section processors toggle"));
516         }
517
518         if (insert_box->is_visible() == show_processor_box) {
519                 return;
520         }
521
522         if (show_processor_box) {
523                 if (master_packer.get_parent()) {
524                         master_packer.get_parent()->remove (master_packer);
525                 }
526                 insert_box->show();
527                 vpacker.pack_start (master_packer,        false, false, PX_SCALE(10));
528         } else {
529                 if (master_packer.get_parent()) {
530                         master_packer.get_parent()->remove (master_packer);
531                 }
532                 insert_box->hide();
533                 vpacker.pack_start (master_packer,        true,  false, PX_SCALE(10));
534         }
535 }
536
537 void
538 MonitorSection::set_session (Session* s)
539 {
540         AxisView::set_session (s);
541         _plugin_selector->set_session (_session);
542
543         if (_session) {
544
545                 _route = _session->monitor_out ();
546
547                 if (_route) {
548                         /* session with monitor section */
549                         _monitor = _route->monitor_control ();
550                         assign_controllables ();
551                         _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
552                                                                                         boost::bind (&MonitorSection::update_output_display, this),
553                                                                                         gui_context());
554                         insert_box->set_route (_route);
555                         _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MonitorSection::processors_changed, this, _1), gui_context());
556                         if (_ui_initialized) {
557                                 update_processor_box ();
558                         }
559                 } else {
560                         /* session with no monitor section */
561                         _output_changed_connection.disconnect();
562                         _monitor.reset ();
563                         _route.reset ();
564                         delete _output_selector;
565                         _output_selector = 0;
566                 }
567
568                 if (channel_table_scroller.get_parent()) {
569                         /* scroller is packed, so remove it */
570                         channel_table_packer.remove (channel_table_scroller);
571                 }
572
573                 if (table_hpacker.get_parent () == &channel_table_packer) {
574                         /* this occurs when the table hpacker is directly
575                                  packed, so remove it.
576                                  */
577                         channel_table_packer.remove (table_hpacker);
578                 } else if (table_hpacker.get_parent()) {
579                         channel_table_viewport.remove ();
580                 }
581
582                 if (_monitor->output_streams().n_audio() > 7) {
583                         /* put the table into a scrolled window, and then put
584                          * that into the channel vpacker, after the table header
585                          */
586                         channel_table_viewport.add (table_hpacker);
587                         channel_table_packer.pack_start (channel_table_scroller, true, true);
588                         channel_table_viewport.show ();
589                         channel_table_scroller.show ();
590
591                 } else {
592                         /* just put the channel table itself into the channel
593                          * vpacker, after the table header
594                          */
595
596                         channel_table_packer.pack_start (table_hpacker, true, true);
597                         channel_table_scroller.hide ();
598                 }
599
600                 table_hpacker.show ();
601                 channel_table.show ();
602
603         } else {
604                 /* no session */
605
606                 _output_changed_connection.disconnect();
607                 _monitor.reset ();
608                 _route.reset ();
609                 control_connections.drop_connections ();
610                 rude_iso_button.unset_active_state ();
611                 rude_solo_button.unset_active_state ();
612                 delete _output_selector;
613                 _output_selector = 0;
614
615                 assign_controllables ();
616         }
617 }
618
619 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
620 {
621         cut.set_name (X_("mute button"));
622         dim.set_name (X_("monitor section dim"));
623         solo.set_name (X_("solo button"));
624         invert.set_name (X_("invert button"));
625
626         cut.unset_flags (Gtk::CAN_FOCUS);
627         dim.unset_flags (Gtk::CAN_FOCUS);
628         solo.unset_flags (Gtk::CAN_FOCUS);
629         invert.unset_flags (Gtk::CAN_FOCUS);
630 }
631
632         void
633 MonitorSection::populate_buttons ()
634 {
635         if (!_monitor) {
636                 return;
637         }
638
639         Glib::RefPtr<Action> act;
640         uint32_t nchans = _monitor->output_streams().n_audio();
641
642         channel_table.resize (nchans, 5);
643         channel_table.set_col_spacings (6);
644         channel_table.set_row_spacings (6);
645         channel_table.set_homogeneous (true);
646
647         const uint32_t row_offset = 0;
648
649         for (uint32_t i = 0; i < nchans; ++i) {
650
651                 string l;
652                 char buf[64];
653
654                 if (nchans == 2) {
655                         if (i == 0) {
656                                 l = "L";
657                         } else {
658                                 l = "R";
659                         }
660                 } else {
661                         char buf[32];
662                         snprintf (buf, sizeof (buf), "%d", i+1);
663                         l = buf;
664                 }
665
666                 Label* label = manage (new Label (l));
667                 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
668
669                 ChannelButtonSet* cbs = new ChannelButtonSet;
670
671                 _channel_buttons.push_back (cbs);
672
673                 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
674                 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
675                 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
676                 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
677
678                 snprintf (buf, sizeof (buf), "monitor-cut-%u", i);
679                 act = ActionManager::get_action (X_("Monitor"), buf);
680                 if (act) {
681                         cbs->cut.set_related_action (act);
682                 }
683
684                 snprintf (buf, sizeof (buf), "monitor-dim-%u", i);
685                 act = ActionManager::get_action (X_("Monitor"), buf);
686                 if (act) {
687                         cbs->dim.set_related_action (act);
688                 }
689
690                 snprintf (buf, sizeof (buf), "monitor-solo-%u", i);
691                 act = ActionManager::get_action (X_("Monitor"), buf);
692                 if (act) {
693                         cbs->solo.set_related_action (act);
694                 }
695
696                 snprintf (buf, sizeof (buf), "monitor-invert-%u", i);
697                 act = ActionManager::get_action (X_("Monitor"), buf);
698                 if (act) {
699                         cbs->invert.set_related_action (act);
700                 }
701         }
702
703         channel_table.show_all ();
704 }
705
706 void
707 MonitorSection::toggle_exclusive_solo ()
708 {
709         if (!_monitor) {
710                 return;
711         }
712
713         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
714         if (act) {
715                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
716                 Config->set_exclusive_solo (tact->get_active());
717         }
718
719 }
720
721 void
722 MonitorSection::toggle_mute_overrides_solo ()
723 {
724         if (!_monitor) {
725                 return;
726         }
727
728         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
729         if (act) {
730                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
731                 Config->set_solo_mute_override (tact->get_active());
732         }
733 }
734
735 void
736 MonitorSection::dim_all ()
737 {
738         if (!_monitor) {
739                 return;
740         }
741
742         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
743         if (act) {
744                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
745                 _monitor->set_dim_all (tact->get_active());
746         }
747
748 }
749
750 void
751 MonitorSection::cut_all ()
752 {
753         if (!_monitor) {
754                 return;
755         }
756
757         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
758         if (act) {
759                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
760                 _monitor->set_cut_all (tact->get_active());
761         }
762 }
763
764 void
765 MonitorSection::mono ()
766 {
767         if (!_monitor) {
768                 return;
769         }
770
771         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
772         if (act) {
773                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
774                 _monitor->set_mono (tact->get_active());
775         }
776 }
777
778 void
779 MonitorSection::cut_channel (uint32_t chn)
780 {
781         if (!_monitor) {
782                 return;
783         }
784
785         char buf[64];
786         snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
787
788         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
789         if (act) {
790                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
791                 _monitor->set_cut (chn, tact->get_active());
792         }
793 }
794
795 void
796 MonitorSection::dim_channel (uint32_t chn)
797 {
798         if (!_monitor) {
799                 return;
800         }
801
802         char buf[64];
803         snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
804
805         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
806         if (act) {
807                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
808                 _monitor->set_dim (chn, tact->get_active());
809         }
810
811 }
812
813 void
814 MonitorSection::solo_channel (uint32_t chn)
815 {
816         if (!_monitor) {
817                 return;
818         }
819
820         char buf[64];
821         snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
822
823         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
824         if (act) {
825                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
826                 _monitor->set_solo (chn, tact->get_active());
827         }
828
829 }
830
831 void
832 MonitorSection::invert_channel (uint32_t chn)
833 {
834         if (!_monitor) {
835                 return;
836         }
837
838         char buf[64];
839         snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
840
841         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
842         if (act) {
843                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
844                 _monitor->set_polarity (chn, tact->get_active());
845         }
846 }
847
848 void
849 MonitorSection::register_actions ()
850 {
851         string action_name;
852         string action_descr;
853         Glib::RefPtr<Action> act;
854
855         monitor_actions = ActionGroup::create (X_("Monitor"));
856         ActionManager::add_action_group (monitor_actions);
857
858         ActionManager::register_toggle_action (monitor_actions, "monitor-mono", "", _("Switch monitor to mono"),
859                         sigc::mem_fun (*this, &MonitorSection::mono));
860
861         ActionManager::register_toggle_action (monitor_actions, "monitor-cut-all", "", _("Cut monitor"),
862                         sigc::mem_fun (*this, &MonitorSection::cut_all));
863
864         ActionManager::register_toggle_action (monitor_actions, "monitor-dim-all", "", _("Dim monitor"),
865                         sigc::mem_fun (*this, &MonitorSection::dim_all));
866
867         act = ActionManager::register_toggle_action (monitor_actions, "toggle-exclusive-solo", "", _("Toggle exclusive solo mode"),
868                         sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
869
870         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
871         tact->set_active (Config->get_exclusive_solo());
872
873         act = ActionManager::register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", "", _("Toggle mute overrides solo mode"),
874                         sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
875
876         tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
877         tact->set_active (Config->get_solo_mute_override());
878
879
880         for (uint32_t chn = 0; chn < 16; ++chn) {
881
882                 action_name = string_compose (X_("monitor-cut-%1"), chn);
883                 action_descr = string_compose (_("Cut monitor channel %1"), chn);
884                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
885                                 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
886
887                 action_name = string_compose (X_("monitor-dim-%1"), chn);
888                 action_descr = string_compose (_("Dim monitor channel %1"), chn);
889                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
890                                 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
891
892                 action_name = string_compose (X_("monitor-solo-%1"), chn);
893                 action_descr = string_compose (_("Solo monitor channel %1"), chn);
894                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
895                                 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
896
897                 action_name = string_compose (X_("monitor-invert-%1"), chn);
898                 action_descr = string_compose (_("Invert monitor channel %1"), chn);
899                 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), "", action_descr.c_str(),
900                                 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
901
902         }
903
904
905         Glib::RefPtr<ActionGroup> solo_actions = ActionGroup::create (X_("Solo"));
906         RadioAction::Group solo_group;
907
908         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", "", _("In-place solo"),
909                         sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
910         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", "", _("After Fade Listen (AFL) solo"),
911                         sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
912         ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", "", _("Pre Fade Listen (PFL) solo"),
913                         sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
914
915         ActionManager::add_action_group (solo_actions);
916 }
917
918 void
919 MonitorSection::connect_actions ()
920 {
921         Glib::RefPtr<Action> act;
922         Glib::RefPtr<ToggleAction> tact;
923
924 #define MON_TOG(NAME, FUNC) \
925         act = ActionManager::get_action (X_("Monitor"), NAME); \
926         tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act); \
927         assert (tact); \
928         tact->signal_toggled().connect (sigc::mem_fun (*this, &MonitorSection::FUNC)); \
929
930         MON_TOG("monitor-mono", mono);
931         MON_TOG("monitor-cut-all", cut_all);
932         MON_TOG("monitor-dim-all", dim_all);
933
934         MON_TOG("toggle-exclusive-solo", toggle_exclusive_solo);
935         tact->set_active (Config->get_exclusive_solo());
936
937         MON_TOG("toggle-mute-overrides-solo", toggle_mute_overrides_solo);
938         tact->set_active (Config->get_solo_mute_override());
939 #undef MON_TOG
940
941 #define MON_BIND(NAME, FUNC, ARG) \
942         act = ActionManager::get_action (X_("Monitor"), NAME); \
943         tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act); \
944         assert (tact); \
945         tact->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::FUNC), ARG));
946
947         for (uint32_t chn = 0; chn < 16; ++chn) {
948                 std::string action_name = string_compose (X_("monitor-cut-%1"), chn);
949                 MON_BIND(action_name.c_str(), cut_channel, chn);
950                 action_name = string_compose (X_("monitor-dim-%1"), chn);
951                 MON_BIND(action_name.c_str(), dim_channel, chn);
952                 action_name = string_compose (X_("monitor-solo-%1"), chn);
953                 MON_BIND(action_name.c_str(), solo_channel, chn);
954                 action_name = string_compose (X_("monitor-invert-%1"), chn);
955                 MON_BIND(action_name.c_str(), invert_channel, chn);
956         }
957 #undef MON_BIND
958
959 #define SOLO_RADIO(NAME, FUNC) \
960         act = ActionManager::get_action (X_("Solo"), NAME); \
961         ract = Glib::RefPtr<RadioAction>::cast_dynamic (act); \
962         assert (ract); \
963         ract->signal_toggled().connect (sigc::mem_fun (*this, &MonitorSection::FUNC)); \
964
965         Glib::RefPtr<RadioAction> ract;
966         SOLO_RADIO ("solo-use-in-place", solo_use_in_place);
967         SOLO_RADIO ("solo-use-afl", solo_use_afl);
968         SOLO_RADIO ("solo-use-pfl", solo_use_pfl);
969 #undef SOLO_RADIO
970 }
971
972 void
973 MonitorSection::solo_use_in_place ()
974 {
975         /* this is driven by a toggle on a radio group, and so is invoked twice,
976                  once for the item that became inactive and once for the one that became
977                  active.
978                  */
979
980         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
981
982         if (act) {
983                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
984                 if (ract) {
985                         if (!ract->get_active ()) {
986                                 /* We are turning SiP off, which means that AFL or PFL will be turned on
987                                          shortly; don't update the solo model in the mean time, as if the currently
988                                          configured listen position is not the one that is about to be turned on,
989                                          things will go wrong.
990                                          */
991                                 _inhibit_solo_model_update = true;
992                         }
993                         Config->set_solo_control_is_listen_control (!ract->get_active());
994                         _inhibit_solo_model_update = false;
995                 }
996         }
997 }
998
999 void
1000 MonitorSection::solo_use_afl ()
1001 {
1002         /* this is driven by a toggle on a radio group, and so is invoked twice,
1003                  once for the item that became inactive and once for the one that became
1004                  active.
1005                  */
1006
1007         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
1008         if (act) {
1009                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1010                 if (ract) {
1011                         if (ract->get_active()) {
1012                                 Config->set_solo_control_is_listen_control (true);
1013                                 Config->set_listen_position (AfterFaderListen);
1014                         }
1015                 }
1016         }
1017 }
1018
1019 void
1020 MonitorSection::solo_use_pfl ()
1021 {
1022         /* this is driven by a toggle on a radio group, and so is invoked twice,
1023            once for the item that became inactive and once for the one that became
1024            active.
1025         */
1026
1027         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
1028         if (act) {
1029                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1030                 if (ract) {
1031                         if (ract->get_active()) {
1032                                 Config->set_solo_control_is_listen_control (true);
1033                                 Config->set_listen_position (PreFaderListen);
1034                         }
1035                 }
1036         }
1037 }
1038
1039 void
1040 MonitorSection::update_solo_model ()
1041 {
1042         if (_inhibit_solo_model_update) {
1043                 return;
1044         }
1045
1046         const char* action_name = 0;
1047         Glib::RefPtr<Action> act;
1048
1049         if (Config->get_solo_control_is_listen_control()) {
1050                 switch (Config->get_listen_position()) {
1051                         case AfterFaderListen:
1052                                 action_name = X_("solo-use-afl");
1053                                 break;
1054                         case PreFaderListen:
1055                                 action_name = X_("solo-use-pfl");
1056                                 break;
1057                 }
1058         } else {
1059                 action_name = X_("solo-use-in-place");
1060         }
1061
1062         act = ActionManager::get_action (X_("Solo"), action_name);
1063         if (act) {
1064
1065                 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1066                 if (ract) {
1067                         /* because these are radio buttons, one of them will be
1068                                  active no matter what. to trigger a change in the
1069                                  action so that the view picks it up, toggle it.
1070                                  */
1071                         if (ract->get_active()) {
1072                                 ract->set_active (false);
1073                         }
1074                         ract->set_active (true);
1075                 }
1076
1077         }
1078 }
1079
1080 void
1081 MonitorSection::map_state ()
1082 {
1083         if (!_route || !_monitor) {
1084                 return;
1085         }
1086
1087         Glib::RefPtr<Action> act;
1088
1089         update_solo_model ();
1090
1091         act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
1092         if (act) {
1093                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1094                 if (tact) {
1095                         tact->set_active (_monitor->cut_all());
1096                 }
1097         }
1098
1099         act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
1100         if (act) {
1101                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1102                 if (tact) {
1103                         tact->set_active (_monitor->dim_all());
1104                 }
1105         }
1106
1107         act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
1108         if (act) {
1109                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1110                 if (tact) {
1111                         tact->set_active (_monitor->mono());
1112                 }
1113         }
1114
1115         uint32_t nchans = _monitor->output_streams().n_audio();
1116
1117         assert (nchans == _channel_buttons.size ());
1118
1119         for (uint32_t n = 0; n < nchans; ++n) {
1120
1121                 char action_name[32];
1122
1123                 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1124                 act = ActionManager::get_action (X_("Monitor"), action_name);
1125                 if (act) {
1126                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1127                         if (tact) {
1128                                 tact->set_active (_monitor->cut (n));
1129                         }
1130                 }
1131
1132                 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1133                 act = ActionManager::get_action (X_("Monitor"), action_name);
1134                 if (act) {
1135                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1136                         if (tact) {
1137                                 tact->set_active (_monitor->dimmed (n));
1138                         }
1139                 }
1140
1141                 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1142                 act = ActionManager::get_action (X_("Monitor"), action_name);
1143                 if (act) {
1144                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1145                         if (tact) {
1146                                 tact->set_active (_monitor->soloed (n));
1147                         }
1148                 }
1149
1150                 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1151                 act = ActionManager::get_action (X_("Monitor"), action_name);
1152                 if (act) {
1153                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1154                         if (tact) {
1155                                 tact->set_active (_monitor->inverted (n));
1156                         }
1157                 }
1158         }
1159 }
1160
1161 void
1162 MonitorSection::do_blink (bool onoff)
1163 {
1164         solo_blink (onoff);
1165         audition_blink (onoff);
1166 }
1167
1168 void
1169 MonitorSection::audition_blink (bool onoff)
1170 {
1171         if (_session == 0) {
1172                 return;
1173         }
1174
1175         if (_session->is_auditioning()) {
1176                 rude_audition_button.set_active (onoff);
1177         } else {
1178                 rude_audition_button.set_active (false);
1179         }
1180 }
1181
1182 void
1183 MonitorSection::solo_blink (bool onoff)
1184 {
1185         if (_session == 0) {
1186                 return;
1187         }
1188
1189         if (_session->soloing() || _session->listening()) {
1190                 rude_solo_button.set_active (onoff);
1191
1192                 if (_session->soloing()) {
1193                         if (_session->solo_isolated()) {
1194                                 rude_iso_button.set_active (onoff);
1195                         } else {
1196                                 rude_iso_button.set_active (false);
1197                         }
1198                 }
1199
1200         } else {
1201                 rude_solo_button.set_active (false);
1202                 rude_iso_button.set_active (false);
1203         }
1204 }
1205
1206 bool
1207 MonitorSection::cancel_isolate (GdkEventButton*)
1208 {
1209         if (_session) {
1210                 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1211                 _session->set_solo_isolated (rl, false, Session::rt_cleanup, Controllable::NoGroup);
1212         }
1213
1214         return true;
1215 }
1216
1217 bool
1218 MonitorSection::cancel_audition (GdkEventButton*)
1219 {
1220         if (_session) {
1221                 _session->cancel_audition();
1222         }
1223         return true;
1224 }
1225
1226 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1227         if (action) { \
1228                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1229                 if (tact && tact->get_active() != value) { \
1230                         tact->set_active(value); \
1231                 } \
1232         }
1233
1234 void
1235 MonitorSection::parameter_changed (std::string name)
1236 {
1237         if (name == "solo-control-is-listen-control") {
1238                 update_solo_model ();
1239         } else if (name == "listen-position") {
1240                 update_solo_model ();
1241         } else if (name == "solo-mute-override") {
1242                 SYNCHRONIZE_TOGGLE_ACTION(
1243                                 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1244                                 Config->get_solo_mute_override ())
1245         } else if (name == "exclusive-solo") {
1246                 SYNCHRONIZE_TOGGLE_ACTION(
1247                                 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1248                                 Config->get_exclusive_solo ())
1249         }
1250 }
1251
1252 void
1253 MonitorSection::assign_controllables ()
1254 {
1255         boost::shared_ptr<Controllable> none;
1256
1257         if (!gain_control) {
1258                 /* too early - GUI controls not set up yet */
1259                 return;
1260         }
1261
1262         if (_session) {
1263                 solo_cut_control->set_controllable (_session->solo_cut_control());
1264                 solo_cut_display->set_controllable (_session->solo_cut_control());
1265         } else {
1266                 solo_cut_control->set_controllable (none);
1267                 solo_cut_display->set_controllable (none);
1268         }
1269
1270         if (_route) {
1271                 gain_control->set_controllable (_route->gain_control());
1272                 gain_display->set_controllable (_route->gain_control());
1273         } else {
1274                 gain_control->set_controllable (none);
1275         }
1276
1277         if (_monitor) {
1278
1279                 cut_all_button.set_controllable (_monitor->cut_control());
1280                 cut_all_button.watch ();
1281                 dim_all_button.set_controllable (_monitor->dim_control());
1282                 dim_all_button.watch ();
1283                 mono_button.set_controllable (_monitor->mono_control());
1284                 mono_button.watch ();
1285
1286                 dim_control->set_controllable (_monitor->dim_level_control ());
1287                 dim_display->set_controllable (_monitor->dim_level_control ());
1288                 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1289                 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1290
1291         } else {
1292
1293                 cut_all_button.set_controllable (none);
1294                 dim_all_button.set_controllable (none);
1295                 mono_button.set_controllable (none);
1296
1297                 dim_control->set_controllable (none);
1298                 dim_display->set_controllable (none);
1299                 solo_boost_control->set_controllable (none);
1300                 solo_boost_display->set_controllable (none);
1301         }
1302 }
1303
1304 string
1305 MonitorSection::state_id() const
1306 {
1307         return "monitor-section";
1308 }
1309
1310 void
1311 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1312 {
1313         using namespace Menu_Helpers;
1314
1315         if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1316                 return;
1317         }
1318
1319         list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1320         while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1321                 ++i;
1322         }
1323
1324         if (i != output_menu_bundles.end()) {
1325                 return;
1326         }
1327
1328         output_menu_bundles.push_back (b);
1329
1330         MenuList& citems = output_menu.items();
1331
1332         std::string n = b->name ();
1333         replace_all (n, "_", " ");
1334
1335         citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1336 }
1337
1338 void
1339 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1340 {
1341
1342         ARDOUR::BundleList current = _route->output()->bundles_connected ();
1343
1344         if (std::find (current.begin(), current.end(), c) == current.end()) {
1345                 _route->output()->connect_ports_to_bundle (c, true, this);
1346         } else {
1347                 _route->output()->disconnect_ports_from_bundle (c, this);
1348         }
1349 }
1350
1351 gint
1352 MonitorSection::output_release (GdkEventButton *ev)
1353 {
1354         switch (ev->button) {
1355         case 3:
1356                 edit_output_configuration ();
1357                 break;
1358         }
1359
1360         return false;
1361 }
1362
1363 struct RouteCompareByName {
1364         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1365                 return a->name().compare (b->name()) < 0;
1366         }
1367 };
1368
1369 gint
1370 MonitorSection::output_press (GdkEventButton *ev)
1371 {
1372         using namespace Menu_Helpers;
1373         if (!_session) {
1374                 MessageDialog msg (_("No session - no I/O changes are possible"));
1375                 msg.run ();
1376                 return true;
1377         }
1378
1379         MenuList& citems = output_menu.items();
1380         switch (ev->button) {
1381
1382         case 3:
1383                 return false;  //wait for the mouse-up to pop the dialog
1384
1385         case 1:
1386         {
1387                 output_menu.set_name ("ArdourContextMenu");
1388                 citems.clear ();
1389                 output_menu_bundles.clear ();
1390
1391                 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1392
1393                 citems.push_back (SeparatorElem());
1394                 uint32_t const n_with_separator = citems.size ();
1395
1396                 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1397
1398                 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1399
1400                 /* give user bundles first chance at being in the menu */
1401
1402                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1403                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1404                                 maybe_add_bundle_to_output_menu (*i, current);
1405                         }
1406                 }
1407
1408                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1409                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1410                                 maybe_add_bundle_to_output_menu (*i, current);
1411                         }
1412                 }
1413
1414                 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1415                 RouteList copy = *routes;
1416                 copy.sort (RouteCompareByName ());
1417                 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1418                         maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1419                 }
1420
1421                 if (citems.size() == n_with_separator) {
1422                         /* no routes added; remove the separator */
1423                         citems.pop_back ();
1424                 }
1425
1426                 citems.push_back (SeparatorElem());
1427                 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1428
1429                 output_menu.popup (1, ev->time);
1430                 break;
1431         }
1432
1433         default:
1434                 break;
1435         }
1436         return TRUE;
1437 }
1438
1439 void
1440 MonitorSection::update_output_display ()
1441 {
1442         if (!_route || !_monitor || _session->deletion_in_progress()) {
1443                 return;
1444         }
1445
1446         uint32_t io_count;
1447         uint32_t io_index;
1448         boost::shared_ptr<Port> port;
1449         vector<string> port_connections;
1450
1451         uint32_t total_connection_count = 0;
1452         uint32_t io_connection_count = 0;
1453         uint32_t ardour_connection_count = 0;
1454         uint32_t system_connection_count = 0;
1455         uint32_t other_connection_count = 0;
1456
1457         ostringstream label;
1458
1459         bool have_label = false;
1460         bool each_io_has_one_connection = true;
1461
1462         string connection_name;
1463         string ardour_track_name;
1464         string other_connection_type;
1465         string system_ports;
1466         string system_port;
1467
1468         ostringstream tooltip;
1469         char * tooltip_cstr;
1470
1471         io_count = _route->n_outputs().n_total();
1472         tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1473
1474
1475         for (io_index = 0; io_index < io_count; ++io_index) {
1476
1477                 port = _route->output()->nth (io_index);
1478
1479                 //ignore any port connections that don't match our DataType
1480                 if (port->type() != DataType::AUDIO) {
1481                         continue;
1482                 }
1483
1484                 port_connections.clear ();
1485                 port->get_connections(port_connections);
1486                 io_connection_count = 0;
1487
1488                 if (!port_connections.empty()) {
1489                         for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1490                                 string pn = "";
1491                                 string& connection_name (*i);
1492
1493                                 if (connection_name.find("system:") == 0) {
1494                                         pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1495                                 }
1496
1497                                 if (io_connection_count == 0) {
1498                                         tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1499                                                 << " -> "
1500                                                 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1501                                 } else {
1502                                         tooltip << ", "
1503                                                 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1504                                 }
1505
1506                                 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1507                                         if (ardour_track_name.empty()) {
1508                                                 // "ardour:Master/in 1" -> "ardour:Master/"
1509                                                 string::size_type slash = connection_name.find("/");
1510                                                 if (slash != string::npos) {
1511                                                         ardour_track_name = connection_name.substr(0, slash + 1);
1512                                                 }
1513                                         }
1514
1515                                         if (connection_name.find(ardour_track_name) == 0) {
1516                                                 ++ardour_connection_count;
1517                                         }
1518                                 } else if (!pn.empty()) {
1519                                         if (system_ports.empty()) {
1520                                                 system_ports += pn;
1521                                         } else {
1522                                                 system_ports += "/" + pn;
1523                                         }
1524                                         if (connection_name.find("system:") == 0) {
1525                                                 ++system_connection_count;
1526                                         }
1527                                 } else if (connection_name.find("system:") == 0) {
1528                                         // "system:playback_123" -> "123"
1529                                         system_port = connection_name.substr(16);
1530                                         if (system_ports.empty()) {
1531                                                 system_ports += system_port;
1532                                         } else {
1533                                                 system_ports += "/" + system_port;
1534                                         }
1535
1536                                         ++system_connection_count;
1537                                 } else {
1538                                         if (other_connection_type.empty()) {
1539                                                 // "jamin:in 1" -> "jamin:"
1540                                                 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1541                                         }
1542
1543                                         if (connection_name.find(other_connection_type) == 0) {
1544                                                 ++other_connection_count;
1545                                         }
1546                                 }
1547
1548                                 ++total_connection_count;
1549                                 ++io_connection_count;
1550                         }
1551                 }
1552
1553                 if (io_connection_count != 1) {
1554                         each_io_has_one_connection = false;
1555                 }
1556         }
1557
1558         if (total_connection_count == 0) {
1559                 tooltip << endl << _("Disconnected");
1560         }
1561
1562         tooltip_cstr = new char[tooltip.str().size() + 1];
1563         strcpy(tooltip_cstr, tooltip.str().c_str());
1564
1565         set_tooltip (output_button, tooltip_cstr, "");
1566
1567         if (each_io_has_one_connection) {
1568                 if (total_connection_count == ardour_connection_count) {
1569                         // all connections are to the same track in ardour
1570                         // "ardour:Master/" -> "Master"
1571                         string::size_type slash = ardour_track_name.find("/");
1572                         if (slash != string::npos) {
1573                                 label << ardour_track_name.substr(7, slash - 7);
1574                                 have_label = true;
1575                         }
1576                 } else if (total_connection_count == system_connection_count) {
1577                         // all connections are to system ports
1578                         label << system_ports;
1579                         have_label = true;
1580                 } else if (total_connection_count == other_connection_count) {
1581                         // all connections are to the same external program eg jamin
1582                         // "jamin:" -> "jamin"
1583                         label << other_connection_type.substr(0, other_connection_type.size() - 1);
1584                         have_label = true;
1585                 }
1586         }
1587
1588         if (!have_label) {
1589                 if (total_connection_count == 0) {
1590                         // Disconnected
1591                         label << "-";
1592                 } else {
1593                         // Odd configuration
1594                         label << "*" << total_connection_count << "*";
1595                 }
1596         }
1597
1598         output_button->set_text (label.str());
1599 }
1600
1601 void
1602 MonitorSection::disconnect_output ()
1603 {
1604         if (_route) {
1605                 _route->output()->disconnect(this);
1606         }
1607 }
1608
1609 void
1610 MonitorSection::edit_output_configuration ()
1611 {
1612         if (_output_selector == 0) {
1613                 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1614         }
1615         _output_selector->present ();
1616 }
1617
1618 void
1619 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1620 {
1621         if (!_route) {
1622                 return;
1623         }
1624         boost::shared_ptr<Port> a = wa.lock ();
1625         boost::shared_ptr<Port> b = wb.lock ();
1626         if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1627                 update_output_display ();
1628         }
1629 }
1630
1631 void
1632 MonitorSection::help_count_processors (boost::weak_ptr<Processor> p, uint32_t* cnt) const
1633 {
1634         boost::shared_ptr<Processor> processor (p.lock ());
1635         if (!processor || !processor->display_to_user()) {
1636                 return;
1637         }
1638         if (boost::dynamic_pointer_cast<Amp>(processor)) {
1639                 return;
1640         }
1641         ++(*cnt);
1642 }
1643
1644 uint32_t
1645 MonitorSection::count_processors ()
1646 {
1647         uint32_t processor_count = 0;
1648         if (_route) {
1649                 _route->foreach_processor (sigc::bind (sigc::mem_fun (*this, &MonitorSection::help_count_processors), &processor_count));
1650         }
1651         return processor_count;
1652 }
1653
1654 void
1655 MonitorSection::processors_changed (ARDOUR::RouteProcessorChange)
1656 {
1657         update_processor_box ();
1658 }