2 * Copyright (C) 2005-2006 Nick Mainsbridge <mainsbridge@gmail.com>
3 * Copyright (C) 2005-2006 Taybin Rutkin <taybin@taybin.com>
4 * Copyright (C) 2005-2007 Doug McLain <doug@nostar.net>
5 * Copyright (C) 2005-2017 Paul Davis <paul@linuxaudiosystems.com>
6 * Copyright (C) 2006-2014 David Robillard <d@drobilla.net>
7 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
8 * Copyright (C) 2008 Hans Baier <hansfbaier@googlemail.com>
9 * Copyright (C) 2009-2010 Sakari Bergen <sakari.bergen@beatwaves.net>
10 * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
11 * Copyright (C) 2013-2016 John Emmas <john@creativepost.co.uk>
12 * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
13 * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
14 * Copyright (C) 2016-2017 Julien "_FrnchFrgg_" RIVAUD <frnchfrgg@free.fr>
15 * Copyright (C) 2018 Len Ovens <len@ovenwerks.net>
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
36 #include <sigc++/bind.h>
38 #include <gtkmm/messagedialog.h>
40 #include "pbd/convert.h"
41 #include "pbd/enumwriter.h"
42 #include "pbd/replace_all.h"
43 #include "pbd/stacktrace.h"
44 #include "pbd/unwind.h"
46 #include "ardour/amp.h"
47 #include "ardour/audio_track.h"
48 #include "ardour/audioengine.h"
49 #include "ardour/internal_send.h"
50 #include "ardour/io.h"
51 #include "ardour/meter.h"
52 #include "ardour/midi_track.h"
53 #include "ardour/pannable.h"
54 #include "ardour/panner.h"
55 #include "ardour/panner_shell.h"
56 #include "ardour/panner_manager.h"
57 #include "ardour/port.h"
58 #include "ardour/profile.h"
59 #include "ardour/route.h"
60 #include "ardour/route_group.h"
61 #include "ardour/send.h"
62 #include "ardour/session.h"
63 #include "ardour/types.h"
64 #include "ardour/user_bundle.h"
65 #include "ardour/vca.h"
66 #include "ardour/vca_manager.h"
68 #include "gtkmm2ext/gtk_ui.h"
69 #include "gtkmm2ext/menu_elems.h"
70 #include "gtkmm2ext/utils.h"
71 #include "gtkmm2ext/doi.h"
73 #include "widgets/tooltips.h"
75 #include "ardour_window.h"
76 #include "context_menu_helper.h"
77 #include "enums_convert.h"
78 #include "mixer_strip.h"
81 #include "public_editor.h"
83 #include "io_selector.h"
85 #include "gui_thread.h"
86 #include "route_group_menu.h"
87 #include "meter_patterns.h"
88 #include "ui_config.h"
92 using namespace ARDOUR;
93 using namespace ArdourWidgets;
96 using namespace Gtkmm2ext;
98 using namespace ArdourMeter;
100 MixerStrip* MixerStrip::_entered_mixer_strip;
101 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
103 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
104 : SessionHandlePtr (sess)
107 , _mixer_owned (in_mixer)
108 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
111 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
112 , rec_mon_table (2, 2)
113 , solo_iso_table (1, 2)
114 , mute_solo_table (1, 2)
115 , bottom_button_table (1, 3)
116 , monitor_section_button (0)
117 , midi_input_enable_button (0)
118 , _plugin_insert_cnt (0)
119 , _comment_button (_("Comments"))
120 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
121 , _visibility (X_("mixer-element-visibility"))
122 , _suspend_menu_callbacks (false)
123 , control_slave_ui (sess)
128 /* the editor mixer strip: don't destroy it every time
129 the underlying route goes away.
132 self_destruct = false;
136 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
137 : SessionHandlePtr (sess)
140 , _mixer_owned (in_mixer)
141 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
144 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
145 , rec_mon_table (2, 2)
146 , solo_iso_table (1, 2)
147 , mute_solo_table (1, 2)
148 , bottom_button_table (1, 3)
149 , monitor_section_button (0)
150 , midi_input_enable_button (0)
151 , _plugin_insert_cnt (0)
152 , _comment_button (_("Comments"))
153 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
154 , _visibility (X_("mixer-element-visibility"))
155 , _suspend_menu_callbacks (false)
156 , control_slave_ui (sess)
165 _entered_mixer_strip= 0;
168 ignore_comment_edit = false;
169 ignore_toggle = false;
173 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
174 longest_label = "longest label";
176 string t = _("Click to toggle the width of this mixer strip.");
178 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
181 width_button.set_icon (ArdourIcon::StripWidth);
182 hide_button.set_tweaks (ArdourButton::Square);
183 set_tooltip (width_button, t);
185 hide_button.set_icon (ArdourIcon::CloseCross);
186 hide_button.set_tweaks (ArdourButton::Square);
187 set_tooltip (&hide_button, _("Hide this mixer strip"));
189 input_button_box.set_spacing(2);
191 input_button.set_text (_("Input"));
192 input_button.set_name ("mixer strip button");
193 input_button_box.pack_start (input_button, true, true);
195 output_button.set_text (_("Output"));
196 output_button.set_name ("mixer strip button");
198 bottom_button_table.attach (gpm.meter_point_button, 2, 3, 0, 1);
200 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
202 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
203 solo_isolated_led->show ();
204 solo_isolated_led->set_no_show_all (true);
205 solo_isolated_led->set_name (X_("solo isolate"));
206 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
207 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
208 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
210 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
211 solo_safe_led->show ();
212 solo_safe_led->set_no_show_all (true);
213 solo_safe_led->set_name (X_("solo safe"));
214 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
215 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
216 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
218 solo_safe_led->set_text (S_("SoloLock|Lock"));
219 solo_isolated_led->set_text (_("Iso"));
221 solo_iso_table.set_homogeneous (true);
222 solo_iso_table.set_spacings (2);
223 if (!ARDOUR::Profile->get_trx()) {
224 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
225 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
227 solo_iso_table.show ();
229 rec_mon_table.set_homogeneous (true);
230 rec_mon_table.set_row_spacings (2);
231 rec_mon_table.set_col_spacings (2);
232 if (ARDOUR::Profile->get_mixbus()) {
233 rec_mon_table.resize (1, 3);
234 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
235 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
236 } else if (!ARDOUR::Profile->get_trx()) {
237 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
238 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
240 rec_mon_table.show ();
242 if (solo_isolated_led) {
243 button_size_group->add_widget (*solo_isolated_led);
246 button_size_group->add_widget (*solo_safe_led);
249 if (!ARDOUR::Profile->get_mixbus()) {
250 if (rec_enable_button) {
251 button_size_group->add_widget (*rec_enable_button);
253 if (monitor_disk_button) {
254 button_size_group->add_widget (*monitor_disk_button);
256 if (monitor_input_button) {
257 button_size_group->add_widget (*monitor_input_button);
261 mute_solo_table.set_homogeneous (true);
262 mute_solo_table.set_spacings (2);
264 bottom_button_table.set_spacings (2);
265 bottom_button_table.set_homogeneous (true);
266 bottom_button_table.attach (group_button, 1, 2, 0, 1);
267 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
269 name_button.set_name ("mixer strip button");
270 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
271 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
273 set_tooltip (&group_button, _("Mix group"));
274 group_button.set_name ("mixer strip button");
276 _comment_button.set_name (X_("mixer strip button"));
277 _comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
278 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
279 _comment_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::comment_button_resized));
281 // TODO implement ArdourKnob::on_size_request properly
282 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
283 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
285 trim_control.set_tooltip_prefix (_("Trim: "));
286 trim_control.set_name ("trim knob");
287 trim_control.set_no_show_all (true);
288 trim_control.StartGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_start_touch));
289 trim_control.StopGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_end_touch));
290 input_button_box.pack_start (trim_control, false, false);
292 global_vpacker.set_border_width (1);
293 global_vpacker.set_spacing (0);
295 width_button.set_name ("mixer strip button");
296 hide_button.set_name ("mixer strip button");
298 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
299 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
301 width_hide_box.set_spacing (2);
302 width_hide_box.pack_start (width_button, false, true);
303 width_hide_box.pack_start (number_label, true, true);
304 width_hide_box.pack_end (hide_button, false, true);
306 number_label.set_text ("-");
307 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
308 number_label.set_no_show_all ();
309 number_label.set_name ("tracknumber label");
310 number_label.set_fixed_colors (0x80808080, 0x80808080);
311 number_label.set_alignment (.5, .5);
312 number_label.set_fallthrough_to_parent (true);
313 number_label.set_tweaks (ArdourButton::OccasionalText);
315 global_vpacker.set_spacing (2);
316 if (!ARDOUR::Profile->get_trx()) {
317 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
318 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
319 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
320 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
321 global_vpacker.pack_start (processor_box, true, true);
323 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
324 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
325 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
326 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
327 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
328 global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
329 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
330 if (!ARDOUR::Profile->get_trx()) {
331 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
332 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
334 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
338 //add a spacer underneath the master bus;
339 //this fills the area that is taken up by the scrollbar on the tracks;
340 //and therefore keeps the faders "even" across the bottom
341 int scrollbar_height = 0;
343 Gtk::Window window (WINDOW_TOPLEVEL);
344 HScrollbar scrollbar;
345 window.add (scrollbar);
346 scrollbar.set_name ("MixerWindow");
347 scrollbar.ensure_style();
348 Gtk::Requisition requisition(scrollbar.size_request ());
349 scrollbar_height = requisition.height;
351 spacer.set_size_request (-1, scrollbar_height);
352 global_vpacker.pack_end (spacer, false, false);
355 global_frame.add (global_vpacker);
356 global_frame.set_shadow_type (Gtk::SHADOW_IN);
357 global_frame.set_name ("BaseFrame");
361 /* force setting of visible selected status */
364 set_selected (false);
369 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
370 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
372 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
373 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
374 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
376 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
377 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
379 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
380 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
381 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
383 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
385 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
387 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
391 /* start off as a passthru strip. we'll correct this, if necessary,
392 in update_diskstream_display().
395 /* start off as a passthru strip. we'll correct this, if necessary,
396 in update_diskstream_display().
399 if (is_midi_track()) {
400 set_name ("MidiTrackStripBase");
402 set_name ("AudioTrackStripBase");
405 add_events (Gdk::BUTTON_RELEASE_MASK|
406 Gdk::ENTER_NOTIFY_MASK|
407 Gdk::LEAVE_NOTIFY_MASK|
409 Gdk::KEY_RELEASE_MASK);
411 set_flags (get_flags() | Gtk::CAN_FOCUS);
413 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
414 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
417 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
418 must be the same as those used in RCOptionEditor so that the configuration changes
419 are recognised when they occur.
421 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
422 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
423 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
424 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
425 _visibility.add (&output_button, X_("Output"), _("Output"), false);
426 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
427 _visibility.add (&control_slave_ui, X_("VCA"), _("VCA Assigns"), false);
429 parameter_changed (X_("mixer-element-visibility"));
430 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
431 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
432 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
434 //watch for mouse enter/exit so we can do some stuff
435 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
436 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
438 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
441 MixerStrip::~MixerStrip ()
443 CatchDeletion (this);
445 if (this ==_entered_mixer_strip)
446 _entered_mixer_strip = NULL;
450 MixerStrip::vca_assign (boost::shared_ptr<ARDOUR::VCA> vca)
452 boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
458 MixerStrip::vca_unassign (boost::shared_ptr<ARDOUR::VCA> vca)
460 boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
466 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
468 _entered_mixer_strip = this;
470 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
471 //because the mixerstrip control is a parent that encompasses the strip
472 deselect_all_processors();
478 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
480 //if we have moved outside our strip, but not into a child view, then deselect ourselves
481 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
482 _entered_mixer_strip= 0;
484 //clear keyboard focus in the gain display. this is cheesy but fixes a longstanding "bug" where the user starts typing in the gain entry, and leaves it active, thereby prohibiting other keybindings from working
485 gpm.gain_display.set_sensitive(false);
487 gpm.gain_display.set_sensitive(true);
489 //if we leave this mixer strip we need to clear out any selections
490 //processor_box.processor_display.select_none(); //but this doesn't work, because it gets triggered when (for example) you open the menu or start a drag
497 MixerStrip::name() const
500 return _route->name();
506 MixerStrip::update_trim_control ()
508 if (route()->trim() && route()->trim()->active() &&
509 route()->n_inputs().n_audio() > 0) {
510 trim_control.show ();
511 trim_control.set_controllable (route()->trim()->gain_control());
513 trim_control.hide ();
514 boost::shared_ptr<Controllable> none;
515 trim_control.set_controllable (none);
520 MixerStrip::trim_start_touch ()
522 assert (_route && _session);
523 if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
524 route()->trim()->gain_control ()->start_touch (_session->transport_sample());
529 MixerStrip::trim_end_touch ()
531 assert (_route && _session);
532 if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
533 route()->trim()->gain_control ()->stop_touch (_session->transport_sample());
538 MixerStrip::set_route (boost::shared_ptr<Route> rt)
540 //the rec/monitor stuff only shows up for tracks.
541 //the show_sends only shows up for buses.
542 //remove them all here, and we may add them back later
543 if (show_sends_button->get_parent()) {
544 rec_mon_table.remove (*show_sends_button);
546 if (rec_enable_button->get_parent()) {
547 rec_mon_table.remove (*rec_enable_button);
549 if (monitor_input_button->get_parent()) {
550 rec_mon_table.remove (*monitor_input_button);
552 if (monitor_disk_button->get_parent()) {
553 rec_mon_table.remove (*monitor_disk_button);
555 if (group_button.get_parent()) {
556 bottom_button_table.remove (group_button);
559 RouteUI::set_route (rt);
561 control_slave_ui.set_stripable (boost::dynamic_pointer_cast<Stripable> (rt));
563 /* ProcessorBox needs access to _route so that it can read
566 processor_box.set_route (rt);
568 revert_to_default_display ();
570 /* unpack these from the parent and stuff them into our own
574 if (gpm.peak_display.get_parent()) {
575 gpm.peak_display.get_parent()->remove (gpm.peak_display);
577 if (gpm.gain_display.get_parent()) {
578 gpm.gain_display.get_parent()->remove (gpm.gain_display);
581 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
582 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
584 if (solo_button->get_parent()) {
585 mute_solo_table.remove (*solo_button);
588 if (mute_button->get_parent()) {
589 mute_solo_table.remove (*mute_button);
592 if (route()->is_master()) {
593 solo_button->hide ();
594 mute_button->show ();
595 rec_mon_table.hide ();
596 solo_iso_table.set_sensitive(false);
597 control_slave_ui.set_sensitive(false);
598 if (monitor_section_button == 0) {
599 Glib::RefPtr<Action> act = ActionManager::get_action ("Mixer", "ToggleMonitorSection");
600 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
601 _session->MonitorBusAddedOrRemoved.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_section_added_or_removed, this), gui_context());
603 monitor_section_button = manage (new ArdourButton);
605 monitor_section_button->set_related_action (act);
606 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
607 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
608 monitor_section_button->show();
609 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
610 monitor_section_added_or_removed ();
613 bottom_button_table.attach (group_button, 1, 2, 0, 1);
614 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
615 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
616 mute_button->show ();
617 solo_button->show ();
618 rec_mon_table.show ();
619 solo_iso_table.set_sensitive(true);
620 control_slave_ui.set_sensitive(true);
623 hide_master_spacer (false);
626 monitor_input_button->show ();
627 monitor_disk_button->show ();
629 monitor_input_button->hide();
630 monitor_disk_button->hide ();
633 update_trim_control();
635 if (is_midi_track()) {
636 if (midi_input_enable_button == 0) {
637 midi_input_enable_button = manage (new ArdourButton);
638 midi_input_enable_button->set_name ("midi input button");
639 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
640 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
641 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
642 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
643 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
645 input_button_box.remove (*midi_input_enable_button);
647 /* get current state */
648 midi_input_status_changed ();
649 input_button_box.pack_start (*midi_input_enable_button, false, false);
651 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
653 if (midi_input_enable_button) {
654 /* removal from the container will delete it */
655 input_button_box.remove (*midi_input_enable_button);
656 midi_input_enable_button = 0;
660 if (is_audio_track()) {
661 boost::shared_ptr<AudioTrack> at = audio_track();
662 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
667 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
668 rec_enable_button->show();
670 if (ARDOUR::Profile->get_mixbus()) {
671 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
672 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
673 } else if (ARDOUR::Profile->get_trx()) {
674 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
676 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
677 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
684 if (!_route->is_master()) {
685 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
686 show_sends_button->show();
690 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
692 delete route_ops_menu;
695 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
696 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
697 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
698 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
700 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
702 if (_route->panner_shell()) {
703 update_panner_choices();
704 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
707 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
709 set_stuff_from_route ();
711 /* now force an update of all the various elements */
713 update_mute_display ();
714 update_solo_display ();
717 route_group_changed ();
718 update_track_number_visibility ();
721 panners.setup_pan ();
723 if (has_audio_outputs ()) {
729 update_diskstream_display ();
730 update_input_display ();
731 update_output_display ();
733 add_events (Gdk::BUTTON_RELEASE_MASK);
735 processor_box.show ();
737 if (!route()->is_master() && !route()->is_monitor()) {
738 /* we don't allow master or control routes to be hidden */
743 gpm.reset_peak_display ();
744 gpm.gain_display.show ();
745 gpm.peak_display.show ();
748 width_hide_box.show();
750 global_vpacker.show();
751 mute_solo_table.show();
752 bottom_button_table.show();
754 gpm.meter_point_button.show();
755 input_button_box.show_all();
756 output_button.show();
758 _comment_button.show();
760 gpm.gain_automation_state_button.show();
762 parameter_changed ("mixer-element-visibility");
769 MixerStrip::set_stuff_from_route ()
771 /* if width is not set, it will be set by the MixerUI or editor */
774 if (get_gui_property ("strip-width", width)) {
775 set_width_enum (width, this);
780 MixerStrip::set_width_enum (Width w, void* owner)
782 /* always set the gpm width again, things may be hidden */
785 panners.set_width (w);
787 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
789 _width_owner = owner;
793 if (_width_owner == this) {
794 set_gui_property ("strip-width", _width);
799 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
804 if (show_sends_button) {
805 show_sends_button->set_text (_("Aux"));
808 gpm.gain_automation_state_button.set_text (
809 gpm.astate_string(gain_automation->automation_state()));
811 if (_route->panner()) {
812 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
813 panners.astate_string(_route->panner()->automation_state()));
817 // panners expect an even number of horiz. pixels
818 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
820 set_size_request (width, -1);
826 if (show_sends_button) {
827 show_sends_button->set_text (_("Snd"));
830 gpm.gain_automation_state_button.set_text (
831 gpm.short_astate_string(gain_automation->automation_state()));
832 gain_meter().setup_meters (); // recalc meter width
834 if (_route->panner()) {
835 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
836 panners.short_astate_string(_route->panner()->automation_state()));
840 // panners expect an even number of horiz. pixels
841 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
843 set_size_request (width, -1);
848 processor_box.set_width (w);
850 update_input_display ();
851 update_output_display ();
852 setup_comment_button ();
853 route_group_changed ();
859 MixerStrip::set_packed (bool yn)
862 set_gui_property ("visible", _packed);
866 struct RouteCompareByName {
867 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
868 return a->name().compare (b->name()) < 0;
873 MixerStrip::output_release (GdkEventButton *ev)
875 switch (ev->button) {
877 edit_output_configuration ();
885 MixerStrip::output_press (GdkEventButton *ev)
887 using namespace Menu_Helpers;
888 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
892 MenuList& citems = output_menu.items();
893 switch (ev->button) {
896 return false; //wait for the mouse-up to pop the dialog
900 output_menu.set_name ("ArdourContextMenu");
902 output_menu_bundles.clear ();
904 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
906 citems.push_back (SeparatorElem());
907 uint32_t const n_with_separator = citems.size ();
909 ARDOUR::BundleList current = _route->output()->bundles_connected ();
911 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
913 /* guess the user-intended main type of the route output */
914 DataType intended_type = guess_main_type(false);
916 /* try adding the master bus first */
917 boost::shared_ptr<Route> master = _session->master_out();
919 maybe_add_bundle_to_output_menu (master->input()->bundle(), current, intended_type);
922 /* then other routes inputs */
923 RouteList copy = _session->get_routelist ();
924 copy.sort (RouteCompareByName ());
925 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
926 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current, intended_type);
929 /* then try adding user bundles, often labeled/grouped physical inputs */
930 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
931 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
932 maybe_add_bundle_to_output_menu (*i, current, intended_type);
936 /* then all other bundles, including physical outs or other sofware */
937 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
938 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
939 maybe_add_bundle_to_output_menu (*i, current, intended_type);
943 if (citems.size() == n_with_separator) {
944 /* no routes added; remove the separator */
948 citems.push_back (SeparatorElem());
950 if (!ARDOUR::Profile->get_mixbus()) {
951 bool need_separator = false;
952 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
953 if (!_route->output()->can_add_port (*i)) {
956 need_separator = true;
959 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
960 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
964 if (need_separator) {
965 citems.push_back (SeparatorElem());
969 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
971 Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
984 MixerStrip::input_release (GdkEventButton *ev)
986 switch (ev->button) {
989 edit_input_configuration ();
1001 MixerStrip::input_press (GdkEventButton *ev)
1003 using namespace Menu_Helpers;
1005 MenuList& citems = input_menu.items();
1006 input_menu.set_name ("ArdourContextMenu");
1009 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
1013 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
1016 switch (ev->button) {
1019 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
1023 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
1025 citems.push_back (SeparatorElem());
1026 uint32_t const n_with_separator = citems.size ();
1028 input_menu_bundles.clear ();
1030 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1032 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1034 /* give user bundles first chance at being in the menu */
1036 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1037 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1038 maybe_add_bundle_to_input_menu (*i, current);
1042 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1043 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1044 maybe_add_bundle_to_input_menu (*i, current);
1048 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1049 RouteList copy = *routes;
1050 copy.sort (RouteCompareByName ());
1051 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1052 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1055 if (citems.size() == n_with_separator) {
1056 /* no routes added; remove the separator */
1060 citems.push_back (SeparatorElem());
1062 bool need_separator = false;
1063 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1064 if (!_route->input()->can_add_port (*i)) {
1067 need_separator = true;
1070 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1071 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1075 if (need_separator) {
1076 citems.push_back (SeparatorElem());
1079 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1081 Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
1093 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1095 if (ignore_toggle) {
1099 _route->input()->connect_ports_to_bundle (c, true, this);
1103 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1105 if (ignore_toggle) {
1109 _route->output()->connect_ports_to_bundle (c, true, true, this);
1113 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1115 using namespace Menu_Helpers;
1117 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1121 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1122 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1126 if (i != input_menu_bundles.end()) {
1130 input_menu_bundles.push_back (b);
1132 MenuList& citems = input_menu.items();
1133 citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1137 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/,
1140 using namespace Menu_Helpers;
1142 /* The bundle should be an input one, but not ours */
1143 if (b->ports_are_inputs() == false || *b == *_route->input()->bundle()) {
1147 /* Don't add the monitor input unless we are Master */
1148 boost::shared_ptr<Route> monitor = _session->monitor_out();
1149 if ((!_route->is_master()) && monitor && b->has_same_ports (monitor->input()->bundle()))
1152 /* It should either match exactly our outputs (if |type| is DataType::NIL)
1153 * or have the same number of |type| channels than our outputs. */
1154 if (type == DataType::NIL) {
1155 if(b->nchannels() != _route->n_outputs())
1158 if (b->nchannels().n(type) != _route->n_outputs().n(type))
1162 /* Avoid adding duplicates */
1163 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1164 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1167 if (i != output_menu_bundles.end()) {
1171 /* Now add the bundle to the menu */
1172 output_menu_bundles.push_back (b);
1174 MenuList& citems = output_menu.items();
1175 citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1179 MixerStrip::update_diskstream_display ()
1181 if (is_track() && input_selector) {
1182 input_selector->hide_all ();
1185 route_color_changed ();
1189 MixerStrip::connect_to_pan ()
1191 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1193 panstate_connection.disconnect ();
1194 panstyle_connection.disconnect ();
1196 if (!_route->panner()) {
1200 boost::shared_ptr<Pannable> p = _route->pannable ();
1202 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1204 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1205 * However, that only works a panner was previously set.
1207 * PannerUI must remain subscribed to _panshell->Changed() in case
1208 * we switch the panner eg. AUX-Send and back
1209 * _route->panner_shell()->Changed() vs _panshell->Changed
1211 if (panners._panner == 0) {
1212 panners.panshell_changed ();
1214 update_panner_choices();
1218 MixerStrip::update_panner_choices ()
1220 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1221 if (!_route->panner_shell()) { return; }
1223 uint32_t in = _route->output()->n_ports().n_audio();
1225 if (_route->panner()) {
1226 in = _route->panner()->in().n_audio();
1229 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1233 MixerStrip::guess_main_type(bool for_input, bool favor_connected) const
1235 /* The heuristic follows these principles:
1236 * A) If all ports that the user connected are of the same type, then he
1237 * very probably intends to use the IO with that type. A common subcase
1238 * is when the IO has only ports of the same type (connected or not).
1239 * B) If several types of ports are connected, then we should guess based
1240 * on the likeliness of the user wanting to use a given type.
1241 * We assume that the DataTypes are ordered from the most likely to the
1242 * least likely when iterating or comparing them with "<".
1243 * C) If no port is connected, the same logic can be applied with all ports
1244 * instead of connected ones. TODO: Try other ideas, for instance look at
1245 * the last plugin output when |for_input| is false (note: when StrictIO
1246 * the outs of the last plugin should be the same as the outs of the route
1247 * modulo the panner which forwards non-audio anyway).
1248 * All of these constraints are respected by the following algorithm that
1249 * just returns the most likely datatype found in connected ports if any, or
1250 * available ports if any (since if all ports are of the same type, the most
1251 * likely found will be that one obviously). */
1253 boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
1255 /* Find most likely type among connected ports */
1256 if (favor_connected) {
1257 DataType type = DataType::NIL; /* NIL is always last so least likely */
1258 for (PortSet::iterator p = io->ports().begin(); p != io->ports().end(); ++p) {
1259 if (p->connected() && p->type() < type)
1262 if (type != DataType::NIL) {
1263 /* There has been a connected port (necessarily non-NIL) */
1268 /* Find most likely type among available ports.
1269 * The iterator stops before NIL. */
1270 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
1271 if (io->n_ports().n(*t) > 0)
1275 /* No port at all, return the most likely datatype by default */
1276 return DataType::front();
1280 * Output port labelling
1282 * Case 1: Each output has one connection, all connections are to system:playback_%i
1283 * out 1 -> system:playback_1
1284 * out 2 -> system:playback_2
1285 * out 3 -> system:playback_3
1288 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1289 * out 1 -> ardour:track_x/in 1
1290 * out 2 -> ardour:track_x/in 2
1291 * Display as: track_x
1293 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1294 * out 1 -> program x:foo
1295 * out 2 -> program x:foo
1296 * Display as: program x
1298 * Case 4: No connections (Disconnected)
1301 * Default case (unusual routing):
1302 * Display as: *number of connections*
1307 * .-----------------------------------------------.
1309 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1310 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1311 * '-----------------------------------------------'
1312 * .-----------------------------------------------.
1315 * '-----------------------------------------------'
1319 MixerStrip::update_io_button (bool for_input)
1321 ostringstream tooltip;
1322 ostringstream label;
1323 bool have_label = false;
1325 uint32_t total_connection_count = 0;
1326 uint32_t typed_connection_count = 0;
1327 bool each_typed_port_has_one_connection = true;
1329 DataType dt = guess_main_type(for_input);
1330 boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
1332 /* Fill in the tooltip. Also count:
1333 * - The total number of connections.
1334 * - The number of main-typed connections.
1335 * - Whether each main-typed port has exactly one connection. */
1337 tooltip << string_compose (_("<b>INPUT</b> to %1"),
1338 Gtkmm2ext::markup_escape_text (_route->name()));
1340 tooltip << string_compose (_("<b>OUTPUT</b> from %1"),
1341 Gtkmm2ext::markup_escape_text (_route->name()));
1344 string arrow = Gtkmm2ext::markup_escape_text(for_input ? " <- " : " -> ");
1345 vector<string> port_connections;
1346 for (PortSet::iterator port = io->ports().begin();
1347 port != io->ports().end();
1349 port_connections.clear();
1350 port->get_connections(port_connections);
1352 uint32_t port_connection_count = 0;
1354 for (vector<string>::iterator i = port_connections.begin();
1355 i != port_connections.end();
1357 ++port_connection_count;
1359 if (port_connection_count == 1) {
1360 tooltip << endl << Gtkmm2ext::markup_escape_text (
1361 port->name().substr(port->name().find("/") + 1));
1367 tooltip << Gtkmm2ext::markup_escape_text(*i);
1370 total_connection_count += port_connection_count;
1371 if (port->type() == dt) {
1372 typed_connection_count += port_connection_count;
1373 each_typed_port_has_one_connection &= (port_connection_count == 1);
1378 if (total_connection_count == 0) {
1379 tooltip << endl << _("Disconnected");
1382 if (typed_connection_count == 0) {
1387 /* Are all main-typed channels connected to the same route ? */
1389 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1390 for (ARDOUR::RouteList::const_iterator route = routes->begin();
1391 route != routes->end();
1393 boost::shared_ptr<IO> dest_io =
1394 for_input ? (*route)->output() : (*route)->input();
1395 if (io->bundle()->connected_to(dest_io->bundle(),
1398 label << Gtkmm2ext::markup_escape_text ((*route)->name());
1405 /* Are all main-typed channels connected to the same (user) bundle ? */
1407 boost::shared_ptr<ARDOUR::BundleList> bundles = _session->bundles ();
1408 for (ARDOUR::BundleList::iterator bundle = bundles->begin();
1409 bundle != bundles->end();
1411 if (boost::dynamic_pointer_cast<UserBundle> (*bundle) == 0)
1413 if (io->bundle()->connected_to(*bundle, _session->engine(),
1415 label << Gtkmm2ext::markup_escape_text ((*bundle)->name());
1422 /* Is each main-typed channel only connected to a physical output ? */
1423 if (!have_label && each_typed_port_has_one_connection) {
1424 ostringstream temp_label;
1425 vector<string> phys;
1426 string playorcapture;
1428 _session->engine().get_physical_inputs(dt, phys);
1429 playorcapture = "capture_";
1431 _session->engine().get_physical_outputs(dt, phys);
1432 playorcapture = "playback_";
1434 for (PortSet::iterator port = io->ports().begin(dt);
1435 port != io->ports().end(dt);
1438 for (vector<string>::iterator s = phys.begin();
1441 if (!port->connected_to(*s))
1443 pn = AudioEngine::instance()->get_pretty_name_by_name(*s);
1445 string::size_type start = (*s).find(playorcapture);
1446 if (start != string::npos) {
1447 pn = (*s).substr(start + playorcapture.size());
1453 temp_label.str(""); /* erase the failed attempt */
1456 if (port != io->ports().begin(dt))
1461 if (!temp_label.str().empty()) {
1462 label << temp_label.str();
1467 /* Is each main-typed channel connected to a single and different port with
1468 * the same client name (e.g. another JACK client) ? */
1469 if (!have_label && each_typed_port_has_one_connection) {
1470 string maybe_client = "";
1471 vector<string> connections;
1472 for (PortSet::iterator port = io->ports().begin(dt);
1473 port != io->ports().end(dt);
1475 port_connections.clear();
1476 port->get_connections(port_connections);
1477 string connection = port_connections.front();
1479 vector<string>::iterator i = connections.begin();
1480 while (i != connections.end() && *i != connection) {
1483 if (i != connections.end())
1484 break; /* duplicate connection */
1485 connections.push_back(connection);
1487 connection = connection.substr(0, connection.find(":"));
1488 if (maybe_client.empty())
1489 maybe_client = connection;
1490 if (maybe_client != connection)
1493 if (connections.size() == io->n_ports().n(dt)) {
1494 label << maybe_client;
1499 /* Odd configuration */
1501 label << "*" << total_connection_count << "*";
1504 if (total_connection_count > typed_connection_count) {
1505 label << "\u2295"; /* circled plus */
1508 /* Actually set the properties of the button */
1509 char * cstr = new char[tooltip.str().size() + 1];
1510 strcpy(cstr, tooltip.str().c_str());
1513 input_button.set_text (label.str());
1514 set_tooltip (&input_button, cstr);
1516 output_button.set_text (label.str());
1517 set_tooltip (&output_button, cstr);
1524 MixerStrip::update_input_display ()
1526 update_io_button (true);
1527 panners.setup_pan ();
1529 if (has_audio_outputs ()) {
1530 panners.show_all ();
1532 panners.hide_all ();
1538 MixerStrip::update_output_display ()
1540 update_io_button (false);
1541 gpm.setup_meters ();
1542 panners.setup_pan ();
1544 if (has_audio_outputs ()) {
1545 panners.show_all ();
1547 panners.hide_all ();
1552 MixerStrip::fast_update ()
1554 gpm.update_meters ();
1558 MixerStrip::diskstream_changed ()
1560 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1564 MixerStrip::io_changed_proxy ()
1566 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1567 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_trim_control));
1571 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1573 boost::shared_ptr<Port> a = wa.lock ();
1574 boost::shared_ptr<Port> b = wb.lock ();
1576 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1577 update_input_display ();
1578 set_width_enum (_width, this);
1581 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1582 update_output_display ();
1583 set_width_enum (_width, this);
1588 MixerStrip::setup_comment_button ()
1590 std::string comment = _route->comment();
1592 set_tooltip (_comment_button, comment.empty() ? _("Click to add/edit comments") : _route->comment());
1594 if (comment.empty ()) {
1595 _comment_button.set_name ("generic button");
1596 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1600 _comment_button.set_name ("comment button");
1602 string::size_type pos = comment.find_first_of (" \t\n");
1603 if (pos != string::npos) {
1604 comment = comment.substr (0, pos);
1606 if (comment.empty()) {
1607 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1609 _comment_button.set_text (comment);
1614 MixerStrip::select_route_group (GdkEventButton *ev)
1616 using namespace Menu_Helpers;
1618 if (ev->button == 1) {
1620 if (group_menu == 0) {
1622 PropertyList* plist = new PropertyList();
1624 plist->add (Properties::group_gain, true);
1625 plist->add (Properties::group_mute, true);
1626 plist->add (Properties::group_solo, true);
1628 group_menu = new RouteGroupMenu (_session, plist);
1632 r.push_back (route ());
1633 group_menu->build (r);
1635 RouteGroup *rg = _route->route_group();
1637 Gtkmm2ext::anchored_menu_popup(group_menu->menu(), &group_button,
1638 rg ? rg->name() : _("No Group"),
1646 MixerStrip::route_group_changed ()
1648 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1650 RouteGroup *rg = _route->route_group();
1653 group_button.set_text (PBD::short_version (rg->name(), 5));
1657 group_button.set_text (_("Grp"));
1660 group_button.set_text (_("~G"));
1667 MixerStrip::route_color_changed ()
1669 using namespace ARDOUR_UI_UTILS;
1670 name_button.modify_bg (STATE_NORMAL, color());
1671 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1672 reset_strip_style ();
1676 MixerStrip::show_passthru_color ()
1678 reset_strip_style ();
1683 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1685 boost::shared_ptr<Processor> processor (p.lock ());
1686 if (!processor || !processor->display_to_user()) {
1689 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
1691 if (pi && pi->is_channelstrip ()) {
1696 ++_plugin_insert_cnt;
1700 MixerStrip::build_route_ops_menu ()
1702 using namespace Menu_Helpers;
1703 route_ops_menu = new Menu;
1704 route_ops_menu->set_name ("ArdourContextMenu");
1706 MenuList& items = route_ops_menu->items();
1708 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1710 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1712 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1714 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1716 if (!Profile->get_mixbus()) {
1717 items.push_back (SeparatorElem());
1720 if (!_route->is_master()
1722 && !_route->mixbus()
1725 if (Profile->get_mixbus()) {
1726 items.push_back (SeparatorElem());
1728 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1731 if (!Profile->get_mixbus()) {
1732 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1733 /* do not allow rename if the track is record-enabled */
1734 items.back().set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1737 items.push_back (SeparatorElem());
1738 items.push_back (CheckMenuElem (_("Active")));
1739 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1740 i->set_active (_route->active());
1741 i->set_sensitive(! _session->transport_rolling());
1742 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1744 if (!Profile->get_mixbus ()) {
1745 items.push_back (SeparatorElem());
1746 items.push_back (CheckMenuElem (_("Strict I/O")));
1747 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1748 i->set_active (_route->strict_io());
1749 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1753 items.push_back (SeparatorElem());
1755 Gtk::Menu* dio_menu = new Menu;
1756 MenuList& dio_items = dio_menu->items();
1757 dio_items.push_back (MenuElem (_("Record Pre-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPreFader)));
1758 dio_items.push_back (MenuElem (_("Record Post-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPostFader)));
1759 dio_items.push_back (MenuElem (_("Custom Record+Playback Positions"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOCustom)));
1761 items.push_back (MenuElem (_("Disk I/O..."), *dio_menu));
1764 _plugin_insert_cnt = 0;
1765 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1766 if (_plugin_insert_cnt > 0) {
1767 items.push_back (SeparatorElem());
1768 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1771 if (boost::dynamic_pointer_cast<MidiTrack>(_route) || _route->the_instrument ()) {
1772 items.push_back (MenuElem (_("Patch Selector..."),
1773 sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
1776 if (_route->the_instrument () && _route->the_instrument ()->output_streams().n_audio() > 2) {
1777 // TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
1778 items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
1779 items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
1782 items.push_back (SeparatorElem());
1783 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1785 items.push_back (SeparatorElem());
1786 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1787 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1788 denormal_menu_item->set_active (_route->denormal_protection());
1791 /* note that this relies on selection being shared across editor and
1792 mixer (or global to the backend, in the future), which is the only
1793 sane thing for users anyway.
1796 StripableTimeAxisView* stav = PublicEditor::instance().get_stripable_time_axis_by_id (_route->id());
1798 Selection& selection (PublicEditor::instance().get_selection());
1799 if (!selection.selected (stav)) {
1800 selection.set (stav);
1803 if (!_route->is_master()) {
1804 items.push_back (SeparatorElem());
1805 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1808 items.push_back (SeparatorElem());
1809 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1815 MixerStrip::name_button_button_press (GdkEventButton* ev)
1817 if (ev->button == 1 || ev->button == 3) {
1818 list_route_operations ();
1820 if (ev->button == 1) {
1821 Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
1824 route_ops_menu->popup (3, ev->time);
1834 MixerStrip::number_button_button_press (GdkEventButton* ev)
1836 if ( ev->button == 3 ) {
1837 list_route_operations ();
1839 route_ops_menu->popup (1, ev->time);
1848 MixerStrip::list_route_operations ()
1850 delete route_ops_menu;
1851 build_route_ops_menu ();
1855 MixerStrip::set_selected (bool yn)
1857 AxisView::set_selected (yn);
1860 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1861 global_frame.set_name ("MixerStripSelectedFrame");
1863 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1864 global_frame.set_name ("MixerStripFrame");
1867 global_frame.queue_draw ();
1870 // processor_box.deselect_all_processors();
1874 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1876 if (what_changed.contains (ARDOUR::Properties::name)) {
1882 MixerStrip::name_changed ()
1886 name_button.set_text (_route->name());
1889 name_button.set_text (PBD::short_version (_route->name(), 5));
1893 set_tooltip (name_button, Gtkmm2ext::markup_escape_text(_route->name()));
1895 if (_session->config.get_track_name_number()) {
1896 const int64_t track_number = _route->track_number ();
1897 if (track_number == 0) {
1898 number_label.set_text ("-");
1900 number_label.set_text (PBD::to_string (abs(_route->track_number ())));
1903 number_label.set_text ("");
1908 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1910 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1914 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1916 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1920 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1922 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1926 MixerStrip::comment_button_resized (Gtk::Allocation& alloc)
1928 _comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1932 MixerStrip::width_button_pressed (GdkEventButton* ev)
1934 if (ev->button != 1) {
1938 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1941 _mixer.set_strip_width (Narrow, true);
1945 _mixer.set_strip_width (Wide, true);
1951 set_width_enum (Narrow, this);
1954 set_width_enum (Wide, this);
1963 MixerStrip::hide_clicked ()
1965 // LAME fix to reset the button status for when it is redisplayed (part 1)
1966 hide_button.set_sensitive(false);
1969 Hiding(); /* EMIT_SIGNAL */
1971 _mixer.hide_strip (this);
1975 hide_button.set_sensitive(true);
1979 MixerStrip::set_embedded (bool yn)
1985 MixerStrip::map_frozen ()
1987 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1989 boost::shared_ptr<AudioTrack> at = audio_track();
1992 switch (at->freeze_state()) {
1993 case AudioTrack::Frozen:
1994 processor_box.set_sensitive (false);
1995 hide_redirect_editors ();
1998 processor_box.set_sensitive (true);
1999 // XXX need some way, maybe, to retoggle redirect editors
2003 processor_box.set_sensitive (true);
2005 RouteUI::map_frozen ();
2009 MixerStrip::hide_redirect_editors ()
2011 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
2015 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
2017 boost::shared_ptr<Processor> processor (p.lock ());
2022 Gtk::Window* w = processor_box.get_processor_ui (processor);
2030 MixerStrip::reset_strip_style ()
2032 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2034 gpm.set_fader_name ("SendStripBase");
2038 if (is_midi_track()) {
2039 if (_route->active()) {
2040 set_name ("MidiTrackStripBase");
2042 set_name ("MidiTrackStripBaseInactive");
2044 gpm.set_fader_name ("MidiTrackFader");
2045 } else if (is_audio_track()) {
2046 if (_route->active()) {
2047 set_name ("AudioTrackStripBase");
2049 set_name ("AudioTrackStripBaseInactive");
2051 gpm.set_fader_name ("AudioTrackFader");
2053 if (_route->active()) {
2054 set_name ("AudioBusStripBase");
2056 set_name ("AudioBusStripBaseInactive");
2058 gpm.set_fader_name ("AudioBusFader");
2060 /* (no MIDI busses yet) */
2067 MixerStrip::engine_stopped ()
2072 MixerStrip::engine_running ()
2077 MixerStrip::meter_point_string (MeterPoint mp)
2090 case MeterPostFader:
2107 return S_("Meter|In");
2111 return S_("Meter|Pr");
2114 case MeterPostFader:
2115 return S_("Meter|Po");
2119 return S_("Meter|O");
2124 return S_("Meter|C");
2133 /** Called when the monitor-section state */
2135 MixerStrip::monitor_changed ()
2137 assert (monitor_section_button);
2138 if (_session->monitor_active()) {
2139 monitor_section_button->set_name ("master monitor section button active");
2141 monitor_section_button->set_name ("master monitor section button normal");
2146 MixerStrip::monitor_section_added_or_removed ()
2148 assert (monitor_section_button);
2149 if (mute_button->get_parent()) {
2150 mute_button->get_parent()->remove(*mute_button);
2152 if (monitor_section_button->get_parent()) {
2153 monitor_section_button->get_parent()->remove(*monitor_section_button);
2155 if (_session && _session->monitor_out ()) {
2156 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2157 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2158 mute_button->show();
2159 monitor_section_button->show();
2161 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2162 mute_button->show();
2166 /** Called when the metering point has changed */
2168 MixerStrip::meter_changed ()
2170 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2171 gpm.setup_meters ();
2172 // reset peak when meter point changes
2173 gpm.reset_peak_display();
2176 /** The bus that we are displaying sends to has changed, or been turned off.
2177 * @param send_to New bus that we are displaying sends to, or 0.
2180 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2182 RouteUI::bus_send_display_changed (send_to);
2185 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2190 revert_to_default_display ();
2193 revert_to_default_display ();
2198 MixerStrip::drop_send ()
2200 boost::shared_ptr<Send> current_send;
2202 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2203 current_send->set_metering (false);
2206 send_gone_connection.disconnect ();
2207 input_button.set_sensitive (true);
2208 output_button.set_sensitive (true);
2209 group_button.set_sensitive (true);
2210 set_invert_sensitive (true);
2211 gpm.meter_point_button.set_sensitive (true);
2212 mute_button->set_sensitive (true);
2213 solo_button->set_sensitive (true);
2214 solo_isolated_led->set_sensitive (true);
2215 solo_safe_led->set_sensitive (true);
2216 monitor_input_button->set_sensitive (true);
2217 monitor_disk_button->set_sensitive (true);
2218 _comment_button.set_sensitive (true);
2219 trim_control.set_sensitive (true);
2220 if (midi_input_enable_button) {
2221 midi_input_enable_button->set_sensitive (true);
2223 control_slave_ui.set_sensitive (true);
2224 RouteUI::check_rec_enable_sensitivity ();
2225 set_button_names (); // update solo button visual state
2229 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2231 _current_delivery = d;
2232 DeliveryChanged (_current_delivery);
2236 MixerStrip::show_send (boost::shared_ptr<Send> send)
2242 set_current_delivery (send);
2244 send->meter()->set_meter_type (_route->meter_type ());
2245 send->set_metering (true);
2246 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2248 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2249 gain_meter().setup_meters ();
2251 uint32_t const in = _current_delivery->pans_required();
2252 uint32_t const out = _current_delivery->pan_outs();
2254 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2255 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2256 panner_ui().setup_pan ();
2257 panner_ui().set_send_drawing_mode (true);
2258 panner_ui().show_all ();
2260 input_button.set_sensitive (false);
2261 group_button.set_sensitive (false);
2262 set_invert_sensitive (false);
2263 gpm.meter_point_button.set_sensitive (false);
2264 mute_button->set_sensitive (false);
2265 solo_button->set_sensitive (false);
2266 rec_enable_button->set_sensitive (false);
2267 solo_isolated_led->set_sensitive (false);
2268 solo_safe_led->set_sensitive (false);
2269 monitor_input_button->set_sensitive (false);
2270 monitor_disk_button->set_sensitive (false);
2271 _comment_button.set_sensitive (false);
2272 trim_control.set_sensitive (false);
2273 if (midi_input_enable_button) {
2274 midi_input_enable_button->set_sensitive (false);
2276 control_slave_ui.set_sensitive (false);
2278 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2279 output_button.set_sensitive (false);
2282 reset_strip_style ();
2286 MixerStrip::revert_to_default_display ()
2290 set_current_delivery (_route->main_outs ());
2292 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2293 gain_meter().setup_meters ();
2295 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2296 update_panner_choices();
2297 panner_ui().setup_pan ();
2298 panner_ui().set_send_drawing_mode (false);
2300 if (has_audio_outputs ()) {
2301 panners.show_all ();
2303 panners.hide_all ();
2306 reset_strip_style ();
2310 MixerStrip::set_button_names ()
2314 mute_button->set_text (_("Mute"));
2315 monitor_input_button->set_text (_("In"));
2316 monitor_disk_button->set_text (_("Disk"));
2317 if (monitor_section_button) {
2318 monitor_section_button->set_text (_("Mon"));
2321 if (_route && _route->solo_safe_control()->solo_safe()) {
2322 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2324 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2326 if (!Config->get_solo_control_is_listen_control()) {
2327 solo_button->set_text (_("Solo"));
2329 switch (Config->get_listen_position()) {
2330 case AfterFaderListen:
2331 solo_button->set_text (_("AFL"));
2333 case PreFaderListen:
2334 solo_button->set_text (_("PFL"));
2338 solo_isolated_led->set_text (_("Iso"));
2339 solo_safe_led->set_text (S_("SoloLock|Lock"));
2343 mute_button->set_text (S_("Mute|M"));
2344 monitor_input_button->set_text (S_("MonitorInput|I"));
2345 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2346 if (monitor_section_button) {
2347 monitor_section_button->set_text (S_("Mon|O"));
2350 if (_route && _route->solo_safe_control()->solo_safe()) {
2351 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2353 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2355 if (!Config->get_solo_control_is_listen_control()) {
2356 solo_button->set_text (S_("Solo|S"));
2358 switch (Config->get_listen_position()) {
2359 case AfterFaderListen:
2360 solo_button->set_text (S_("AfterFader|A"));
2362 case PreFaderListen:
2363 solo_button->set_text (S_("Prefader|P"));
2368 solo_isolated_led->set_text (S_("SoloIso|I"));
2369 solo_safe_led->set_text (S_("SoloLock|L"));
2374 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2376 gpm.meter_point_button.set_text ("");
2381 MixerStrip::plugin_selector()
2383 return _mixer.plugin_selector();
2387 MixerStrip::hide_things ()
2389 processor_box.hide_things ();
2393 MixerStrip::input_active_button_press (GdkEventButton*)
2395 /* nothing happens on press */
2400 MixerStrip::input_active_button_release (GdkEventButton* ev)
2402 boost::shared_ptr<MidiTrack> mt = midi_track ();
2408 boost::shared_ptr<RouteList> rl (new RouteList);
2410 rl->push_back (route());
2412 _session->set_exclusive_input_active (rl, !mt->input_active(),
2413 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2419 MixerStrip::midi_input_status_changed ()
2421 if (midi_input_enable_button) {
2422 boost::shared_ptr<MidiTrack> mt = midi_track ();
2424 midi_input_enable_button->set_active (mt->input_active ());
2429 MixerStrip::state_id () const
2431 return string_compose ("strip %1", _route->id().to_s());
2435 MixerStrip::parameter_changed (string p)
2437 if (p == _visibility.get_state_name()) {
2438 /* The user has made changes to the mixer strip visibility, so get
2439 our VisibilityGroup to reflect these changes in our widgets.
2441 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2442 } else if (p == "track-name-number") {
2444 update_track_number_visibility();
2448 /** Called to decide whether the solo isolate / solo lock button visibility should
2449 * be overridden from that configured by the user. We do this for the master bus.
2451 * @return optional value that is present if visibility state should be overridden.
2453 boost::optional<bool>
2454 MixerStrip::override_solo_visibility () const
2456 if (_route && _route->is_master ()) {
2457 return boost::optional<bool> (false);
2460 return boost::optional<bool> ();
2464 MixerStrip::add_input_port (DataType t)
2466 _route->input()->add_port ("", this, t);
2470 MixerStrip::add_output_port (DataType t)
2472 _route->output()->add_port ("", this, t);
2476 MixerStrip::route_active_changed ()
2478 reset_strip_style ();
2482 MixerStrip::copy_processors ()
2484 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2488 MixerStrip::cut_processors ()
2490 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2494 MixerStrip::paste_processors ()
2496 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2500 MixerStrip::select_all_processors ()
2502 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2506 MixerStrip::deselect_all_processors ()
2508 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2512 MixerStrip::delete_processors ()
2514 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2518 MixerStrip::toggle_processors ()
2520 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2524 MixerStrip::ab_plugins ()
2526 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2530 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2532 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2535 if (ev->button == 3) {
2536 popup_level_meter_menu (ev);
2544 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2546 using namespace Gtk::Menu_Helpers;
2548 Gtk::Menu* m = ARDOUR_UI_UTILS::shared_popup_menu ();
2549 MenuList& items = m->items ();
2551 RadioMenuItem::Group group;
2553 PBD::Unwinder<bool> uw (_suspend_menu_callbacks, true);
2554 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2555 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2556 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2557 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2558 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2560 if (gpm.meter_channels().n_audio() == 0) {
2561 m->popup (ev->button, ev->time);
2565 RadioMenuItem::Group tgroup;
2566 items.push_back (SeparatorElem());
2568 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2569 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2570 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2571 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2572 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2573 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2574 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2575 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2576 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2577 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2578 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2581 if (_route->is_master()) {
2584 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2585 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2586 /* non-master bus */
2589 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2596 MeterType cmt = _route->meter_type();
2597 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2599 items.push_back (SeparatorElem());
2600 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2601 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2602 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2603 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2604 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2605 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2607 m->popup (ev->button, ev->time);
2611 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2612 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2614 using namespace Menu_Helpers;
2616 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2617 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2618 i->set_active (_route->meter_point() == point);
2622 MixerStrip::set_meter_point (MeterPoint p)
2624 if (_suspend_menu_callbacks) return;
2625 _route->set_meter_point (p);
2629 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2630 RadioMenuItem::Group& group, string const & name, MeterType type)
2632 using namespace Menu_Helpers;
2634 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2635 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2636 i->set_active (_route->meter_type() == type);
2640 MixerStrip::set_meter_type (MeterType t)
2642 if (_suspend_menu_callbacks) return;
2643 _route->set_meter_type (t);
2647 MixerStrip::update_track_number_visibility ()
2649 DisplaySuspender ds;
2650 bool show_label = _session->config.get_track_name_number();
2652 if (_route && _route->is_master()) {
2657 number_label.show ();
2658 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2659 // except the width of the number label is subtracted from the name-hbox, so we
2660 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2661 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2663 number_label.set_size_request(tnw, -1);
2664 number_label.show ();
2666 number_label.hide ();
2671 MixerStrip::color () const
2673 return route_color ();
2677 MixerStrip::marked_for_display () const
2679 return !_route->presentation_info().hidden();
2683 MixerStrip::set_marked_for_display (bool yn)
2685 return RouteUI::mark_hidden (!yn);
2689 MixerStrip::hide_master_spacer (bool yn)
2691 if (_mixer_owned && route()->is_master() && !yn) {