2 * Copyright (C) 2010-2019 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2011-2012 David Robillard <d@drobilla.net>
4 * Copyright (C) 2011 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2014-2015 Tim Mayberry <mojofunk@gmail.com>
6 * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
7 * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <gdkmm/pixbuf.h>
26 #include "pbd/compose.h"
27 #include "pbd/error.h"
28 #include "pbd/replace_all.h"
29 #include "pbd/stacktrace.h"
31 #include "gtkmm2ext/actions.h"
32 #include "gtkmm2ext/utils.h"
34 #include <gtkmm/menu.h>
35 #include <gtkmm/menuitem.h>
37 #include "widgets/tearoff.h"
38 #include "widgets/tooltips.h"
40 #include "ardour/amp.h"
41 #include "ardour/audioengine.h"
42 #include "ardour/monitor_processor.h"
43 #include "ardour/port.h"
44 #include "ardour/route.h"
45 #include "ardour/solo_isolate_control.h"
46 #include "ardour/user_bundle.h"
47 #include "ardour/plugin_manager.h"
49 #include "ardour_ui.h"
50 #include "gui_thread.h"
52 #include "monitor_section.h"
53 #include "public_editor.h"
55 #include "ui_config.h"
60 using namespace ARDOUR;
61 using namespace ArdourWidgets;
62 using namespace ARDOUR_UI_UTILS;
64 using namespace Gtkmm2ext;
68 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
70 #define SYNCHRONIZE_TOGGLE_ACTION(action, value) \
71 if (action && action->get_active() != value) { \
72 action->set_active(value); \
75 MonitorSection::MonitorSection ()
76 : RouteUI ((Session*) 0)
79 , channel_table_viewport (*channel_table_scroller.get_hadjustment()
80 , *channel_table_scroller.get_vadjustment ())
83 , solo_boost_control (0)
84 , solo_cut_control (0)
87 , solo_boost_display (0)
88 , solo_cut_display (0)
89 , _output_selector (0)
90 , solo_in_place_button (_("SiP"), ArdourButton::led_default_elements)
91 , afl_button (_("AFL"), ArdourButton::led_default_elements)
92 , pfl_button (_("PFL"), ArdourButton::led_default_elements)
93 , exclusive_solo_button (ArdourButton::led_default_elements)
94 , solo_mute_override_button (ArdourButton::led_default_elements)
95 , toggle_processorbox_button (ArdourButton::default_elements)
96 , _inhibit_solo_model_update (false)
98 , _ui_initialized (false)
100 /* note that although this a RouteUI, we never called ::set_route() so
101 * we do not need to worry about self-destructing when the Route (the
102 * monitor out) is destroyed.
105 using namespace Menu_Helpers;
107 Glib::RefPtr<Action> act;
111 set_data ("ardour-bindings", bindings);
113 channel_size_group = SizeGroup::create (SIZE_GROUP_HORIZONTAL);
115 insert_box = new ProcessorBox (0, boost::bind (&MonitorSection::plugin_selector, this), _rr_selection, 0);
116 insert_box->set_no_show_all ();
118 // TODO allow keyboard shortcuts in ProcessorBox
120 /* Rude Solo & Solo Isolated */
121 rude_solo_button.set_text (_("Soloing"));
122 rude_solo_button.set_name ("rude solo");
123 rude_solo_button.show ();
125 rude_iso_button.set_text (_("Isolated"));
126 rude_iso_button.set_name ("rude isolate");
127 rude_iso_button.show ();
129 rude_audition_button.set_text (_("Auditioning"));
130 rude_audition_button.set_name ("rude audition");
131 rude_audition_button.show ();
133 Timers::blink_connect (sigc::mem_fun (*this, &MonitorSection::do_blink));
135 UI::instance()->set_tip (rude_solo_button, _("When active, something is soloed.\nClick to de-solo everything"));
137 rude_iso_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_isolate), false);
138 UI::instance()->set_tip (rude_iso_button, _("When active, something is solo-isolated.\nClick to de-isolate everything"));
140 rude_audition_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::cancel_audition), false);
141 UI::instance()->set_tip (rude_audition_button, _("When active, auditioning is active.\nClick to stop the audition"));
143 /* SIP, AFL, PFL radio */
145 solo_in_place_button.set_name ("monitor section solo model");
146 afl_button.set_name ("monitor section solo model");
147 pfl_button.set_name ("monitor section solo model");
149 solo_in_place_button.set_led_left (true);
150 afl_button.set_led_left (true);
151 pfl_button.set_led_left (true);
153 solo_in_place_button.show ();
157 act = ActionManager::get_action (X_("Solo"), X_("solo-use-in-place"));
158 set_tooltip (solo_in_place_button, _("Solo controls affect solo-in-place"));
160 solo_in_place_button.set_related_action (act);
163 act = ActionManager::get_action (X_("Solo"), X_("solo-use-afl"));
164 set_tooltip (afl_button, _("Solo controls toggle after-fader-listen"));
166 afl_button.set_related_action (act);
169 act = ActionManager::get_action (X_("Solo"), X_("solo-use-pfl"));
170 set_tooltip (pfl_button, _("Solo controls toggle pre-fader-listen"));
172 pfl_button.set_related_action (act);
175 /* Solo option buttons */
176 exclusive_solo_button.set_text (_("Excl. Solo"));
177 exclusive_solo_button.set_name (X_("monitor section solo option"));
178 set_tooltip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
180 act = ActionManager::get_action (X_("Solo"), X_("toggle-exclusive-solo"));
182 exclusive_solo_button.set_related_action (act);
185 solo_mute_override_button.set_text (_("Solo ยป Mute"));
186 solo_mute_override_button.set_name (X_("monitor section solo option"));
187 set_tooltip (&solo_mute_override_button, _("If enabled, solo will override mute\n(a soloed & muted track or bus will be audible)"));
189 solo_mute_override_button.set_related_action (ActionManager::get_action (X_("Solo"), X_("toggle-mute-overrides-solo")));
191 /* Processor Box hide/shos */
192 toggle_processorbox_button.set_text (_("Processors"));
193 toggle_processorbox_button.set_name (X_("monitor section processors toggle"));
194 set_tooltip (&toggle_processorbox_button, _("Allow one to add monitor effect processors"));
196 proctoggle = ActionManager::get_toggle_action (X_("Monitor"), X_("toggle-monitor-processor-box"));
197 toggle_processorbox_button.set_related_action (proctoggle);
200 Label* solo_boost_label;
201 Label* solo_cut_label;
204 /* Solo Boost Knob */
206 solo_boost_control = new ArdourKnob ();
207 solo_boost_control->set_name("monitor section knob");
208 solo_boost_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
209 set_tooltip (*solo_boost_control, _("Gain increase for soloed signals (0dB is normal)"));
211 solo_boost_display = new ArdourDisplay ();
212 set_tooltip (*solo_boost_display, _("Gain increase for soloed signals (0dB is normal)"));
213 solo_boost_display->set_name("monitor section button");
214 solo_boost_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
215 solo_boost_display->add_controllable_preset(_("0 dB"), 0.0);
216 solo_boost_display->add_controllable_preset(_("3 dB"), 3.0);
217 solo_boost_display->add_controllable_preset(_("6 dB"), 6.0);
218 solo_boost_display->add_controllable_preset(_("10 dB"), 10.0);
220 solo_boost_label = manage (new Label (_("Solo Boost")));
224 solo_cut_control = new ArdourKnob ();
225 solo_cut_control->set_name ("monitor section knob");
226 solo_cut_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
227 set_tooltip (*solo_cut_control, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
229 solo_cut_display = new ArdourDisplay ();
230 set_tooltip (*solo_cut_display, _("Gain reduction non-soloed signals\nA value above -inf dB causes \"solo-in-front\""));
231 solo_cut_display->set_name("monitor section button");
232 solo_cut_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
233 solo_cut_display->add_controllable_preset(_("0 dB"), 0.0);
234 solo_cut_display->add_controllable_preset(_("-6 dB"), -6.0);
235 solo_cut_display->add_controllable_preset(_("-12 dB"), -12.0);
236 solo_cut_display->add_controllable_preset(_("-20 dB"), -20.0);
237 solo_cut_display->add_controllable_preset(_("OFF"), -1200.0);
239 solo_cut_label = manage (new Label (_("SiP Cut")));
243 dim_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
244 dim_control->set_name ("monitor section knob");
245 dim_control->set_size_request (PX_SCALE(36), PX_SCALE(36));
246 set_tooltip (*dim_control, _("Gain reduction to use when dimming monitor outputs"));
248 dim_display = new ArdourDisplay ();
249 set_tooltip (*dim_display, _("Gain reduction to use when dimming monitor outputs"));
250 dim_display->set_name ("monitor section button");
251 dim_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
252 dim_display->add_controllable_preset(_("0 dB"), 0.0);
253 dim_display->add_controllable_preset(_("-3 dB"), -3.0);
254 dim_display->add_controllable_preset(_("-6 dB"), -6.0);
255 dim_display->add_controllable_preset(_("-12 dB"), -12.0);
256 dim_display->add_controllable_preset(_("-20 dB"), -20.0);
258 dim_label = manage (new Label (_("Dim")));
261 cut_all_button.set_text (_("Mute"));
262 cut_all_button.set_name ("mute button");
263 cut_all_button.set_size_request (-1, PX_SCALE(30));
264 cut_all_button.show ();
266 act = ActionManager::get_action (X_("Monitor Section"), X_("monitor-cut-all"));
268 cut_all_button.set_related_action (act);
272 dim_all_button.set_text (_("Dim"));
273 dim_all_button.set_name ("monitor section dim");
274 dim_all_button.set_size_request (-1, PX_SCALE(25));
275 act = ActionManager::get_action (X_("Monitor Section"), X_("monitor-dim-all"));
277 dim_all_button.set_related_action (act);
281 mono_button.set_text (_("Mono"));
282 mono_button.set_name ("monitor section mono");
283 mono_button.set_size_request (-1, PX_SCALE(25));
284 act = ActionManager::get_action (X_("Monitor Section"), X_("monitor-mono"));
286 mono_button.set_related_action (act);
291 gain_control = new ArdourKnob (ArdourKnob::default_elements, ArdourKnob::Detent);
292 gain_control->set_name("monitor section knob");
293 gain_control->set_size_request (PX_SCALE(60), PX_SCALE(60));
295 gain_display = new ArdourDisplay ();
296 gain_display->set_name("monitor section button");
297 gain_display->set_size_request (PX_SCALE(68), PX_SCALE(20));
298 gain_display->add_controllable_preset(_("0 dB"), 0.0);
299 gain_display->add_controllable_preset(_("-3 dB"), -3.0);
300 gain_display->add_controllable_preset(_("-6 dB"), -6.0);
301 gain_display->add_controllable_preset(_("-12 dB"), -12.0);
302 gain_display->add_controllable_preset(_("-20 dB"), -20.0);
303 gain_display->add_controllable_preset(_("-30 dB"), -30.0);
305 Label* output_label = manage (new Label (_("Output")));
306 output_label->set_name (X_("MonitorSectionLabel"));
308 output_button = new ArdourButton ();
309 output_button->set_text (_("Output"));
310 output_button->set_name (X_("monitor section button"));
311 output_button->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
312 output_button->set_layout_ellipsize_width (PX_SCALE(128) * PANGO_SCALE);
314 channel_table_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
315 channel_table_scroller.set_size_request (-1, PX_SCALE(150));
316 channel_table_scroller.set_shadow_type (Gtk::SHADOW_NONE);
317 channel_table_scroller.show ();
318 channel_table_scroller.add (channel_table_viewport);
320 channel_size_group->add_widget (channel_table_header);
321 channel_table_header.resize (1, 5);
323 Label* l1 = manage (new Label (X_(" ")));
324 l1->set_name (X_("MonitorSectionLabel"));
325 channel_table_header.attach (*l1, 0, 1, 0, 1, EXPAND|FILL);
327 l1 = manage (new Label (_("Mute")));
328 l1->set_name (X_("MonitorSectionLabel"));
329 channel_table_header.attach (*l1, 1, 2, 0, 1, EXPAND|FILL);
331 l1 = manage (new Label (_("Dim")));
332 l1->set_name (X_("MonitorSectionLabel"));
333 channel_table_header.attach (*l1, 2, 3, 0, 1, EXPAND|FILL);
335 l1 = manage (new Label (_("Solo")));
336 l1->set_name (X_("MonitorSectionLabel"));
337 channel_table_header.attach (*l1, 3, 4, 0, 1, EXPAND|FILL);
339 l1 = manage (new Label (_("Inv")));
340 l1->set_name (X_("MonitorSectionLabel"));
341 channel_table_header.attach (*l1, 4, 5, 0, 1, EXPAND|FILL);
343 channel_table_header.show ();
346 /****************************************************************************
347 * LAYOUT top to bottom
350 Gtk::Label *top_spacer = manage (new Gtk::Label);
352 // solo, iso information
353 HBox* rude_box = manage (new HBox);
354 rude_box->set_spacing (PX_SCALE(4));
355 rude_box->set_homogeneous (true);
356 rude_box->pack_start (rude_solo_button, true, true);
357 rude_box->pack_start (rude_iso_button, true, true);
359 // solo options (right align)
360 HBox* tbx1 = manage (new HBox);
361 tbx1->pack_end (exclusive_solo_button, false, false);
363 HBox* tbx2 = manage (new HBox);
364 tbx2->pack_end (solo_mute_override_button, false, false);
366 HBox* tbx3 = manage (new HBox);
367 tbx3->pack_end (toggle_processorbox_button, false, false);
369 HBox* tbx0 = manage (new HBox); // space
371 // combined solo mode (Sip, AFL, PFL) & solo options
372 Table *solo_tbl = manage (new Table);
373 solo_tbl->attach (solo_in_place_button, 0, 1, 0, 1, EXPAND|FILL, SHRINK, 0, 2);
374 solo_tbl->attach (pfl_button, 0, 1, 1, 2, EXPAND|FILL, SHRINK, 0, 2);
375 solo_tbl->attach (afl_button, 0, 1, 2, 3, EXPAND|FILL, SHRINK, 0, 2);
376 solo_tbl->attach (*tbx0, 1, 2, 0, 3, EXPAND|FILL, SHRINK, 2, 2);
377 solo_tbl->attach (*tbx1, 2, 3, 0, 1, EXPAND|FILL, SHRINK, 0, 2);
378 solo_tbl->attach (*tbx2, 2, 3, 1, 2, EXPAND|FILL, SHRINK, 0, 2);
379 solo_tbl->attach (*tbx3, 2, 3, 2, 3, EXPAND|FILL, SHRINK, 0, 2);
381 // boost, cut, dim volume control
382 Table *level_tbl = manage (new Table);
383 level_tbl->attach (*solo_boost_label, 0, 2, 0, 1, EXPAND|FILL, SHRINK, 1, 2);
384 level_tbl->attach (*solo_boost_control, 0, 2, 1, 2, EXPAND|FILL, SHRINK, 1, 2);
385 level_tbl->attach (*solo_boost_display, 0, 2, 2, 3, EXPAND , SHRINK, 1, 2);
387 level_tbl->attach (*solo_cut_label, 2, 4, 0, 1, EXPAND|FILL, SHRINK, 1, 2);
388 level_tbl->attach (*solo_cut_control, 2, 4, 1, 2, EXPAND|FILL, SHRINK, 1, 2);
389 level_tbl->attach (*solo_cut_display, 2, 4, 2, 3, EXPAND , SHRINK, 1, 2);
391 level_tbl->attach (*dim_label, 1, 3, 3, 4, EXPAND|FILL, SHRINK, 1, 2);
392 level_tbl->attach (*dim_control, 1, 3, 4, 5, EXPAND|FILL, SHRINK, 1, 2);
393 level_tbl->attach (*dim_display, 1, 3, 5, 6, EXPAND , SHRINK, 1, 2);
396 HBox* mono_dim_box = manage (new HBox);
397 mono_dim_box->set_spacing (PX_SCALE(4));
398 mono_dim_box->set_homogeneous (true);
399 mono_dim_box->pack_start (mono_button, true, true);
400 mono_dim_box->pack_end (dim_all_button, true, true);
403 Label* spin_label = manage (new Label (_("Monitor")));
404 VBox* spin_packer = manage (new VBox);
405 spin_packer->set_spacing (PX_SCALE(2));
406 spin_packer->pack_start (*spin_label, false, false);
407 spin_packer->pack_start (*gain_control, false, false);
408 spin_packer->pack_start (*gain_display, false, false);
410 master_packer.pack_start (*spin_packer, true, false);
412 // combined gain section (channels, mute, dim)
413 VBox* lower_packer = manage (new VBox);
414 lower_packer->pack_start (channel_table_header, false, false, PX_SCALE(0));
415 lower_packer->pack_start (channel_table_packer, false, false, PX_SCALE(8));
416 lower_packer->pack_start (*mono_dim_box, false, false, PX_SCALE(2));
417 lower_packer->pack_start (cut_all_button, false, false, PX_SCALE(2));
419 // calc height of mixer scrollbar
420 int scrollbar_height = 0;
422 Gtk::Window window (WINDOW_TOPLEVEL);
423 HScrollbar scrollbar;
424 window.add (scrollbar);
425 scrollbar.set_name ("MixerWindow");
426 scrollbar.ensure_style();
427 Gtk::Requisition requisition(scrollbar.size_request ());
428 scrollbar_height = requisition.height;
431 // output port select
432 VBox* out_packer = manage (new VBox);
433 out_packer->set_spacing (PX_SCALE(2));
434 out_packer->pack_start (*output_label, false, false);
435 out_packer->pack_start (*output_button, false, false);
437 /****************************************************************************
440 vpacker.set_border_width (PX_SCALE(3));
441 vpacker.pack_start (*top_spacer, false, false, PX_SCALE(3));
442 vpacker.pack_start (*rude_box, false, false, PX_SCALE(3));
443 vpacker.pack_start (rude_audition_button, false, false, 0);
444 vpacker.pack_start (*solo_tbl, false, false, PX_SCALE(8));
445 vpacker.pack_start (*insert_box, true, true, PX_SCALE(8));
446 vpacker.pack_start (*level_tbl, false, false, PX_SCALE(8));
447 vpacker.pack_start (*lower_packer, false, false, PX_SCALE(8));
448 vpacker.pack_start (master_packer, false, false, PX_SCALE(10));
449 vpacker.pack_end (*out_packer, false, false,
451 scrollbar_height /* no outer frame */
453 scrollbar_height + 2 /* frame borders */
457 hpacker.set_spacing (0);
458 hpacker.pack_start (vpacker, true, true);
462 gain_control->show_all ();
463 gain_display->show_all ();
464 dim_control->show_all ();
465 dim_display->show_all();
466 solo_boost_control->show_all ();
467 solo_boost_display->show_all();
469 mono_dim_box->show ();
470 spin_packer->show ();
471 master_packer.show ();
474 solo_tbl->show_all();
476 lower_packer->show ();
484 output_button->signal_button_press_event().connect (sigc::mem_fun(*this, &MonitorSection::output_press), false);
485 output_button->signal_button_release_event().connect (sigc::mem_fun(*this, &MonitorSection::output_release), false);
487 signal_enter_notify_event().connect (sigc::mem_fun (*this, &MonitorSection::enter_handler));
488 signal_leave_notify_event().connect (sigc::mem_fun (*this, &MonitorSection::leave_handler));
489 set_flags (CAN_FOCUS);
491 _tearoff = new TearOff (*this);
493 if (!UIConfiguration::instance().get_floating_monitor_section()) {
494 /* if torn off, make this a normal window
495 * (default is WINDOW_TYPE_HINT_UTILITY in libs/gtkmm2ext/tearoff.cc)
497 _tearoff->tearoff_window().set_type_hint (Gdk::WINDOW_TYPE_HINT_NORMAL);
499 _tearoff->tearoff_window().set_title (X_("Monitor"));
500 _tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), (Gtk::Window*) &_tearoff->tearoff_window()), false);
502 update_output_display ();
503 update_processor_box ();
504 _ui_initialized = true;
506 /* catch changes that affect us */
507 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
508 *this, invalidator (*this), boost::bind (&MonitorSection::port_connected_or_disconnected, this, _1, _3), gui_context ()
510 Config->ParameterChanged.connect (config_connection, invalidator (*this), boost::bind (&MonitorSection::parameter_changed, this, _1), gui_context());
513 MonitorSection::~MonitorSection ()
515 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
519 _channel_buttons.clear ();
520 route_connections.drop_connections ();
522 delete insert_box; insert_box = 0;
523 delete output_button; output_button = 0;
524 delete gain_control; gain_control = 0;
525 delete gain_display; gain_display = 0;
526 delete dim_control; dim_control = 0;
527 delete dim_display; dim_display = 0;
528 delete solo_boost_control; solo_boost_control = 0;
529 delete solo_boost_display; solo_boost_display = 0;
530 delete solo_cut_control; solo_cut_control = 0;
531 delete solo_cut_display; solo_cut_display = 0;
532 delete _tearoff; _tearoff = 0;
533 delete _output_selector; _output_selector = 0;
534 delete channel_table; channel_table = 0;
538 MonitorSection::enter_handler (GdkEventCrossing* ev)
545 MonitorSection::leave_handler (GdkEventCrossing* ev)
547 switch (ev->detail) {
548 case GDK_NOTIFY_INFERIOR:
554 /* cancel focus if we're not torn off. With X11 WM's that do
555 * focus-follows-mouse, focus will be taken from us anyway.
558 Widget* top = get_toplevel();
560 if (top->is_toplevel() && top != &_tearoff->tearoff_window()) {
561 Window* win = dynamic_cast<Window*> (top);
562 gtk_window_set_focus (win->gobj(), 0);
569 MonitorSection::update_processor_box ()
571 bool show_processor_box = proctoggle->get_active ();
573 if (count_processors () > 0 && !show_processor_box) {
574 toggle_processorbox_button.set_name (X_("monitor section processors present"));
576 toggle_processorbox_button.set_name (X_("monitor section processors toggle"));
579 if (insert_box->is_visible() == show_processor_box) {
583 if (show_processor_box) {
584 if (master_packer.get_parent()) {
585 master_packer.get_parent()->remove (master_packer);
588 vpacker.pack_start (master_packer, false, false, PX_SCALE(10));
590 if (master_packer.get_parent()) {
591 master_packer.get_parent()->remove (master_packer);
594 vpacker.pack_start (master_packer, true, false, PX_SCALE(10));
599 MonitorSection::set_session (Session* s)
601 RouteUI::set_session (s);
602 insert_box->set_session (_session);
604 Glib::RefPtr<ActionGroup> global_monitor_actions = ActionManager::get_action_group (X_("Monitor Section"));
608 /* These are not actually dependent on the Session, but they
609 * need to be set after construction, not during, and
610 * this is as good a place as any.
613 ActionManager::get_toggle_action (X_("Solo"), X_("toggle-exclusive-solo"))->set_active (Config->get_exclusive_solo());
614 ActionManager::get_toggle_action (X_("Solo"), X_("toggle-mute-overrides-solo"))->set_active (Config->get_solo_mute_override());
616 _route = _session->monitor_out ();
619 /* session with monitor section */
620 _monitor = _route->monitor_control ();
621 assign_controllables ();
622 _route->output()->changed.connect (route_connections, invalidator (*this),
623 boost::bind (&MonitorSection::update_output_display, this),
625 insert_box->set_route (_route);
626 _route->processors_changed.connect (route_connections, invalidator (*this), boost::bind (&MonitorSection::processors_changed, this, _1), gui_context());
627 _route->output()->PortCountChanged.connect (route_connections, invalidator (*this), boost::bind (&MonitorSection::populate_buttons, this), gui_context());
628 _route->DropReferences.connect (route_connections, invalidator (*this), boost::bind (&MonitorSection::drop_route, this), gui_context());
630 if (_ui_initialized) {
631 update_processor_box ();
634 SYNCHRONIZE_TOGGLE_ACTION (ActionManager::get_toggle_action (X_("Monitor"), "UseMonitorSection"), true);
635 ActionManager::set_sensitive (global_monitor_actions, true);
636 ActionManager::set_sensitive (monitor_actions, true);
637 ActionManager::set_sensitive (solo_actions, true);
640 /* session with no monitor section */
641 route_connections.drop_connections();
644 delete _output_selector;
645 _output_selector = 0;
647 SYNCHRONIZE_TOGGLE_ACTION (ActionManager::get_toggle_action (X_("Monitor"), "UseMonitorSection"), false);
648 ActionManager::set_sensitive (global_monitor_actions, false);
649 ActionManager::set_sensitive (monitor_actions, false);
650 ActionManager::set_sensitive (solo_actions, true);
651 /* this action needs to always be true in this, so that we can turn it back on */
652 ActionManager::get_toggle_action (X_("Monitor"), X_("UseMonitorSection"))->set_sensitive (true);
664 unassign_controllables ();
667 ActionManager::set_sensitive (monitor_actions, false);
668 ActionManager::set_sensitive (solo_actions, false);
669 ActionManager::set_sensitive (global_monitor_actions, false);
674 MonitorSection::drop_route ()
676 route_connections.drop_connections();
679 unassign_controllables ();
680 rude_iso_button.unset_active_state ();
681 rude_solo_button.unset_active_state ();
682 delete _output_selector;
683 _output_selector = 0;
686 MonitorSection::ChannelButtonSet::ChannelButtonSet ()
688 cut.set_name (X_("mute button"));
689 dim.set_name (X_("monitor section dim"));
690 solo.set_name (X_("solo button"));
691 invert.set_name (X_("invert button"));
693 cut.unset_flags (Gtk::CAN_FOCUS);
694 dim.unset_flags (Gtk::CAN_FOCUS);
695 solo.unset_flags (Gtk::CAN_FOCUS);
696 invert.unset_flags (Gtk::CAN_FOCUS);
700 MonitorSection::populate_buttons ()
707 channel_size_group->remove_widget (*channel_table);
708 delete channel_table;
711 channel_table = new Gtk::Table();
713 channel_table->set_col_spacings (6);
714 channel_table->set_row_spacings (6);
715 channel_table->set_homogeneous (true);
717 channel_size_group->add_widget (*channel_table);
718 channel_table->show ();
719 table_hpacker.pack_start (*channel_table, true, true);
721 for (ChannelButtons::iterator i = _channel_buttons.begin(); i != _channel_buttons.end(); ++i) {
724 _channel_buttons.clear ();
726 Glib::RefPtr<Action> act;
727 uint32_t nchans = _monitor->output_streams().n_audio();
729 channel_table->resize (nchans, 5);
731 const uint32_t row_offset = 0;
733 for (uint32_t i = 0; i < nchans; ++i) {
746 snprintf (buf, sizeof (buf), "%d", i+1);
750 Label* label = manage (new Label (l));
751 channel_table->attach (*label, 0, 1, i+row_offset, i+row_offset+1, EXPAND|FILL);
753 ChannelButtonSet* cbs = new ChannelButtonSet;
755 _channel_buttons.push_back (cbs);
757 channel_table->attach (cbs->cut, 1, 2, i+row_offset, i+row_offset+1, EXPAND|FILL);
758 channel_table->attach (cbs->dim, 2, 3, i+row_offset, i+row_offset+1, EXPAND|FILL);
759 channel_table->attach (cbs->solo, 3, 4, i+row_offset, i+row_offset+1, EXPAND|FILL);
760 channel_table->attach (cbs->invert, 4, 5, i+row_offset, i+row_offset+1, EXPAND|FILL);
762 snprintf (buf, sizeof (buf), "monitor-cut-%u", i);
763 act = ActionManager::get_action (X_("Monitor"), buf);
765 cbs->cut.set_related_action (act);
768 snprintf (buf, sizeof (buf), "monitor-dim-%u", i);
769 act = ActionManager::get_action (X_("Monitor"), buf);
771 cbs->dim.set_related_action (act);
774 snprintf (buf, sizeof (buf), "monitor-solo-%u", i);
775 act = ActionManager::get_action (X_("Monitor"), buf);
777 cbs->solo.set_related_action (act);
780 snprintf (buf, sizeof (buf), "monitor-invert-%u", i);
781 act = ActionManager::get_action (X_("Monitor"), buf);
783 cbs->invert.set_related_action (act);
787 channel_table->show_all ();
789 if (channel_table_scroller.get_parent()) {
790 /* scroller is packed, so remove it */
791 channel_table_packer.remove (channel_table_scroller);
794 if (table_hpacker.get_parent () == &channel_table_packer) {
795 /* this occurs when the table hpacker is directly
796 packed, so remove it.
798 channel_table_packer.remove (table_hpacker);
799 } else if (table_hpacker.get_parent()) {
800 channel_table_viewport.remove ();
804 /* put the table into a scrolled window, and then put
805 * that into the channel vpacker, after the table header
807 channel_table_viewport.add (table_hpacker);
808 channel_table_packer.pack_start (channel_table_scroller, true, true);
809 channel_table_viewport.show ();
810 channel_table_scroller.show ();
813 /* just put the channel table itself into the channel
814 * vpacker, after the table header
816 channel_table_packer.pack_start (table_hpacker, true, true);
817 channel_table_scroller.hide ();
819 table_hpacker.show ();
820 channel_table->show ();
824 MonitorSection::toggle_exclusive_solo ()
830 Config->set_exclusive_solo (ActionManager::get_toggle_action (X_("Solo"), "toggle-exclusive-solo")->get_active());
834 MonitorSection::toggle_mute_overrides_solo ()
840 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Solo"), "toggle-mute-overrides-solo");
841 Config->set_solo_mute_override (tact->get_active());
845 MonitorSection::dim_all ()
851 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Monitor Section"), "monitor-dim-all");
852 _monitor->set_dim_all (tact->get_active());
856 MonitorSection::cut_all ()
862 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Monitor Section"), "monitor-cut-all");
863 _monitor->set_cut_all (tact->get_active());
867 MonitorSection::mono ()
873 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Monitor Section"), "monitor-mono");
874 _monitor->set_mono (tact->get_active());
878 MonitorSection::cut_channel (uint32_t chn)
885 snprintf (buf, sizeof (buf), "monitor-cut-%u", chn);
887 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Monitor"), buf);
888 _monitor->set_cut (chn, tact->get_active());
892 MonitorSection::dim_channel (uint32_t chn)
899 snprintf (buf, sizeof (buf), "monitor-dim-%u", chn);
901 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Monitor"), buf);
902 _monitor->set_dim (chn, tact->get_active());
906 MonitorSection::solo_channel (uint32_t chn)
913 snprintf (buf, sizeof (buf), "monitor-solo-%u", chn);
915 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Monitor"), buf);
916 _monitor->set_solo (chn, tact->get_active());
921 MonitorSection::invert_channel (uint32_t chn)
928 snprintf (buf, sizeof (buf), "monitor-invert-%u", chn);
930 Glib::RefPtr<ToggleAction> tact = ActionManager::get_toggle_action (X_("Monitor"), buf);
931 _monitor->set_polarity (chn, tact->get_active());
935 MonitorSection::register_actions ()
939 Glib::RefPtr<Action> act;
941 /* ...will get sensitized if a mon-session is added */
943 monitor_actions = ActionManager::create_action_group (bindings, X_("Monitor"));
944 solo_actions = ActionManager::create_action_group (bindings, X_("Monitor"));
947 ActionManager::register_toggle_action (monitor_actions, X_("UseMonitorSection"), _("Use Monitor Section"), sigc::mem_fun(*this, &MonitorSection::toggle_use_monitor_section));
949 /* these are global monitor actions that invoke MonitorSectioncode. Do
950 * not create local versions (i.e. as part of "monitor_actions")
951 * because then we can end up with two different bindings (one global,
952 * one local to the monitor section) for the same action.
955 Glib::RefPtr<ActionGroup> global_monitor_actions = ActionManager::get_action_group (X_("Monitor Section"));
957 ActionManager::register_toggle_action (global_monitor_actions, "monitor-mono", _("Mono"), sigc::mem_fun (*this, &MonitorSection::mono));
958 ActionManager::register_toggle_action (global_monitor_actions, "monitor-cut-all", _("Mute"), sigc::mem_fun (*this, &MonitorSection::cut_all));
959 ActionManager::register_toggle_action (global_monitor_actions, "monitor-dim-all", _("Dim"), sigc::mem_fun (*this, &MonitorSection::dim_all));
961 ActionManager::register_toggle_action (monitor_actions, "toggle-monitor-processor-box", _("Toggle Monitor Section Processor Box"),
962 sigc::mem_fun (*this, &MonitorSection::update_processor_box));
965 for (uint32_t chn = 0; chn < 16; ++chn) {
967 action_name = string_compose (X_("monitor-cut-%1"), chn);
968 action_descr = string_compose (_("Cut monitor channel %1"), chn);
969 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
970 sigc::bind (sigc::mem_fun (*this, &MonitorSection::cut_channel), chn));
972 action_name = string_compose (X_("monitor-dim-%1"), chn);
973 action_descr = string_compose (_("Dim monitor channel %1"), chn);
974 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
975 sigc::bind (sigc::mem_fun (*this, &MonitorSection::dim_channel), chn));
977 action_name = string_compose (X_("monitor-solo-%1"), chn);
978 action_descr = string_compose (_("Solo monitor channel %1"), chn);
979 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
980 sigc::bind (sigc::mem_fun (*this, &MonitorSection::solo_channel), chn));
982 action_name = string_compose (X_("monitor-invert-%1"), chn);
983 action_descr = string_compose (_("Invert monitor channel %1"), chn);
984 ActionManager::register_toggle_action (monitor_actions, action_name.c_str(), action_descr.c_str(),
985 sigc::bind (sigc::mem_fun (*this, &MonitorSection::invert_channel), chn));
989 solo_actions = ActionManager::create_action_group (bindings, X_("Solo"));
990 RadioAction::Group solo_group;
992 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-in-place", _("In-place solo"),
993 sigc::mem_fun (*this, &MonitorSection::solo_use_in_place));
994 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-afl", _("After Fade Listen (AFL) solo"),
995 sigc::mem_fun (*this, &MonitorSection::solo_use_afl));
996 ActionManager::register_radio_action (solo_actions, solo_group, "solo-use-pfl", _("Pre Fade Listen (PFL) solo"),
997 sigc::mem_fun (*this, &MonitorSection::solo_use_pfl));
999 ActionManager::register_toggle_action (solo_actions, "toggle-exclusive-solo", _("Toggle exclusive solo mode"),
1000 sigc::mem_fun (*this, &MonitorSection::toggle_exclusive_solo));
1001 ActionManager::register_toggle_action (solo_actions, "toggle-mute-overrides-solo", _("Toggle mute overrides solo mode"),
1002 sigc::mem_fun (*this, &MonitorSection::toggle_mute_overrides_solo));
1006 MonitorSection::solo_use_in_place ()
1008 /* this is driven by a toggle on a radio group, and so is invoked twice,
1009 once for the item that became inactive and once for the one that became
1013 Glib::RefPtr<RadioAction> ract = ActionManager::get_radio_action (X_("Solo"), X_("solo-use-in-place"));
1014 if (!ract->get_active ()) {
1015 /* We are turning SiP off, which means that AFL or PFL will be turned on
1016 shortly; don't update the solo model in the mean time, as if the currently
1017 configured listen position is not the one that is about to be turned on,
1018 things will go wrong.
1020 _inhibit_solo_model_update = true;
1022 Config->set_solo_control_is_listen_control (!ract->get_active());
1023 _inhibit_solo_model_update = false;
1027 MonitorSection::solo_use_afl ()
1029 /* this is driven by a toggle on a radio group, and so is invoked twice,
1030 once for the item that became inactive and once for the one that became
1034 Glib::RefPtr<RadioAction> ract = ActionManager::get_radio_action (X_("Solo"), X_("solo-use-afl"));
1035 if (ract->get_active()) {
1036 Config->set_solo_control_is_listen_control (true);
1037 Config->set_listen_position (AfterFaderListen);
1042 MonitorSection::solo_use_pfl ()
1044 /* this is driven by a toggle on a radio group, and so is invoked twice,
1045 once for the item that became inactive and once for the one that became
1049 Glib::RefPtr<RadioAction> ract = ActionManager::get_radio_action (X_("Solo"), X_("solo-use-pfl"));
1050 if (ract->get_active()) {
1051 Config->set_solo_control_is_listen_control (true);
1052 Config->set_listen_position (PreFaderListen);
1057 MonitorSection::update_solo_model ()
1059 if (_inhibit_solo_model_update) {
1063 const char* action_name = 0;
1064 Glib::RefPtr<RadioAction> ract;
1066 if (Config->get_solo_control_is_listen_control()) {
1067 switch (Config->get_listen_position()) {
1068 case AfterFaderListen:
1069 action_name = X_("solo-use-afl");
1071 case PreFaderListen:
1072 action_name = X_("solo-use-pfl");
1076 action_name = X_("solo-use-in-place");
1079 ract = ActionManager::get_radio_action (X_("Solo"), action_name);
1081 /* because these are radio buttons, one of them will be
1082 active no matter what. to trigger a change in the
1083 action so that the view picks it up, toggle it.
1086 if (ract->get_active()) {
1087 ract->set_active (false);
1090 ract->set_active (true);
1094 MonitorSection::map_state ()
1096 if (!_route || !_monitor) {
1100 update_solo_model ();
1102 Glib::RefPtr<Action> act;
1103 Glib::RefPtr<ToggleAction> tact;
1105 tact = ActionManager::get_toggle_action (X_("Monitor Section"), "monitor-cut-all");
1106 tact->set_active (_monitor->cut_all());
1108 tact = ActionManager::get_toggle_action (X_("Monitor Section"), "monitor-dim-all");
1109 tact->set_active (_monitor->dim_all());
1111 tact = ActionManager::get_toggle_action (X_("Monitor Section"), "monitor-mono");
1112 tact->set_active (_monitor->mono());
1114 uint32_t nchans = _monitor->output_streams().n_audio();
1116 assert (nchans == _channel_buttons.size ());
1118 for (uint32_t n = 0; n < nchans; ++n) {
1120 char action_name[32];
1122 snprintf (action_name, sizeof (action_name), "monitor-cut-%u", n);
1123 tact = ActionManager::get_toggle_action (X_("Monitor"), action_name);
1124 tact->set_active (_monitor->cut (n));
1126 snprintf (action_name, sizeof (action_name), "monitor-dim-%u", n);
1127 tact = ActionManager::get_toggle_action (X_("Monitor"), action_name);
1128 tact->set_active (_monitor->dimmed (n));
1130 snprintf (action_name, sizeof (action_name), "monitor-solo-%u", n);
1131 tact = ActionManager::get_toggle_action (X_("Monitor"), action_name);
1132 tact->set_active (_monitor->soloed (n));
1134 snprintf (action_name, sizeof (action_name), "monitor-invert-%u", n);
1135 tact = ActionManager::get_toggle_action (X_("Monitor"), action_name);
1136 tact->set_active (_monitor->inverted (n));
1141 MonitorSection::do_blink (bool onoff)
1143 if (!UIConfiguration::instance().get_blink_alert_indicators ()) {
1148 audition_blink (onoff);
1152 MonitorSection::audition_blink (bool onoff)
1154 if (_session == 0) {
1158 if (_session->is_auditioning()) {
1159 rude_audition_button.set_active (onoff);
1161 rude_audition_button.set_active (false);
1166 MonitorSection::solo_blink (bool onoff)
1168 if (_session == 0) {
1172 if (_session->soloing() || _session->listening()) {
1173 rude_solo_button.set_active (onoff);
1175 if (_session->soloing()) {
1176 if (_session->solo_isolated()) {
1177 rude_iso_button.set_active (onoff);
1179 rude_iso_button.set_active (false);
1184 rude_solo_button.set_active (false);
1185 rude_iso_button.set_active (false);
1190 MonitorSection::cancel_isolate (GdkEventButton*)
1193 boost::shared_ptr<RouteList> rl (_session->get_routes ());
1194 _session->set_controls (route_list_to_control_list (rl, &Stripable::solo_isolate_control), 0.0, Controllable::NoGroup);
1201 MonitorSection::cancel_audition (GdkEventButton*)
1204 _session->cancel_audition();
1210 MonitorSection::parameter_changed (std::string name)
1212 if (name == "solo-control-is-listen-control") {
1213 update_solo_model ();
1214 } else if (name == "listen-position") {
1215 update_solo_model ();
1216 } else if (name == "solo-mute-override") {
1217 SYNCHRONIZE_TOGGLE_ACTION (ActionManager::get_toggle_action (X_("Solo"), "toggle-mute-overrides-solo"), Config->get_solo_mute_override ());
1218 } else if (name == "exclusive-solo") {
1219 SYNCHRONIZE_TOGGLE_ACTION (ActionManager::get_toggle_action (X_("Solo"), "toggle-exclusive-solo"), Config->get_exclusive_solo ());
1224 MonitorSection::unassign_controllables ()
1226 boost::shared_ptr<Controllable> none;
1228 solo_cut_control->set_controllable (none);
1229 solo_cut_display->set_controllable (none);
1230 gain_control->set_controllable (none);
1231 gain_display->set_controllable (none);
1232 cut_all_button.set_controllable (none);
1233 dim_all_button.set_controllable (none);
1234 mono_button.set_controllable (none);
1235 dim_control->set_controllable (none);
1236 dim_display->set_controllable (none);
1237 solo_boost_control->set_controllable (none);
1238 solo_boost_display->set_controllable (none);
1242 MonitorSection::assign_controllables ()
1248 solo_cut_control->set_controllable (_session->solo_cut_control());
1249 solo_cut_display->set_controllable (_session->solo_cut_control());
1251 gain_control->set_controllable (_route->gain_control());
1252 gain_display->set_controllable (_route->gain_control());
1253 cut_all_button.set_controllable (_monitor->cut_control());
1254 cut_all_button.watch ();
1255 dim_all_button.set_controllable (_monitor->dim_control());
1256 dim_all_button.watch ();
1257 mono_button.set_controllable (_monitor->mono_control());
1258 mono_button.watch ();
1259 dim_control->set_controllable (_monitor->dim_level_control ());
1260 dim_display->set_controllable (_monitor->dim_level_control ());
1261 solo_boost_control->set_controllable (_monitor->solo_boost_control ());
1262 solo_boost_display->set_controllable (_monitor->solo_boost_control ());
1266 MonitorSection::state_id() const
1268 return "monitor-section";
1272 MonitorSection::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1274 using namespace Menu_Helpers;
1276 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1280 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1281 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1285 if (i != output_menu_bundles.end()) {
1289 output_menu_bundles.push_back (b);
1291 MenuList& citems = output_menu.items();
1293 std::string n = b->name ();
1294 replace_all (n, "_", " ");
1296 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MonitorSection::bundle_output_chosen), b)));
1300 MonitorSection::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1303 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1305 if (std::find (current.begin(), current.end(), c) == current.end()) {
1306 _route->output()->connect_ports_to_bundle (c, true, this);
1308 _route->output()->disconnect_ports_from_bundle (c, this);
1313 MonitorSection::output_release (GdkEventButton *ev)
1315 switch (ev->button) {
1317 edit_output_configuration ();
1324 struct RouteCompareByName {
1325 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1326 return a->name().compare (b->name()) < 0;
1331 MonitorSection::output_press (GdkEventButton *ev)
1333 using namespace Menu_Helpers;
1335 MessageDialog msg (_("No session - no I/O changes are possible"));
1340 MenuList& citems = output_menu.items();
1341 switch (ev->button) {
1344 return false; //wait for the mouse-up to pop the dialog
1348 output_menu.set_name ("ArdourContextMenu");
1350 output_menu_bundles.clear ();
1352 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(this), &MonitorSection::disconnect_output)));
1354 citems.push_back (SeparatorElem());
1355 uint32_t const n_with_separator = citems.size ();
1357 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1359 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1361 /* give user bundles first chance at being in the menu */
1363 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1364 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1365 maybe_add_bundle_to_output_menu (*i, current);
1369 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1370 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1371 maybe_add_bundle_to_output_menu (*i, current);
1375 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1376 RouteList copy = *routes;
1377 copy.sort (RouteCompareByName ());
1378 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1379 maybe_add_bundle_to_output_menu ((*i)->output()->bundle(), current);
1382 if (citems.size() == n_with_separator) {
1383 /* no routes added; remove the separator */
1387 citems.push_back (SeparatorElem());
1388 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(this), &MonitorSection::edit_output_configuration)));
1390 output_menu.popup (1, ev->time);
1401 MonitorSection::update_output_display ()
1403 if (!_route || !_monitor || _session->deletion_in_progress()) {
1409 boost::shared_ptr<Port> port;
1410 vector<string> port_connections;
1412 uint32_t total_connection_count = 0;
1413 uint32_t io_connection_count = 0;
1414 uint32_t ardour_connection_count = 0;
1415 uint32_t system_connection_count = 0;
1416 uint32_t other_connection_count = 0;
1418 ostringstream label;
1420 bool have_label = false;
1421 bool each_io_has_one_connection = true;
1423 string connection_name;
1424 string ardour_track_name;
1425 string other_connection_type;
1426 string system_ports;
1429 ostringstream tooltip;
1430 char * tooltip_cstr;
1432 io_count = _route->n_outputs().n_total();
1433 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (_route->name()));
1436 for (io_index = 0; io_index < io_count; ++io_index) {
1438 port = _route->output()->nth (io_index);
1440 //ignore any port connections that don't match our DataType
1441 if (port->type() != DataType::AUDIO) {
1445 port_connections.clear ();
1446 port->get_connections(port_connections);
1447 io_connection_count = 0;
1449 if (!port_connections.empty()) {
1450 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1452 string& connection_name (*i);
1454 if (connection_name.find("system:") == 0) {
1455 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1458 if (io_connection_count == 0) {
1459 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1461 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1464 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1467 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1468 if (ardour_track_name.empty()) {
1469 // "ardour:Master/in 1" -> "ardour:Master/"
1470 string::size_type slash = connection_name.find("/");
1471 if (slash != string::npos) {
1472 ardour_track_name = connection_name.substr(0, slash + 1);
1476 if (connection_name.find(ardour_track_name) == 0) {
1477 ++ardour_connection_count;
1479 } else if (!pn.empty()) {
1480 if (system_ports.empty()) {
1483 system_ports += "/" + pn;
1485 if (connection_name.find("system:") == 0) {
1486 ++system_connection_count;
1488 } else if (connection_name.find("system:") == 0) {
1489 // "system:playback_123" -> "123"
1490 system_port = connection_name.substr(16);
1491 if (system_ports.empty()) {
1492 system_ports += system_port;
1494 system_ports += "/" + system_port;
1497 ++system_connection_count;
1499 if (other_connection_type.empty()) {
1500 // "jamin:in 1" -> "jamin:"
1501 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1504 if (connection_name.find(other_connection_type) == 0) {
1505 ++other_connection_count;
1509 ++total_connection_count;
1510 ++io_connection_count;
1514 if (io_connection_count != 1) {
1515 each_io_has_one_connection = false;
1519 if (total_connection_count == 0) {
1520 tooltip << endl << _("Disconnected");
1523 tooltip_cstr = new char[tooltip.str().size() + 1];
1524 strcpy(tooltip_cstr, tooltip.str().c_str());
1526 set_tooltip (output_button, tooltip_cstr, "");
1528 if (each_io_has_one_connection) {
1529 if (total_connection_count == ardour_connection_count) {
1530 // all connections are to the same track in ardour
1531 // "ardour:Master/" -> "Master"
1532 string::size_type slash = ardour_track_name.find("/");
1533 if (slash != string::npos) {
1534 label << ardour_track_name.substr(7, slash - 7);
1537 } else if (total_connection_count == system_connection_count) {
1538 // all connections are to system ports
1539 label << system_ports;
1541 } else if (total_connection_count == other_connection_count) {
1542 // all connections are to the same external program eg jamin
1543 // "jamin:" -> "jamin"
1544 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1550 if (total_connection_count == 0) {
1554 // Odd configuration
1555 label << "*" << total_connection_count << "*";
1559 output_button->set_text (label.str());
1563 MonitorSection::disconnect_output ()
1566 _route->output()->disconnect(this);
1571 MonitorSection::edit_output_configuration ()
1573 if (_output_selector == 0) {
1574 _output_selector = new MonitorSelectorWindow (_session, _route->output());
1576 _output_selector->present ();
1580 MonitorSection::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1585 boost::shared_ptr<Port> a = wa.lock ();
1586 boost::shared_ptr<Port> b = wb.lock ();
1587 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1588 update_output_display ();
1593 MonitorSection::load_bindings ()
1595 bindings = Bindings::get_bindings (X_("Monitor Section"));
1599 MonitorSection::help_count_processors (boost::weak_ptr<Processor> p, uint32_t* cnt) const
1601 boost::shared_ptr<Processor> processor (p.lock ());
1602 if (!processor || !processor->display_to_user()) {
1605 if (boost::dynamic_pointer_cast<Amp>(processor)) {
1612 MonitorSection::count_processors ()
1614 uint32_t processor_count = 0;
1616 _route->foreach_processor (sigc::bind (sigc::mem_fun (*this, &MonitorSection::help_count_processors), &processor_count));
1618 return processor_count;
1622 MonitorSection::processors_changed (ARDOUR::RouteProcessorChange)
1624 update_processor_box ();
1628 MonitorSection::plugin_selector ()
1630 return Mixer_UI::instance()->plugin_selector ();
1634 MonitorSection::use_others_actions ()
1636 rude_solo_button.set_related_action (ActionManager::get_action (X_("Main"), X_("cancel-solo")));
1640 MonitorSection::toggle_use_monitor_section ()
1645 bool want_ms = ActionManager::get_toggle_action (X_("Monitor"), "UseMonitorSection")->get_active();
1646 bool have_ms = Config->get_use_monitor_bus ();
1648 if (want_ms == have_ms) {
1653 Config->set_use_monitor_bus (true);
1654 ActionManager::get_toggle_action (X_("Mixer"), X_("ToggleMonitorSection"))->set_active (true);
1656 Config->set_use_monitor_bus (false);