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