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/user_bundle.h"
40 #include "ardour/plugin_manager.h"
42 #include "ardour_ui.h"
43 #include "gui_thread.h"
44 #include "monitor_section.h"
45 #include "public_editor.h"
48 #include "ui_config.h"
53 using namespace ARDOUR;
54 using namespace ARDOUR_UI_UTILS;
56 using namespace Gtkmm2ext;
60 Glib::RefPtr<ActionGroup> MonitorSection::monitor_actions;
62 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
64 MonitorSection::MonitorSection (Session* s)
68 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
69 , *channel_table_scroller.get_vadjustment ())
72 , solo_boost_control (0)
73 , solo_cut_control (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 , toggle_processorbox_button (ArdourButton::default_elements)
85 , _inhibit_solo_model_update (false)
86 , _ui_initialized (false)
87 , myactions (X_("monitor section"))
91 using namespace Menu_Helpers;
93 Glib::RefPtr<Action> act;
95 if (!monitor_actions) {
99 set_data ("ardour-bindings", bindings);
100 ARDOUR_UI::instance()->add_keyboard_binding_tab (_("Monitor Section"), *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 ARDOUR_UI::instance()->remove_keyboard_binding_tab (_("Monitor Section"));
497 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
501 _channel_buttons.clear ();
502 _output_changed_connection.disconnect ();
504 delete insert_box; insert_box = 0;
505 delete output_button; output_button = 0;
506 delete gain_control; gain_control = 0;
507 delete gain_display; gain_display = 0;
508 delete dim_control; dim_control = 0;
509 delete dim_display; dim_display = 0;
510 delete solo_boost_control; solo_boost_control = 0;
511 delete solo_boost_display; solo_boost_display = 0;
512 delete solo_cut_control; solo_cut_control = 0;
513 delete solo_cut_display; solo_cut_display = 0;
514 delete _tearoff; _tearoff = 0;
515 delete _output_selector; _output_selector = 0;
519 MonitorSection::enter_handler (GdkEventCrossing* ev)
526 MonitorSection::leave_handler (GdkEventCrossing* ev)
528 switch (ev->detail) {
529 case GDK_NOTIFY_INFERIOR:
535 /* cancel focus if we're not torn off. With X11 WM's that do
536 * focus-follows-mouse, focus will be taken from us anyway.
539 Widget* top = get_toplevel();
541 if (top->is_toplevel() && top != &_tearoff->tearoff_window()) {
542 Window* win = dynamic_cast<Window*> (top);
543 gtk_window_set_focus (win->gobj(), 0);
550 MonitorSection::update_processor_box ()
552 bool show_processor_box = Glib::RefPtr<ToggleAction>::cast_dynamic (proctoggle)->get_active ();
554 if (count_processors () > 0 && !show_processor_box) {
555 toggle_processorbox_button.set_name (X_("monitor section processors present"));
557 toggle_processorbox_button.set_name (X_("monitor section processors toggle"));
560 if (insert_box->is_visible() == show_processor_box) {
564 if (show_processor_box) {
565 if (master_packer.get_parent()) {
566 master_packer.get_parent()->remove (master_packer);
569 vpacker.pack_start (master_packer, false, false, PX_SCALE(10));
571 if (master_packer.get_parent()) {
572 master_packer.get_parent()->remove (master_packer);
575 vpacker.pack_start (master_packer, true, false, PX_SCALE(10));
580 MonitorSection::set_session (Session* s)
582 AxisView::set_session (s);
583 _plugin_selector->set_session (_session);
587 _route = _session->monitor_out ();
590 /* session with monitor section */
591 _monitor = _route->monitor_control ();
592 assign_controllables ();
593 _route->output()->changed.connect (_output_changed_connection, invalidator (*this),
594 boost::bind (&MonitorSection::update_output_display, this),
596 insert_box->set_route (_route);
597 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&MonitorSection::processors_changed, this, _1), gui_context());
598 if (_ui_initialized) {
599 update_processor_box ();
602 /* session with no monitor section */
603 _output_changed_connection.disconnect();
606 delete _output_selector;
607 _output_selector = 0;
610 if (channel_table_scroller.get_parent()) {
611 /* scroller is packed, so remove it */
612 channel_table_packer.remove (channel_table_scroller);
615 if (table_hpacker.get_parent () == &channel_table_packer) {
616 /* this occurs when the table hpacker is directly
617 packed, so remove it.
619 channel_table_packer.remove (table_hpacker);
620 } else if (table_hpacker.get_parent()) {
621 channel_table_viewport.remove ();
624 if (_monitor->output_streams().n_audio() > 7) {
625 /* put the table into a scrolled window, and then put
626 * that into the channel vpacker, after the table header
628 channel_table_viewport.add (table_hpacker);
629 channel_table_packer.pack_start (channel_table_scroller, true, true);
630 channel_table_viewport.show ();
631 channel_table_scroller.show ();
634 /* just put the channel table itself into the channel
635 * vpacker, after the table header
638 channel_table_packer.pack_start (table_hpacker, true, true);
639 channel_table_scroller.hide ();
642 table_hpacker.show ();
643 channel_table.show ();
648 _output_changed_connection.disconnect();
651 control_connections.drop_connections ();
652 rude_iso_button.unset_active_state ();
653 rude_solo_button.unset_active_state ();
654 delete _output_selector;
655 _output_selector = 0;
657 assign_controllables ();
661 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
663 cut.set_name (X_("mute button"));
664 dim.set_name (X_("monitor section dim"));
665 solo.set_name (X_("solo button"));
666 invert.set_name (X_("invert button"));
668 cut.unset_flags (Gtk::CAN_FOCUS);
669 dim.unset_flags (Gtk::CAN_FOCUS);
670 solo.unset_flags (Gtk::CAN_FOCUS);
671 invert.unset_flags (Gtk::CAN_FOCUS);
675 MonitorSection::populate_buttons ()
681 Glib::RefPtr<Action> act;
682 uint32_t nchans = _monitor->output_streams().n_audio();
684 channel_table.resize (nchans, 5);
685 channel_table.set_col_spacings (6);
686 channel_table.set_row_spacings (6);
687 channel_table.set_homogeneous (true);
689 const uint32_t row_offset = 0;
691 for (uint32_t i = 0; i < nchans; ++i) {
704 snprintf (buf, sizeof (buf), "%d", i+1);
708 Label* label = manage (new Label (l));
709 channel_table.attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
711 ChannelButtonSet* cbs = new ChannelButtonSet;
713 _channel_buttons.push_back (cbs);
715 channel_table.attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
716 channel_table.attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
717 channel_table.attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
718 channel_table.attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
720 snprintf (buf, sizeof (buf), "monitor-cut-%u", i);
721 act = ActionManager::get_action (X_("Monitor"), buf);
723 cbs->cut.set_related_action (act);
726 snprintf (buf, sizeof (buf), "monitor-dim-%u", i);
727 act = ActionManager::get_action (X_("Monitor"), buf);
729 cbs->dim.set_related_action (act);
732 snprintf (buf, sizeof (buf), "monitor-solo-%u", i);
733 act = ActionManager::get_action (X_("Monitor"), buf);
735 cbs->solo.set_related_action (act);
738 snprintf (buf, sizeof (buf), "monitor-invert-%u", i);
739 act = ActionManager::get_action (X_("Monitor"), buf);
741 cbs->invert.set_related_action (act);
745 channel_table.show_all ();
749 MonitorSection::toggle_exclusive_solo ()
755 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo");
757 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
758 Config->set_exclusive_solo (tact->get_active());
764 MonitorSection::toggle_mute_overrides_solo ()
770 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo");
772 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
773 Config->set_solo_mute_override (tact->get_active());
778 MonitorSection::dim_all ()
784 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
786 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
787 _monitor->set_dim_all (tact->get_active());
793 MonitorSection::cut_all ()
799 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
801 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
802 _monitor->set_cut_all (tact->get_active());
807 MonitorSection::mono ()
813 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
815 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
816 _monitor->set_mono (tact->get_active());
821 MonitorSection::cut_channel (uint32_t chn)
828 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
830 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
832 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
833 _monitor->set_cut (chn, tact->get_active());
838 MonitorSection::dim_channel (uint32_t chn)
845 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
847 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
849 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
850 _monitor->set_dim (chn, tact->get_active());
856 MonitorSection::solo_channel (uint32_t chn)
863 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
865 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
867 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
868 _monitor->set_solo (chn, tact->get_active());
874 MonitorSection::invert_channel (uint32_t chn)
881 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
883 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Monitor"), buf);
885 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
886 _monitor->set_polarity (chn, tact->get_active());
891 MonitorSection::register_actions ()
895 Glib::RefPtr<Action> act;
897 monitor_actions = myactions.create_action_group (X_("Monitor"));
899 myactions.register_toggle_action (monitor_actions, "monitor-mono", _("Switch monitor to mono"),
900 sigc::mem_fun (*this, &MonitorSection::mono));
902 myactions.register_toggle_action (monitor_actions, "monitor-cut-all", _("Cut monitor"),
903 sigc::mem_fun (*this, &MonitorSection::cut_all));
905 myactions.register_toggle_action (monitor_actions, "monitor-dim-all", _("Dim monitor"),
906 sigc::mem_fun (*this, &MonitorSection::dim_all));
908 act = myactions.register_toggle_action (monitor_actions, "toggle-exclusive-solo", _("Toggle exclusive solo mode"),
909 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
911 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
912 tact->set_active (Config->get_exclusive_solo());
914 act = myactions.register_toggle_action (monitor_actions, "toggle-mute-overrides-solo", _("Toggle mute overrides solo mode"),
915 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
917 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
918 tact->set_active (Config->get_solo_mute_override());
920 for (uint32_t chn = 0; chn < 16; ++chn) {
922 action_name = string_compose (X_("monitor-cut-%1"), chn);
923 action_descr = string_compose (_("Cut monitor channel %1"), chn);
924 myactions.register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
925 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
927 action_name = string_compose (X_("monitor-dim-%1"), chn);
928 action_descr = string_compose (_("Dim monitor channel %1"), chn);
929 myactions.register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
930 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
932 action_name = string_compose (X_("monitor-solo-%1"), chn);
933 action_descr = string_compose (_("Solo monitor channel %1"), chn);
934 myactions.register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
935 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
937 action_name = string_compose (X_("monitor-invert-%1"), chn);
938 action_descr = string_compose (_("Invert monitor channel %1"), chn);
939 myactions.register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
940 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
945 Glib::RefPtr<ActionGroup> solo_actions = myactions.create_action_group (X_("Solo"));
946 RadioAction::Group solo_group;
948 myactions.register_radio_action (solo_actions, solo_group, "solo-use-in-place", _("In-place solo"),
949 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
950 myactions.register_radio_action (solo_actions, solo_group, "solo-use-afl", _("After Fade Listen (AFL) solo"),
951 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
952 myactions.register_radio_action (solo_actions, solo_group, "solo-use-pfl", _("Pre Fade Listen (PFL) solo"),
953 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
955 myactions.register_toggle_action (monitor_actions, "toggle-monitor-processor-box", _("Toggle Monitor Section Processor Box"),
956 sigc::mem_fun(*this, &MonitorSection::update_processor_box));
960 MonitorSection::connect_actions ()
962 Glib::RefPtr<Action> act;
963 Glib::RefPtr<ToggleAction> tact;
965 #define MON_TOG(NAME, FUNC) \
966 act = ActionManager::get_action (X_("Monitor"), NAME); \
967 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act); \
969 tact->signal_toggled().connect (sigc::mem_fun (*this, &MonitorSection::FUNC)); \
971 MON_TOG("monitor-mono", mono);
972 MON_TOG("monitor-cut-all", cut_all);
973 MON_TOG("monitor-dim-all", dim_all);
975 MON_TOG("toggle-exclusive-solo", toggle_exclusive_solo);
976 tact->set_active (Config->get_exclusive_solo());
978 MON_TOG("toggle-mute-overrides-solo", toggle_mute_overrides_solo);
979 tact->set_active (Config->get_solo_mute_override());
982 #define MON_BIND(NAME, FUNC, ARG) \
983 act = ActionManager::get_action (X_("Monitor"), NAME); \
984 tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act); \
986 tact->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MonitorSection::FUNC), ARG));
988 for (uint32_t chn = 0; chn < 16; ++chn) {
989 std::string action_name = string_compose (X_("monitor-cut-%1"), chn);
990 MON_BIND(action_name.c_str(), cut_channel, chn);
991 action_name = string_compose (X_("monitor-dim-%1"), chn);
992 MON_BIND(action_name.c_str(), dim_channel, chn);
993 action_name = string_compose (X_("monitor-solo-%1"), chn);
994 MON_BIND(action_name.c_str(), solo_channel, chn);
995 action_name = string_compose (X_("monitor-invert-%1"), chn);
996 MON_BIND(action_name.c_str(), invert_channel, chn);
1000 #define SOLO_RADIO(NAME, FUNC) \
1001 act = ActionManager::get_action (X_("Solo"), NAME); \
1002 ract = Glib::RefPtr<RadioAction>::cast_dynamic (act); \
1004 ract->signal_toggled().connect (sigc::mem_fun (*this, &MonitorSection::FUNC)); \
1006 Glib::RefPtr<RadioAction> ract;
1007 SOLO_RADIO ("solo-use-in-place", solo_use_in_place);
1008 SOLO_RADIO ("solo-use-afl", solo_use_afl);
1009 SOLO_RADIO ("solo-use-pfl", solo_use_pfl);
1014 MonitorSection::solo_use_in_place ()
1016 /* this is driven by a toggle on a radio group, and so is invoked twice,
1017 once for the item that became inactive and once for the one that became
1021 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
1024 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1026 if (!ract->get_active ()) {
1027 /* We are turning SiP off, which means that AFL or PFL will be turned on
1028 shortly; don't update the solo model in the mean time, as if the currently
1029 configured listen position is not the one that is about to be turned on,
1030 things will go wrong.
1032 _inhibit_solo_model_update = true;
1034 Config->set_solo_control_is_listen_control (!ract->get_active());
1035 _inhibit_solo_model_update = false;
1041 MonitorSection::solo_use_afl ()
1043 /* this is driven by a toggle on a radio group, and so is invoked twice,
1044 once for the item that became inactive and once for the one that became
1048 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
1050 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1052 if (ract->get_active()) {
1053 Config->set_solo_control_is_listen_control (true);
1054 Config->set_listen_position (AfterFaderListen);
1061 MonitorSection::solo_use_pfl ()
1063 /* this is driven by a toggle on a radio group, and so is invoked twice,
1064 once for the item that became inactive and once for the one that became
1068 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
1070 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1072 if (ract->get_active()) {
1073 Config->set_solo_control_is_listen_control (true);
1074 Config->set_listen_position (PreFaderListen);
1081 MonitorSection::update_solo_model ()
1083 if (_inhibit_solo_model_update) {
1087 const char* action_name = 0;
1088 Glib::RefPtr<Action> act;
1090 if (Config->get_solo_control_is_listen_control()) {
1091 switch (Config->get_listen_position()) {
1092 case AfterFaderListen:
1093 action_name = X_("solo-use-afl");
1095 case PreFaderListen:
1096 action_name = X_("solo-use-pfl");
1100 action_name = X_("solo-use-in-place");
1103 act = ActionManager::get_action (X_("Solo"), action_name);
1106 Glib::RefPtr<RadioAction> ract = Glib::RefPtr<RadioAction>::cast_dynamic (act);
1108 /* because these are radio buttons, one of them will be
1109 active no matter what. to trigger a change in the
1110 action so that the view picks it up, toggle it.
1112 if (ract->get_active()) {
1113 ract->set_active (false);
1115 ract->set_active (true);
1122 MonitorSection::map_state ()
1124 if (!_route || !_monitor) {
1128 Glib::RefPtr<Action> act;
1130 update_solo_model ();
1132 act = ActionManager::get_action (X_("Monitor"), "monitor-cut-all");
1134 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1136 tact->set_active (_monitor->cut_all());
1140 act = ActionManager::get_action (X_("Monitor"), "monitor-dim-all");
1142 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1144 tact->set_active (_monitor->dim_all());
1148 act = ActionManager::get_action (X_("Monitor"), "monitor-mono");
1150 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1152 tact->set_active (_monitor->mono());
1156 uint32_t nchans = _monitor->output_streams().n_audio();
1158 assert (nchans == _channel_buttons.size ());
1160 for (uint32_t n = 0; n < nchans; ++n) {
1162 char action_name[32];
1164 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1165 act = ActionManager::get_action (X_("Monitor"), action_name);
1167 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1169 tact->set_active (_monitor->cut (n));
1173 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1174 act = ActionManager::get_action (X_("Monitor"), action_name);
1176 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1178 tact->set_active (_monitor->dimmed (n));
1182 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1183 act = ActionManager::get_action (X_("Monitor"), action_name);
1185 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1187 tact->set_active (_monitor->soloed (n));
1191 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1192 act = ActionManager::get_action (X_("Monitor"), action_name);
1194 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
1196 tact->set_active (_monitor->inverted (n));
1203 MonitorSection::do_blink (bool onoff)
1206 audition_blink (onoff);
1210 MonitorSection::audition_blink (bool onoff)
1212 if (_session == 0) {
1216 if (_session->is_auditioning()) {
1217 rude_audition_button.set_active (onoff);
1219 rude_audition_button.set_active (false);
1224 MonitorSection::solo_blink (bool onoff)
1226 if (_session == 0) {
1230 if (_session->soloing() || _session->listening()) {
1231 rude_solo_button.set_active (onoff);
1233 if (_session->soloing()) {
1234 if (_session->solo_isolated()) {
1235 rude_iso_button.set_active (onoff);
1237 rude_iso_button.set_active (false);
1242 rude_solo_button.set_active (false);
1243 rude_iso_button.set_active (false);
1248 MonitorSection::cancel_isolate (GdkEventButton*)
1251 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1252 _session->set_solo_isolated (rl, false, Session::rt_cleanup, Controllable::NoGroup);
1259 MonitorSection::cancel_audition (GdkEventButton*)
1262 _session->cancel_audition();
1267 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
1269 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(action); \
1270 if (tact && tact->get_active() != value) { \
1271 tact->set_active(value); \
1276 MonitorSection::parameter_changed (std::string name)
1278 if (name == "solo-control-is-listen-control") {
1279 update_solo_model ();
1280 } else if (name == "listen-position") {
1281 update_solo_model ();
1282 } else if (name == "solo-mute-override") {
1283 SYNCHRONIZE_TOGGLE_ACTION(
1284 ActionManager::get_action (X_("Monitor"), "toggle-mute-overrides-solo"),
1285 Config->get_solo_mute_override ())
1286 } else if (name == "exclusive-solo") {
1287 SYNCHRONIZE_TOGGLE_ACTION(
1288 ActionManager::get_action (X_("Monitor"), "toggle-exclusive-solo"),
1289 Config->get_exclusive_solo ())
1294 MonitorSection::assign_controllables ()
1296 boost::shared_ptr<Controllable> none;
1298 if (!gain_control) {
1299 /* too early - GUI controls not set up yet */
1304 solo_cut_control->set_controllable (_session->solo_cut_control());
1305 solo_cut_display->set_controllable (_session->solo_cut_control());
1307 solo_cut_control->set_controllable (none);
1308 solo_cut_display->set_controllable (none);
1312 gain_control->set_controllable (_route->gain_control());
1313 gain_display->set_controllable (_route->gain_control());
1315 gain_control->set_controllable (none);
1320 cut_all_button.set_controllable (_monitor->cut_control());
1321 cut_all_button.watch ();
1322 dim_all_button.set_controllable (_monitor->dim_control());
1323 dim_all_button.watch ();
1324 mono_button.set_controllable (_monitor->mono_control());
1325 mono_button.watch ();
1327 dim_control->set_controllable (_monitor->dim_level_control ());
1328 dim_display->set_controllable (_monitor->dim_level_control ());
1329 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1330 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1334 cut_all_button.set_controllable (none);
1335 dim_all_button.set_controllable (none);
1336 mono_button.set_controllable (none);
1338 dim_control->set_controllable (none);
1339 dim_display->set_controllable (none);
1340 solo_boost_control->set_controllable (none);
1341 solo_boost_display->set_controllable (none);
1346 MonitorSection::state_id() const
1348 return "monitor-section";
1352 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1354 using namespace Menu_Helpers;
1356 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1360 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1361 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1365 if (i != output_menu_bundles.end()) {
1369 output_menu_bundles.push_back (b);
1371 MenuList& citems = output_menu.items();
1373 std::string n = b->name ();
1374 replace_all (n, "_", " ");
1376 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1380 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1383 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1385 if (std::find (current.begin(), current.end(), c) == current.end()) {
1386 _route->output()->connect_ports_to_bundle (c, true, this);
1388 _route->output()->disconnect_ports_from_bundle (c, this);
1393 MonitorSection::output_release (GdkEventButton *ev)
1395 switch (ev->button) {
1397 edit_output_configuration ();
1404 struct RouteCompareByName {
1405 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1406 return a->name().compare (b->name()) < 0;
1411 MonitorSection::output_press (GdkEventButton *ev)
1413 using namespace Menu_Helpers;
1415 MessageDialog msg (_("No session - no I/O changes are possible"));
1420 MenuList& citems = output_menu.items();
1421 switch (ev->button) {
1424 return false; //wait for the mouse-up to pop the dialog
1428 output_menu.set_name ("ArdourContextMenu");
1430 output_menu_bundles.clear ();
1432 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1434 citems.push_back (SeparatorElem());
1435 uint32_t const n_with_separator = citems.size ();
1437 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1439 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1441 /* give user bundles first chance at being in the menu */
1443 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1444 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1445 maybe_add_bundle_to_output_menu (*i, current);
1449 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1450 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1451 maybe_add_bundle_to_output_menu (*i, current);
1455 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1456 RouteList copy = *routes;
1457 copy.sort (RouteCompareByName ());
1458 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1459 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1462 if (citems.size() == n_with_separator) {
1463 /* no routes added; remove the separator */
1467 citems.push_back (SeparatorElem());
1468 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1470 output_menu.popup (1, ev->time);
1481 MonitorSection::update_output_display ()
1483 if (!_route || !_monitor || _session->deletion_in_progress()) {
1489 boost::shared_ptr<Port> port;
1490 vector<string> port_connections;
1492 uint32_t total_connection_count = 0;
1493 uint32_t io_connection_count = 0;
1494 uint32_t ardour_connection_count = 0;
1495 uint32_t system_connection_count = 0;
1496 uint32_t other_connection_count = 0;
1498 ostringstream label;
1500 bool have_label = false;
1501 bool each_io_has_one_connection = true;
1503 string connection_name;
1504 string ardour_track_name;
1505 string other_connection_type;
1506 string system_ports;
1509 ostringstream tooltip;
1510 char * tooltip_cstr;
1512 io_count = _route->n_outputs().n_total();
1513 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1516 for (io_index = 0; io_index < io_count; ++io_index) {
1518 port = _route->output()->nth (io_index);
1520 //ignore any port connections that don't match our DataType
1521 if (port->type() != DataType::AUDIO) {
1525 port_connections.clear ();
1526 port->get_connections(port_connections);
1527 io_connection_count = 0;
1529 if (!port_connections.empty()) {
1530 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1532 string& connection_name (*i);
1534 if (connection_name.find("system:") == 0) {
1535 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1538 if (io_connection_count == 0) {
1539 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1541 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1544 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1547 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1548 if (ardour_track_name.empty()) {
1549 // "ardour:Master/in 1" -> "ardour:Master/"
1550 string::size_type slash = connection_name.find("/");
1551 if (slash != string::npos) {
1552 ardour_track_name = connection_name.substr(0, slash + 1);
1556 if (connection_name.find(ardour_track_name) == 0) {
1557 ++ardour_connection_count;
1559 } else if (!pn.empty()) {
1560 if (system_ports.empty()) {
1563 system_ports += "/" + pn;
1565 if (connection_name.find("system:") == 0) {
1566 ++system_connection_count;
1568 } else if (connection_name.find("system:") == 0) {
1569 // "system:playback_123" -> "123"
1570 system_port = connection_name.substr(16);
1571 if (system_ports.empty()) {
1572 system_ports += system_port;
1574 system_ports += "/" + system_port;
1577 ++system_connection_count;
1579 if (other_connection_type.empty()) {
1580 // "jamin:in 1" -> "jamin:"
1581 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1584 if (connection_name.find(other_connection_type) == 0) {
1585 ++other_connection_count;
1589 ++total_connection_count;
1590 ++io_connection_count;
1594 if (io_connection_count != 1) {
1595 each_io_has_one_connection = false;
1599 if (total_connection_count == 0) {
1600 tooltip << endl << _("Disconnected");
1603 tooltip_cstr = new char[tooltip.str().size() + 1];
1604 strcpy(tooltip_cstr, tooltip.str().c_str());
1606 set_tooltip (output_button, tooltip_cstr, "");
1608 if (each_io_has_one_connection) {
1609 if (total_connection_count == ardour_connection_count) {
1610 // all connections are to the same track in ardour
1611 // "ardour:Master/" -> "Master"
1612 string::size_type slash = ardour_track_name.find("/");
1613 if (slash != string::npos) {
1614 label << ardour_track_name.substr(7, slash - 7);
1617 } else if (total_connection_count == system_connection_count) {
1618 // all connections are to system ports
1619 label << system_ports;
1621 } else if (total_connection_count == other_connection_count) {
1622 // all connections are to the same external program eg jamin
1623 // "jamin:" -> "jamin"
1624 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1630 if (total_connection_count == 0) {
1634 // Odd configuration
1635 label << "*" << total_connection_count << "*";
1639 output_button->set_text (label.str());
1643 MonitorSection::disconnect_output ()
1646 _route->output()->disconnect(this);
1651 MonitorSection::edit_output_configuration ()
1653 if (_output_selector == 0) {
1654 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1656 _output_selector->present ();
1660 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1665 boost::shared_ptr<Port> a = wa.lock ();
1666 boost::shared_ptr<Port> b = wb.lock ();
1667 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1668 update_output_display ();
1673 MonitorSection::load_bindings ()
1675 bindings = Bindings::get_bindings (X_("monitor section"), myactions);
1679 MonitorSection::help_count_processors (boost::weak_ptr<Processor> p, uint32_t* cnt) const
1681 boost::shared_ptr<Processor> processor (p.lock ());
1682 if (!processor || !processor->display_to_user()) {
1685 if (boost::dynamic_pointer_cast<Amp>(processor)) {
1692 MonitorSection::count_processors ()
1694 uint32_t processor_count = 0;
1696 _route->foreach_processor (sigc::bind (sigc::mem_fun (*this, &MonitorSection::help_count_processors), &processor_count));
1698 return processor_count;
1702 MonitorSection::processors_changed (ARDOUR::RouteProcessorChange)
1704 update_processor_box ();