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