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