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