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