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