2 Copyright (C) 2012 Paul Davis
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.
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.
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.
20 #include <gdkmm/pixbuf.h>
22 #include "pbd/compose.h"
23 #include "pbd/error.h"
24 #include "pbd/replace_all.h"
26 #include "gtkmm2ext/bindable_button.h"
27 #include "gtkmm2ext/tearoff.h"
28 #include "gtkmm2ext/actions.h"
29 #include "gtkmm2ext/utils.h"
31 #include <gtkmm/menu.h>
32 #include <gtkmm/menuitem.h>
34 #include "ardour/amp.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/monitor_processor.h"
37 #include "ardour/port.h"
38 #include "ardour/route.h"
39 #include "ardour/solo_isolate_control.h"
40 #include "ardour/user_bundle.h"
41 #include "ardour/plugin_manager.h"
43 #include "ardour_ui.h"
44 #include "gui_thread.h"
45 #include "monitor_section.h"
46 #include "public_editor.h"
49 #include "ui_config.h"
54 using namespace ARDOUR;
55 using namespace ARDOUR_UI_UTILS;
57 using namespace Gtkmm2ext;
61 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
63 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
65 MonitorSection::MonitorSection (Session* s)
66 : SessionHandlePtr (s)
69 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
70 , *channel_table_scroller.get_vadjustment ())
73 , solo_boost_control (0)
74 , solo_cut_control (0)
77 , solo_boost_display (0)
78 , solo_cut_display (0)
79 , _output_selector (0)
80 , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
81 , afl_button (_("AFL"), ArdourButton::led_default_elements)
82 , pfl_button (_("PFL"), ArdourButton::led_default_elements)
83 , exclusive_solo_button (ArdourButton::led_default_elements)
84 , solo_mute_override_button (ArdourButton::led_default_elements)
85 , toggle_processorbox_button (ArdourButton::default_elements)
86 , _inhibit_solo_model_update (false)
87 , _ui_initialized (false)
88 , myactions (X_("monitor section"))
92 using namespace Menu_Helpers;
94 Glib::RefPtr<Action> act;
96 if (!monitor_actions) {
100 set_data ("ardour-bindings", bindings);
104 _plugin_selector = new PluginSelector (PluginManager::instance());
105 insert_box = new ProcessorBox (_session, boost::bind (&MonitorSection::plugin_selector, this), _rr_selection, 0);
106 insert_box->set_no_show_all ();
108 // TODO allow keyboard shortcuts in ProcessorBox
112 /* Rude Solo & Solo Isolated */
113 rude_solo_button.set_text (_("Soloing"));
114 rude_solo_button.set_name ("rude solo");
115 rude_solo_button.show ();
117 rude_iso_button.set_text (_("Isolated"));
118 rude_iso_button.set_name ("rude isolate");
119 rude_iso_button.show ();
121 rude_audition_button.set_text (_("Auditioning"));
122 rude_audition_button.set_name ("rude audition");
123 rude_audition_button.show ();
125 Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
127 act = ActionManager::get_action (X_("Main"), X_("cancel-solo"));
128 rude_solo_button.set_related_action (act);
129 UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
131 rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
132 UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
134 rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
135 UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
137 /* SIP, AFL, PFL radio */
139 solo_in_place_button.set_name ("monitor section solo model");
140 afl_button.set_name ("monitor section solo model");
141 pfl_button.set_name ("monitor section solo model");
143 solo_in_place_button.set_led_left (true);
144 afl_button.set_led_left (true);
145 pfl_button.set_led_left (true);
147 solo_in_place_button.show ();
151 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
152 set_tooltip (solo_in_place_button, _("Solo controls affect solo-in-place"));
154 solo_in_place_button.set_related_action (act);
157 act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
158 set_tooltip (afl_button, _("Solo controls toggle after-fader-listen"));
160 afl_button.set_related_action (act);
163 act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
164 set_tooltip (pfl_button, _("Solo controls toggle pre-fader-listen"));
166 pfl_button.set_related_action (act);
169 /* Solo option buttons */
170 exclusive_solo_button.set_text (_("Excl. Solo"));
171 exclusive_solo_button.set_name (X_("monitor section solo option"));
172 set_tooltip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
174 act = ActionManager::get_action (X_("Monitor"), X_("toggle-exclusive-solo"));
176 exclusive_solo_button.set_related_action (act);
179 solo_mute_override_button.set_text (_("Solo ยป Mute"));
180 solo_mute_override_button.set_name (X_("monitor section solo option"));
181 set_tooltip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
183 act = ActionManager::get_action (X_("Monitor"), X_("toggle-mute-overrides-solo"));
185 solo_mute_override_button.set_related_action (act);
188 /* Processor Box hide/shos */
189 toggle_processorbox_button.set_text (_("Processors"));
190 toggle_processorbox_button.set_name (X_("monitor section processors toggle"));
191 set_tooltip (&toggle_processorbox_button, _("Allow one to add monitor effect processors"));
193 proctoggle = ActionManager::get_action (X_("Monitor"), X_("toggle-monitor-processor-box"));
194 toggle_processorbox_button.set_related_action (proctoggle);
197 Label* solo_boost_label;
198 Label* solo_cut_label;
201 /* Solo Boost Knob */
203 solo_boost_control = new ArdourKnob ();
204 solo_boost_control->set_name("monitor section knob");
205 solo_boost_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
206 set_tooltip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
208 solo_boost_display = new ArdourDisplay ();
209 solo_boost_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
210 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
211 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
212 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
213 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
215 solo_boost_label = manage (new Label (_("Solo Boost")));
219 solo_cut_control = new ArdourKnob ();
220 solo_cut_control->set_name ("monitor section knob");
221 solo_cut_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
222 set_tooltip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
224 solo_cut_display = new ArdourDisplay ();
225 solo_cut_display->set_name("monitor section dropdown"); // XXX
226 solo_cut_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
227 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
228 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
229 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
230 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
231 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
233 solo_cut_label = manage (new Label (_("SiP Cut")));
237 dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
238 dim_control->set_name ("monitor section knob");
239 dim_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
240 set_tooltip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
242 dim_display = new ArdourDisplay ();
243 dim_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
244 dim_display->add_controllable_preset(_("0 dB"), 0.0);
245 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
246 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
247 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
248 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
250 dim_label = manage (new Label (_("Dim")));
253 cut_all_button.set_text (_("Mute"));
254 cut_all_button.set_name ("mute button");
255 cut_all_button.set_size_request (-1, PX_SCALE(30));
256 cut_all_button.show ();
258 act = ActionManager::get_action (X_("Monitor"), X_("monitor-cut-all"));
260 cut_all_button.set_related_action (act);
264 dim_all_button.set_text (_("Dim"));
265 dim_all_button.set_name ("monitor section dim");
266 dim_all_button.set_size_request (-1, PX_SCALE(25));
267 act = ActionManager::get_action (X_("Monitor"), X_("monitor-dim-all"));
269 dim_all_button.set_related_action (act);
273 mono_button.set_text (_("Mono"));
274 mono_button.set_name ("monitor section mono");
275 mono_button.set_size_request (-1, PX_SCALE(25));
276 act = ActionManager::get_action (X_("Monitor"), X_("monitor-mono"));
278 mono_button.set_related_action (act);
283 gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
284 gain_control->set_name("monitor section knob");
285 gain_control->set_size_request (PX_SCALE(60), PX_SCALE(60));
287 gain_display = new ArdourDisplay ();
288 gain_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
289 gain_display->add_controllable_preset(_("0 dB"), 0.0);
290 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
291 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
292 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
293 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
294 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
296 Label* output_label = manage (new Label (_("Output")));
297 output_label->set_name (X_("MonitorSectionLabel"));
299 output_button = new ArdourButton ();
300 output_button->set_text (_("Output"));
301 output_button->set_name (X_("monitor section cut")); // XXX
302 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
303 output_button->set_layout_ellipsize_width (PX_SCALE(128) * PANGO_SCALE);
305 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
306 channel_table_scroller.set_size_request (-1, PX_SCALE(150));
307 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
308 channel_table_scroller.show ();
309 channel_table_scroller.add (channel_table_viewport);
311 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
312 channel_size_group->add_widget (channel_table_header);
313 channel_size_group->add_widget (channel_table);
315 channel_table_header.resize (1, 5);
317 Label* l1 = manage (new Label (X_(" ")));
318 l1->set_name (X_("MonitorSectionLabel"));
319 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
321 l1 = manage (new Label (_("Mute")));
322 l1->set_name (X_("MonitorSectionLabel"));
323 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
325 l1 = manage (new Label (_("Dim")));
326 l1->set_name (X_("MonitorSectionLabel"));
327 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
329 l1 = manage (new Label (_("Solo")));
330 l1->set_name (X_("MonitorSectionLabel"));
331 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
333 l1 = manage (new Label (_("Inv")));
334 l1->set_name (X_("MonitorSectionLabel"));
335 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
337 channel_table_header.show ();
340 /****************************************************************************
341 * LAYOUT top to bottom
344 // solo, iso information
345 HBox* rude_box = manage (new HBox);
346 rude_box->set_spacing (PX_SCALE(4));
347 rude_box->set_homogeneous (true);
348 rude_box->pack_start (rude_solo_button, true, true);
349 rude_box->pack_start (rude_iso_button, true, true);
351 // solo options (right align)
352 HBox* tbx1 = manage (new HBox);
353 tbx1->pack_end (exclusive_solo_button, false, false);
355 HBox* tbx2 = manage (new HBox);
356 tbx2->pack_end (solo_mute_override_button, false, false);
358 HBox* tbx3 = manage (new HBox);
359 tbx3->pack_end (toggle_processorbox_button, false, false);
361 HBox* tbx0 = manage (new HBox); // space
363 // combined solo mode (Sip, AFL, PFL) & solo options
364 Table *solo_tbl = manage (new Table);
365 solo_tbl->attach (solo_in_place_button, 0, 1, 0, 1, EXPAND|FILL, SHRINK, 0, 2);
366 solo_tbl->attach (pfl_button, 0, 1, 1, 2, EXPAND|FILL, SHRINK, 0, 2);
367 solo_tbl->attach (afl_button, 0, 1, 2, 3, EXPAND|FILL, SHRINK, 0, 2);
368 solo_tbl->attach (*tbx0, 1, 2, 0, 3, EXPAND|FILL, SHRINK, 2, 2);
369 solo_tbl->attach (*tbx1, 2, 3, 0, 1, EXPAND|FILL, SHRINK, 0, 2);
370 solo_tbl->attach (*tbx2, 2, 3, 1, 2, EXPAND|FILL, SHRINK, 0, 2);
371 solo_tbl->attach (*tbx3, 2, 3, 2, 3, EXPAND|FILL, SHRINK, 0, 2);
373 // boost, cut, dim volume control
374 Table *level_tbl = manage (new Table);
375 level_tbl->attach (*solo_boost_label, 0, 2, 0, 1, EXPAND|FILL, SHRINK, 1, 2);
376 level_tbl->attach (*solo_boost_control, 0, 2, 1, 2, EXPAND|FILL, SHRINK, 1, 2);
377 level_tbl->attach (*solo_boost_display, 0, 2, 2, 3, EXPAND , SHRINK, 1, 2);
379 level_tbl->attach (*solo_cut_label, 2, 4, 0, 1, EXPAND|FILL, SHRINK, 1, 2);
380 level_tbl->attach (*solo_cut_control, 2, 4, 1, 2, EXPAND|FILL, SHRINK, 1, 2);
381 level_tbl->attach (*solo_cut_display, 2, 4, 2, 3, EXPAND , SHRINK, 1, 2);
383 level_tbl->attach (*dim_label, 1, 3, 3, 4, EXPAND|FILL, SHRINK, 1, 2);
384 level_tbl->attach (*dim_control, 1, 3, 4, 5, EXPAND|FILL, SHRINK, 1, 2);
385 level_tbl->attach (*dim_display, 1, 3, 5, 6, EXPAND , SHRINK, 1, 2);
387 /* note that we don't pack the table_hpacker till later
388 * -> top level channel_table_packer */
389 table_hpacker.pack_start (channel_table, true, true);
392 HBox* mono_dim_box = manage (new HBox);
393 mono_dim_box->set_spacing (PX_SCALE(4));
394 mono_dim_box->set_homogeneous (true);
395 mono_dim_box->pack_start (mono_button, true, true);
396 mono_dim_box->pack_end (dim_all_button, true, true);
399 Label* spin_label = manage (new Label (_("Monitor")));
400 VBox* spin_packer = manage (new VBox);
401 spin_packer->set_spacing (PX_SCALE(2));
402 spin_packer->pack_start (*spin_label, false, false);
403 spin_packer->pack_start (*gain_control, false, false);
404 spin_packer->pack_start (*gain_display, false, false);
406 master_packer.pack_start (*spin_packer, true, false);
408 // combined gain section (channels, mute, dim)
409 VBox* lower_packer = manage (new VBox);
410 lower_packer->pack_start (channel_table_header, false, false, PX_SCALE(0));
411 lower_packer->pack_start (channel_table_packer, false, false, PX_SCALE(8));
412 lower_packer->pack_start (*mono_dim_box, false, false, PX_SCALE(2));
413 lower_packer->pack_start (cut_all_button, false, false, PX_SCALE(2));
415 // output port select
416 VBox* out_packer = manage (new VBox);
417 out_packer->set_spacing (PX_SCALE(2));
418 out_packer->pack_start (*output_label, false, false);
419 out_packer->pack_start (*output_button, false, false);
421 /****************************************************************************
424 vpacker.set_border_width (PX_SCALE(3));
425 vpacker.pack_start (*rude_box, false, false, PX_SCALE(3));
426 vpacker.pack_start (rude_audition_button, false, false, 0);
427 vpacker.pack_start (*solo_tbl, false, false, PX_SCALE(8));
428 vpacker.pack_start (*insert_box, true, true, PX_SCALE(8));
429 vpacker.pack_start (*level_tbl, false, false, PX_SCALE(8));
430 vpacker.pack_start (*lower_packer, false, false, PX_SCALE(8));
431 vpacker.pack_start (master_packer, false, false, PX_SCALE(10));
432 vpacker.pack_end (*out_packer, false, false, PX_SCALE(3));
434 hpacker.set_spacing (0);
435 hpacker.pack_start (vpacker, true, true);
439 gain_control->show_all ();
440 gain_display->show_all ();
441 dim_control->show_all ();
442 dim_display->show_all();
443 solo_boost_control->show_all ();
444 solo_boost_display->show_all();
446 mono_dim_box->show ();
447 spin_packer->show ();
448 master_packer.show ();
449 channel_table.show ();
452 solo_tbl->show_all();
454 lower_packer->show ();
462 assign_controllables ();
464 output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
465 output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
467 signal_enter_notify_event().connect (sigc::mem_fun (*this, &MonitorSection::enter_handler));
468 signal_leave_notify_event().connect (sigc::mem_fun (*this, &MonitorSection::leave_handler));
469 set_flags (CAN_FOCUS);
471 _tearoff = new TearOff (*this);
473 if (!UIConfiguration::instance().get_floating_monitor_section()) {
474 /* if torn off, make this a normal window
475 * (default is WINDOW_TYPE_HINT_UTILITY in libs/gtkmm2ext/tearoff.cc)
477 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
479 _tearoff->tearoff_window().set_title (X_("Monitor"));
480 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), (Gtk::Window*) &_tearoff->tearoff_window()), false);
482 update_output_display ();
483 update_processor_box ();
484 _ui_initialized = true;
486 /* catch changes that affect us */
487 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
488 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
490 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
493 MonitorSection::~MonitorSection ()
495 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
499 _channel_buttons.clear ();
500 _output_changed_connection.disconnect ();
502 delete insert_box; insert_box = 0;
503 delete output_button; output_button = 0;
504 delete gain_control; gain_control = 0;
505 delete gain_display; gain_display = 0;
506 delete dim_control; dim_control = 0;
507 delete dim_display; dim_display = 0;
508 delete solo_boost_control; solo_boost_control = 0;
509 delete solo_boost_display; solo_boost_display = 0;
510 delete solo_cut_control; solo_cut_control = 0;
511 delete solo_cut_display; solo_cut_display = 0;
512 delete _tearoff; _tearoff = 0;
513 delete _output_selector; _output_selector = 0;
517 MonitorSection::enter_handler (GdkEventCrossing* ev)
524 MonitorSection::leave_handler (GdkEventCrossing* ev)
526 switch (ev->detail) {
527 case GDK_NOTIFY_INFERIOR:
533 /* cancel focus if we're not torn off. With X11 WM's that do
534 * focus-follows-mouse, focus will be taken from us anyway.
537 Widget* top = get_toplevel();
539 if (top->is_toplevel() && top != &_tearoff->tearoff_window()) {
540 Window* win = dynamic_cast<Window*> (top);
541 gtk_window_set_focus (win->gobj(), 0);
548 MonitorSection::update_processor_box ()
550 bool show_processor_box = Glib::RefPtr<ToggleAction>::cast_dynamic (proctoggle)->get_active ();
552 if (count_processors () > 0 && !show_processor_box) {
553 toggle_processorbox_button.set_name (X_("monitor section processors present"));
555 toggle_processorbox_button.set_name (X_("monitor section processors toggle"));
558 if (insert_box->is_visible() == show_processor_box) {
562 if (show_processor_box) {
563 if (master_packer.get_parent()) {
564 master_packer.get_parent()->remove (master_packer);
567 vpacker.pack_start (master_packer, false, false, PX_SCALE(10));
569 if (master_packer.get_parent()) {
570 master_packer.get_parent()->remove (master_packer);
573 vpacker.pack_start (master_packer, true, false, PX_SCALE(10));
578 MonitorSection::set_session (Session* s)
580 RouteUI::set_session (s);
581 _plugin_selector->set_session (_session);
585 _route = _session->monitor_out ();
588 /* session with monitor section */
589 _monitor = _route->monitor_control ();
590 assign_controllables ();
591 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
592 boost::bind (&MonitorSection::update_output_display, this),
594 insert_box->set_route (_route);
595 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MonitorSection::processors_changed, this, _1), gui_context());
596 if (_ui_initialized) {
597 update_processor_box ();
600 /* session with no monitor section */
601 _output_changed_connection.disconnect();
604 delete _output_selector;
605 _output_selector = 0;
608 if (channel_table_scroller.get_parent()) {
609 /* scroller is packed, so remove it */
610 channel_table_packer.remove (channel_table_scroller);
613 if (table_hpacker.get_parent () == &channel_table_packer) {
614 /* this occurs when the table hpacker is directly
615 packed, so remove it.
617 channel_table_packer.remove (table_hpacker);
618 } else if (table_hpacker.get_parent()) {
619 channel_table_viewport.remove ();
622 if (_monitor->output_streams().n_audio() > 7) {
623 /* put the table into a scrolled window, and then put
624 * that into the channel vpacker, after the table header
626 channel_table_viewport.add (table_hpacker);
627 channel_table_packer.pack_start (channel_table_scroller, true, true);
628 channel_table_viewport.show ();
629 channel_table_scroller.show ();
632 /* just put the channel table itself into the channel
633 * vpacker, after the table header
636 channel_table_packer.pack_start (table_hpacker, true, true);
637 channel_table_scroller.hide ();
640 table_hpacker.show ();
641 channel_table.show ();
646 _output_changed_connection.disconnect();
649 control_connections.drop_connections ();
650 rude_iso_button.unset_active_state ();
651 rude_solo_button.unset_active_state ();
652 delete _output_selector;
653 _output_selector = 0;
655 assign_controllables ();
659 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
661 cut.set_name (X_("mute button"));
662 dim.set_name (X_("monitor section dim"));
663 solo.set_name (X_("solo button"));
664 invert.set_name (X_("invert button"));
666 cut.unset_flags (Gtk::CAN_FOCUS);
667 dim.unset_flags (Gtk::CAN_FOCUS);
668 solo.unset_flags (Gtk::CAN_FOCUS);
669 invert.unset_flags (Gtk::CAN_FOCUS);
673 MonitorSection::populate_buttons ()
679 Glib::RefPtr<Action> act;
680 uint32_t nchans = _monitor->output_streams().n_audio();
682 channel_table.resize (nchans, 5);
683 channel_table.set_col_spacings (6);
684 channel_table.set_row_spacings (6);
685 channel_table.set_homogeneous (true);
687 const uint32_t row_offset = 0;
689 for (uint32_t i = 0; i < nchans; ++i) {
702 snprintf (buf, sizeof (buf), "%d", i+1);
706 Label* label = manage (new Label (l));
707 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
709 ChannelButtonSet* cbs = new ChannelButtonSet;
711 _channel_buttons.push_back (cbs);
713 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
714 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
715 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
716 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
718 snprintf (buf, sizeof (buf), "monitor-cut-%u", i);
719 act = ActionManager::get_action (X_("Monitor"), buf);
721 cbs->cut.set_related_action (act);
724 snprintf (buf, sizeof (buf), "monitor-dim-%u", i);
725 act = ActionManager::get_action (X_("Monitor"), buf);
727 cbs->dim.set_related_action (act);
730 snprintf (buf, sizeof (buf), "monitor-solo-%u", i);
731 act = ActionManager::get_action (X_("Monitor"), buf);
733 cbs->solo.set_related_action (act);
736 snprintf (buf, sizeof (buf), "monitor-invert-%u", i);
737 act = ActionManager::get_action (X_("Monitor"), buf);
739 cbs->invert.set_related_action (act);
743 channel_table.show_all ();
747 MonitorSection::toggle_exclusive_solo ()
753 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
755 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
756 Config->set_exclusive_solo (tact->get_active());
762 MonitorSection::toggle_mute_overrides_solo ()
768 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
770 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
771 Config->set_solo_mute_override (tact->get_active());
776 MonitorSection::dim_all ()
782 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
784 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
785 _monitor->set_dim_all (tact->get_active());
791 MonitorSection::cut_all ()
797 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
799 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
800 _monitor->set_cut_all (tact->get_active());
805 MonitorSection::mono ()
811 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
813 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
814 _monitor->set_mono (tact->get_active());
819 MonitorSection::cut_channel (uint32_t chn)
826 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
828 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
830 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
831 _monitor->set_cut (chn, tact->get_active());
836 MonitorSection::dim_channel (uint32_t chn)
843 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
845 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
847 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
848 _monitor->set_dim (chn, tact->get_active());
854 MonitorSection::solo_channel (uint32_t chn)
861 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
863 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
865 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
866 _monitor->set_solo (chn, tact->get_active());
872 MonitorSection::invert_channel (uint32_t chn)
879 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
881 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
883 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
884 _monitor->set_polarity (chn, tact->get_active());
889 MonitorSection::register_actions ()
893 Glib::RefPtr<Action> act;
895 monitor_actions = myactions.create_action_group (X_("Monitor"));
897 myactions.register_toggle_action (monitor_actions, "monitor-mono", _("Switch monitor to mono"),
898 sigc::mem_fun (*this, &MonitorSection::mono));
900 myactions.register_toggle_action (monitor_actions, "monitor-cut-all", _("Cut monitor"),
901 sigc::mem_fun (*this, &MonitorSection::cut_all));
903 myactions.register_toggle_action (monitor_actions, "monitor-dim-all", _("Dim monitor"),
904 sigc::mem_fun (*this, &MonitorSection::dim_all));
906 act = myactions.register_toggle_action (monitor_actions, "toggle-exclusive-solo", _("Toggle exclusive solo mode"),
907 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
909 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
910 tact->set_active (Config->get_exclusive_solo());
912 act = myactions.register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", _("Toggle mute overrides solo mode"),
913 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
915 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
916 tact->set_active (Config->get_solo_mute_override());
918 for (uint32_t chn = 0; chn < 16; ++chn) {
920 action_name = string_compose (X_("monitor-cut-%1"), chn);
921 action_descr = string_compose (_("Cut monitor channel %1"), chn);
922 myactions.register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
923 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
925 action_name = string_compose (X_("monitor-dim-%1"), chn);
926 action_descr = string_compose (_("Dim monitor channel %1"), chn);
927 myactions.register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
928 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
930 action_name = string_compose (X_("monitor-solo-%1"), chn);
931 action_descr = string_compose (_("Solo monitor channel %1"), chn);
932 myactions.register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
933 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
935 action_name = string_compose (X_("monitor-invert-%1"), chn);
936 action_descr = string_compose (_("Invert monitor channel %1"), chn);
937 myactions.register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
938 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
943 Glib::RefPtr<ActionGroup> solo_actions = myactions.create_action_group (X_("Solo"));
944 RadioAction::Group solo_group;
946 myactions.register_radio_action (solo_actions, solo_group, "solo-use-in-place", _("In-place solo"),
947 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
948 myactions.register_radio_action (solo_actions, solo_group, "solo-use-afl", _("After Fade Listen (AFL) solo"),
949 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
950 myactions.register_radio_action (solo_actions, solo_group, "solo-use-pfl", _("Pre Fade Listen (PFL) solo"),
951 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
953 myactions.register_toggle_action (monitor_actions, "toggle-monitor-processor-box", _("Toggle Monitor Section Processor Box"),
954 sigc::mem_fun(*this, &MonitorSection::update_processor_box));
958 MonitorSection::connect_actions ()
960 Glib::RefPtr<Action> act;
961 Glib::RefPtr<ToggleAction> tact;
963 #define MON_TOG(NAME, FUNC) \
964 act = ActionManager::get_action (X_("Monitor"), NAME); \
965 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act); \
967 tact->signal_toggled().connect (sigc::mem_fun (*this, &MonitorSection::FUNC)); \
969 MON_TOG("monitor-mono", mono);
970 MON_TOG("monitor-cut-all", cut_all);
971 MON_TOG("monitor-dim-all", dim_all);
973 MON_TOG("toggle-exclusive-solo", toggle_exclusive_solo);
974 tact->set_active (Config->get_exclusive_solo());
976 MON_TOG("toggle-mute-overrides-solo", toggle_mute_overrides_solo);
977 tact->set_active (Config->get_solo_mute_override());
980 #define MON_BIND(NAME, FUNC, ARG) \
981 act = ActionManager::get_action (X_("Monitor"), NAME); \
982 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act); \
984 tact->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::FUNC), ARG));
986 for (uint32_t chn = 0; chn < 16; ++chn) {
987 std::string action_name = string_compose (X_("monitor-cut-%1"), chn);
988 MON_BIND(action_name.c_str(), cut_channel, chn);
989 action_name = string_compose (X_("monitor-dim-%1"), chn);
990 MON_BIND(action_name.c_str(), dim_channel, chn);
991 action_name = string_compose (X_("monitor-solo-%1"), chn);
992 MON_BIND(action_name.c_str(), solo_channel, chn);
993 action_name = string_compose (X_("monitor-invert-%1"), chn);
994 MON_BIND(action_name.c_str(), invert_channel, chn);
998 #define SOLO_RADIO(NAME, FUNC) \
999 act = ActionManager::get_action (X_("Solo"), NAME); \
1000 ract = Glib::RefPtr<RadioAction>::cast_dynamic (act); \
1002 ract->signal_toggled().connect (sigc::mem_fun (*this, &MonitorSection::FUNC)); \
1004 Glib::RefPtr<RadioAction> ract;
1005 SOLO_RADIO ("solo-use-in-place", solo_use_in_place);
1006 SOLO_RADIO ("solo-use-afl", solo_use_afl);
1007 SOLO_RADIO ("solo-use-pfl", solo_use_pfl);
1012 MonitorSection::solo_use_in_place ()
1014 /* this is driven by a toggle on a radio group, and so is invoked twice,
1015 once for the item that became inactive and once for the one that became
1019 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
1022 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1024 if (!ract->get_active ()) {
1025 /* We are turning SiP off, which means that AFL or PFL will be turned on
1026 shortly; don't update the solo model in the mean time, as if the currently
1027 configured listen position is not the one that is about to be turned on,
1028 things will go wrong.
1030 _inhibit_solo_model_update = true;
1032 Config->set_solo_control_is_listen_control (!ract->get_active());
1033 _inhibit_solo_model_update = false;
1039 MonitorSection::solo_use_afl ()
1041 /* this is driven by a toggle on a radio group, and so is invoked twice,
1042 once for the item that became inactive and once for the one that became
1046 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
1048 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1050 if (ract->get_active()) {
1051 Config->set_solo_control_is_listen_control (true);
1052 Config->set_listen_position (AfterFaderListen);
1059 MonitorSection::solo_use_pfl ()
1061 /* this is driven by a toggle on a radio group, and so is invoked twice,
1062 once for the item that became inactive and once for the one that became
1066 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
1068 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1070 if (ract->get_active()) {
1071 Config->set_solo_control_is_listen_control (true);
1072 Config->set_listen_position (PreFaderListen);
1079 MonitorSection::update_solo_model ()
1081 if (_inhibit_solo_model_update) {
1085 const char* action_name = 0;
1086 Glib::RefPtr<Action> act;
1088 if (Config->get_solo_control_is_listen_control()) {
1089 switch (Config->get_listen_position()) {
1090 case AfterFaderListen:
1091 action_name = X_("solo-use-afl");
1093 case PreFaderListen:
1094 action_name = X_("solo-use-pfl");
1098 action_name = X_("solo-use-in-place");
1101 act = ActionManager::get_action (X_("Solo"), action_name);
1104 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1106 /* because these are radio buttons, one of them will be
1107 active no matter what. to trigger a change in the
1108 action so that the view picks it up, toggle it.
1110 if (ract->get_active()) {
1111 ract->set_active (false);
1113 ract->set_active (true);
1120 MonitorSection::map_state ()
1122 if (!_route || !_monitor) {
1126 Glib::RefPtr<Action> act;
1128 update_solo_model ();
1130 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
1132 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1134 tact->set_active (_monitor->cut_all());
1138 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
1140 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1142 tact->set_active (_monitor->dim_all());
1146 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
1148 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1150 tact->set_active (_monitor->mono());
1154 uint32_t nchans = _monitor->output_streams().n_audio();
1156 assert (nchans == _channel_buttons.size ());
1158 for (uint32_t n = 0; n < nchans; ++n) {
1160 char action_name[32];
1162 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1163 act = ActionManager::get_action (X_("Monitor"), action_name);
1165 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1167 tact->set_active (_monitor->cut (n));
1171 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1172 act = ActionManager::get_action (X_("Monitor"), action_name);
1174 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1176 tact->set_active (_monitor->dimmed (n));
1180 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1181 act = ActionManager::get_action (X_("Monitor"), action_name);
1183 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1185 tact->set_active (_monitor->soloed (n));
1189 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1190 act = ActionManager::get_action (X_("Monitor"), action_name);
1192 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1194 tact->set_active (_monitor->inverted (n));
1201 MonitorSection::do_blink (bool onoff)
1204 audition_blink (onoff);
1208 MonitorSection::audition_blink (bool onoff)
1210 if (_session == 0) {
1214 if (_session->is_auditioning()) {
1215 rude_audition_button.set_active (onoff);
1217 rude_audition_button.set_active (false);
1222 MonitorSection::solo_blink (bool onoff)
1224 if (_session == 0) {
1228 if (_session->soloing() || _session->listening()) {
1229 rude_solo_button.set_active (onoff);
1231 if (_session->soloing()) {
1232 if (_session->solo_isolated()) {
1233 rude_iso_button.set_active (onoff);
1235 rude_iso_button.set_active (false);
1240 rude_solo_button.set_active (false);
1241 rude_iso_button.set_active (false);
1246 MonitorSection::cancel_isolate (GdkEventButton*)
1249 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1250 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_isolate_control), 0.0, Controllable::NoGroup);
1257 MonitorSection::cancel_audition (GdkEventButton*)
1260 _session->cancel_audition();
1265 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1267 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1268 if (tact && tact->get_active() != value) { \
1269 tact->set_active(value); \
1274 MonitorSection::parameter_changed (std::string name)
1276 if (name == "solo-control-is-listen-control") {
1277 update_solo_model ();
1278 } else if (name == "listen-position") {
1279 update_solo_model ();
1280 } else if (name == "solo-mute-override") {
1281 SYNCHRONIZE_TOGGLE_ACTION(
1282 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1283 Config->get_solo_mute_override ())
1284 } else if (name == "exclusive-solo") {
1285 SYNCHRONIZE_TOGGLE_ACTION(
1286 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1287 Config->get_exclusive_solo ())
1292 MonitorSection::assign_controllables ()
1294 boost::shared_ptr<Controllable> none;
1296 if (!gain_control) {
1297 /* too early - GUI controls not set up yet */
1302 solo_cut_control->set_controllable (_session->solo_cut_control());
1303 solo_cut_display->set_controllable (_session->solo_cut_control());
1305 solo_cut_control->set_controllable (none);
1306 solo_cut_display->set_controllable (none);
1310 gain_control->set_controllable (_route->gain_control());
1311 gain_display->set_controllable (_route->gain_control());
1313 gain_control->set_controllable (none);
1318 cut_all_button.set_controllable (_monitor->cut_control());
1319 cut_all_button.watch ();
1320 dim_all_button.set_controllable (_monitor->dim_control());
1321 dim_all_button.watch ();
1322 mono_button.set_controllable (_monitor->mono_control());
1323 mono_button.watch ();
1325 dim_control->set_controllable (_monitor->dim_level_control ());
1326 dim_display->set_controllable (_monitor->dim_level_control ());
1327 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1328 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1332 cut_all_button.set_controllable (none);
1333 dim_all_button.set_controllable (none);
1334 mono_button.set_controllable (none);
1336 dim_control->set_controllable (none);
1337 dim_display->set_controllable (none);
1338 solo_boost_control->set_controllable (none);
1339 solo_boost_display->set_controllable (none);
1344 MonitorSection::state_id() const
1346 return "monitor-section";
1350 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1352 using namespace Menu_Helpers;
1354 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1358 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1359 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1363 if (i != output_menu_bundles.end()) {
1367 output_menu_bundles.push_back (b);
1369 MenuList& citems = output_menu.items();
1371 std::string n = b->name ();
1372 replace_all (n, "_", " ");
1374 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1378 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1381 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1383 if (std::find (current.begin(), current.end(), c) == current.end()) {
1384 _route->output()->connect_ports_to_bundle (c, true, this);
1386 _route->output()->disconnect_ports_from_bundle (c, this);
1391 MonitorSection::output_release (GdkEventButton *ev)
1393 switch (ev->button) {
1395 edit_output_configuration ();
1402 struct RouteCompareByName {
1403 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1404 return a->name().compare (b->name()) < 0;
1409 MonitorSection::output_press (GdkEventButton *ev)
1411 using namespace Menu_Helpers;
1413 MessageDialog msg (_("No session - no I/O changes are possible"));
1418 MenuList& citems = output_menu.items();
1419 switch (ev->button) {
1422 return false; //wait for the mouse-up to pop the dialog
1426 output_menu.set_name ("ArdourContextMenu");
1428 output_menu_bundles.clear ();
1430 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1432 citems.push_back (SeparatorElem());
1433 uint32_t const n_with_separator = citems.size ();
1435 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1437 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1439 /* give user bundles first chance at being in the menu */
1441 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1442 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1443 maybe_add_bundle_to_output_menu (*i, current);
1447 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1448 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1449 maybe_add_bundle_to_output_menu (*i, current);
1453 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1454 RouteList copy = *routes;
1455 copy.sort (RouteCompareByName ());
1456 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1457 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1460 if (citems.size() == n_with_separator) {
1461 /* no routes added; remove the separator */
1465 citems.push_back (SeparatorElem());
1466 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1468 output_menu.popup (1, ev->time);
1479 MonitorSection::update_output_display ()
1481 if (!_route || !_monitor || _session->deletion_in_progress()) {
1487 boost::shared_ptr<Port> port;
1488 vector<string> port_connections;
1490 uint32_t total_connection_count = 0;
1491 uint32_t io_connection_count = 0;
1492 uint32_t ardour_connection_count = 0;
1493 uint32_t system_connection_count = 0;
1494 uint32_t other_connection_count = 0;
1496 ostringstream label;
1498 bool have_label = false;
1499 bool each_io_has_one_connection = true;
1501 string connection_name;
1502 string ardour_track_name;
1503 string other_connection_type;
1504 string system_ports;
1507 ostringstream tooltip;
1508 char * tooltip_cstr;
1510 io_count = _route->n_outputs().n_total();
1511 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1514 for (io_index = 0; io_index < io_count; ++io_index) {
1516 port = _route->output()->nth (io_index);
1518 //ignore any port connections that don't match our DataType
1519 if (port->type() != DataType::AUDIO) {
1523 port_connections.clear ();
1524 port->get_connections(port_connections);
1525 io_connection_count = 0;
1527 if (!port_connections.empty()) {
1528 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1530 string& connection_name (*i);
1532 if (connection_name.find("system:") == 0) {
1533 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1536 if (io_connection_count == 0) {
1537 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1539 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1542 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1545 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1546 if (ardour_track_name.empty()) {
1547 // "ardour:Master/in 1" -> "ardour:Master/"
1548 string::size_type slash = connection_name.find("/");
1549 if (slash != string::npos) {
1550 ardour_track_name = connection_name.substr(0, slash + 1);
1554 if (connection_name.find(ardour_track_name) == 0) {
1555 ++ardour_connection_count;
1557 } else if (!pn.empty()) {
1558 if (system_ports.empty()) {
1561 system_ports += "/" + pn;
1563 if (connection_name.find("system:") == 0) {
1564 ++system_connection_count;
1566 } else if (connection_name.find("system:") == 0) {
1567 // "system:playback_123" -> "123"
1568 system_port = connection_name.substr(16);
1569 if (system_ports.empty()) {
1570 system_ports += system_port;
1572 system_ports += "/" + system_port;
1575 ++system_connection_count;
1577 if (other_connection_type.empty()) {
1578 // "jamin:in 1" -> "jamin:"
1579 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1582 if (connection_name.find(other_connection_type) == 0) {
1583 ++other_connection_count;
1587 ++total_connection_count;
1588 ++io_connection_count;
1592 if (io_connection_count != 1) {
1593 each_io_has_one_connection = false;
1597 if (total_connection_count == 0) {
1598 tooltip << endl << _("Disconnected");
1601 tooltip_cstr = new char[tooltip.str().size() + 1];
1602 strcpy(tooltip_cstr, tooltip.str().c_str());
1604 set_tooltip (output_button, tooltip_cstr, "");
1606 if (each_io_has_one_connection) {
1607 if (total_connection_count == ardour_connection_count) {
1608 // all connections are to the same track in ardour
1609 // "ardour:Master/" -> "Master"
1610 string::size_type slash = ardour_track_name.find("/");
1611 if (slash != string::npos) {
1612 label << ardour_track_name.substr(7, slash - 7);
1615 } else if (total_connection_count == system_connection_count) {
1616 // all connections are to system ports
1617 label << system_ports;
1619 } else if (total_connection_count == other_connection_count) {
1620 // all connections are to the same external program eg jamin
1621 // "jamin:" -> "jamin"
1622 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1628 if (total_connection_count == 0) {
1632 // Odd configuration
1633 label << "*" << total_connection_count << "*";
1637 output_button->set_text (label.str());
1641 MonitorSection::disconnect_output ()
1644 _route->output()->disconnect(this);
1649 MonitorSection::edit_output_configuration ()
1651 if (_output_selector == 0) {
1652 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1654 _output_selector->present ();
1658 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1663 boost::shared_ptr<Port> a = wa.lock ();
1664 boost::shared_ptr<Port> b = wb.lock ();
1665 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1666 update_output_display ();
1671 MonitorSection::load_bindings ()
1673 bindings = Bindings::get_bindings (X_("Monitor Section"), myactions);
1677 MonitorSection::help_count_processors (boost::weak_ptr<Processor> p, uint32_t* cnt) const
1679 boost::shared_ptr<Processor> processor (p.lock ());
1680 if (!processor || !processor->display_to_user()) {
1683 if (boost::dynamic_pointer_cast<Amp>(processor)) {
1690 MonitorSection::count_processors ()
1692 uint32_t processor_count = 0;
1694 _route->foreach_processor (sigc::bind (sigc::mem_fun (*this, &MonitorSection::help_count_processors), &processor_count));
1696 return processor_count;
1700 MonitorSection::processors_changed (ARDOUR::RouteProcessorChange)
1702 update_processor_box ();