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::HideEye);
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 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
224 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
225 solo_iso_table.show ();
227 rec_mon_table.set_homogeneous (true);
228 rec_mon_table.set_row_spacings (2);
229 rec_mon_table.set_col_spacings (2);
230 if (ARDOUR::Profile->get_mixbus()) {
231 rec_mon_table.resize (1, 3);
232 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
233 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
235 rec_mon_table.show ();
237 if (solo_isolated_led) {
238 button_size_group->add_widget (*solo_isolated_led);
241 button_size_group->add_widget (*solo_safe_led);
244 if (!ARDOUR::Profile->get_mixbus()) {
245 if (rec_enable_button) {
246 button_size_group->add_widget (*rec_enable_button);
248 if (monitor_disk_button) {
249 button_size_group->add_widget (*monitor_disk_button);
251 if (monitor_input_button) {
252 button_size_group->add_widget (*monitor_input_button);
256 mute_solo_table.set_homogeneous (true);
257 mute_solo_table.set_spacings (2);
259 bottom_button_table.set_spacings (2);
260 bottom_button_table.set_homogeneous (true);
261 bottom_button_table.attach (group_button, 1, 2, 0, 1);
262 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
264 name_button.set_name ("mixer strip button");
265 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
266 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
268 set_tooltip (&group_button, _("Mix group"));
269 group_button.set_name ("mixer strip button");
271 _comment_button.set_name (X_("mixer strip button"));
272 _comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
273 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
274 _comment_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::comment_button_resized));
276 // TODO implement ArdourKnob::on_size_request properly
277 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
278 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
280 trim_control.set_tooltip_prefix (_("Trim: "));
281 trim_control.set_name ("trim knob");
282 trim_control.set_no_show_all (true);
283 trim_control.StartGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_start_touch));
284 trim_control.StopGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_end_touch));
285 input_button_box.pack_start (trim_control, false, false);
287 global_vpacker.set_border_width (1);
288 global_vpacker.set_spacing (0);
290 width_button.set_name ("mixer strip button");
291 hide_button.set_name ("mixer strip button");
293 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
294 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
296 width_hide_box.set_spacing (2);
297 width_hide_box.pack_start (width_button, false, true);
298 width_hide_box.pack_start (number_label, true, true);
299 width_hide_box.pack_end (hide_button, false, true);
301 number_label.set_text ("-");
302 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
303 number_label.set_no_show_all ();
304 number_label.set_name ("tracknumber label");
305 number_label.set_fixed_colors (0x80808080, 0x80808080);
306 number_label.set_alignment (.5, .5);
307 number_label.set_fallthrough_to_parent (true);
308 number_label.set_tweaks (ArdourButton::OccasionalText);
310 global_vpacker.set_spacing (2);
311 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
313 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
314 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
315 global_vpacker.pack_start (processor_box, true, true);
316 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
317 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
318 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
319 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
320 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
321 global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
322 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
323 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
324 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
327 //add a spacer underneath the master bus;
328 //this fills the area that is taken up by the scrollbar on the tracks;
329 //and therefore keeps the faders "even" across the bottom
330 int scrollbar_height = 0;
332 Gtk::Window window (WINDOW_TOPLEVEL);
333 HScrollbar scrollbar;
334 window.add (scrollbar);
335 scrollbar.set_name ("MixerWindow");
336 scrollbar.ensure_style();
337 Gtk::Requisition requisition(scrollbar.size_request ());
338 scrollbar_height = requisition.height;
340 spacer.set_size_request (-1, scrollbar_height);
341 global_vpacker.pack_end (spacer, false, false);
344 global_frame.add (global_vpacker);
345 global_frame.set_shadow_type (Gtk::SHADOW_IN);
346 global_frame.set_name ("BaseFrame");
350 /* force setting of visible selected status */
353 set_selected (false);
358 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
359 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
360 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
362 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
363 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
365 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
366 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
367 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
369 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
371 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
373 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
377 /* start off as a passthru strip. we'll correct this, if necessary,
378 in update_diskstream_display().
381 /* start off as a passthru strip. we'll correct this, if necessary,
382 in update_diskstream_display().
385 if (is_midi_track()) {
386 set_name ("MidiTrackStripBase");
388 set_name ("AudioTrackStripBase");
391 add_events (Gdk::BUTTON_RELEASE_MASK|
392 Gdk::ENTER_NOTIFY_MASK|
393 Gdk::LEAVE_NOTIFY_MASK|
395 Gdk::KEY_RELEASE_MASK);
397 set_flags (get_flags() | Gtk::CAN_FOCUS);
399 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
400 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
403 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
404 must be the same as those used in RCOptionEditor so that the configuration changes
405 are recognised when they occur.
407 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
408 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
409 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
410 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
411 _visibility.add (&output_button, X_("Output"), _("Output"), false);
412 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
413 _visibility.add (&control_slave_ui, X_("VCA"), _("VCA Assigns"), false);
415 parameter_changed (X_("mixer-element-visibility"));
416 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
417 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
418 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
420 //watch for mouse enter/exit so we can do some stuff
421 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
422 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
424 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
427 MixerStrip::~MixerStrip ()
429 CatchDeletion (this);
431 if (this ==_entered_mixer_strip)
432 _entered_mixer_strip = NULL;
436 MixerStrip::vca_assign (boost::shared_ptr<ARDOUR::VCA> vca)
438 boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
444 MixerStrip::vca_unassign (boost::shared_ptr<ARDOUR::VCA> vca)
446 boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
452 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
454 _entered_mixer_strip = this;
456 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
457 //because the mixerstrip control is a parent that encompasses the strip
458 deselect_all_processors();
464 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
466 //if we have moved outside our strip, but not into a child view, then deselect ourselves
467 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
468 _entered_mixer_strip= 0;
470 //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
471 gpm.gain_display.set_sensitive(false);
473 gpm.gain_display.set_sensitive(true);
475 //if we leave this mixer strip we need to clear out any selections
476 //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
483 MixerStrip::name() const
486 return _route->name();
492 MixerStrip::update_trim_control ()
494 if (route()->trim() && route()->trim()->active() &&
495 route()->n_inputs().n_audio() > 0) {
496 trim_control.show ();
497 trim_control.set_controllable (route()->trim()->gain_control());
499 trim_control.hide ();
500 boost::shared_ptr<Controllable> none;
501 trim_control.set_controllable (none);
506 MixerStrip::trim_start_touch ()
508 assert (_route && _session);
509 if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
510 route()->trim()->gain_control ()->start_touch (_session->transport_sample());
515 MixerStrip::trim_end_touch ()
517 assert (_route && _session);
518 if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
519 route()->trim()->gain_control ()->stop_touch (_session->transport_sample());
524 MixerStrip::set_route (boost::shared_ptr<Route> rt)
526 //the rec/monitor stuff only shows up for tracks.
527 //the show_sends only shows up for buses.
528 //remove them all here, and we may add them back later
529 if (show_sends_button->get_parent()) {
530 rec_mon_table.remove (*show_sends_button);
532 if (rec_enable_button->get_parent()) {
533 rec_mon_table.remove (*rec_enable_button);
535 if (monitor_input_button->get_parent()) {
536 rec_mon_table.remove (*monitor_input_button);
538 if (monitor_disk_button->get_parent()) {
539 rec_mon_table.remove (*monitor_disk_button);
541 if (group_button.get_parent()) {
542 bottom_button_table.remove (group_button);
545 RouteUI::set_route (rt);
547 control_slave_ui.set_stripable (boost::dynamic_pointer_cast<Stripable> (rt));
549 /* ProcessorBox needs access to _route so that it can read
552 processor_box.set_route (rt);
554 revert_to_default_display ();
556 /* unpack these from the parent and stuff them into our own
560 if (gpm.peak_display.get_parent()) {
561 gpm.peak_display.get_parent()->remove (gpm.peak_display);
563 if (gpm.gain_display.get_parent()) {
564 gpm.gain_display.get_parent()->remove (gpm.gain_display);
567 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
568 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
570 if (solo_button->get_parent()) {
571 mute_solo_table.remove (*solo_button);
574 if (mute_button->get_parent()) {
575 mute_solo_table.remove (*mute_button);
578 if (route()->is_master()) {
579 solo_button->hide ();
580 mute_button->show ();
581 rec_mon_table.hide ();
582 solo_iso_table.set_sensitive(false);
583 control_slave_ui.set_sensitive(false);
584 if (monitor_section_button == 0) {
585 Glib::RefPtr<Action> act = ActionManager::get_action ("Mixer", "ToggleMonitorSection");
586 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
587 _session->MonitorBusAddedOrRemoved.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_section_added_or_removed, this), gui_context());
589 monitor_section_button = manage (new ArdourButton);
591 monitor_section_button->set_related_action (act);
592 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
593 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
594 monitor_section_button->show();
595 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
596 monitor_section_added_or_removed ();
599 bottom_button_table.attach (group_button, 1, 2, 0, 1);
600 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
601 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
602 mute_button->show ();
603 solo_button->show ();
604 rec_mon_table.show ();
605 solo_iso_table.set_sensitive(true);
606 control_slave_ui.set_sensitive(true);
609 hide_master_spacer (false);
612 monitor_input_button->show ();
613 monitor_disk_button->show ();
615 monitor_input_button->hide();
616 monitor_disk_button->hide ();
619 update_trim_control();
621 if (is_midi_track()) {
622 if (midi_input_enable_button == 0) {
623 midi_input_enable_button = manage (new ArdourButton);
624 midi_input_enable_button->set_name ("midi input button");
625 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
626 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
627 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
628 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
629 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
631 input_button_box.remove (*midi_input_enable_button);
633 /* get current state */
634 midi_input_status_changed ();
635 input_button_box.pack_start (*midi_input_enable_button, false, false);
637 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
639 if (midi_input_enable_button) {
640 /* removal from the container will delete it */
641 input_button_box.remove (*midi_input_enable_button);
642 midi_input_enable_button = 0;
646 if (is_audio_track()) {
647 boost::shared_ptr<AudioTrack> at = audio_track();
648 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
653 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
654 rec_enable_button->show();
656 if (ARDOUR::Profile->get_mixbus()) {
657 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
658 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
660 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
661 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
668 if (!_route->is_master()) {
669 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
670 show_sends_button->show();
674 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
676 delete route_ops_menu;
679 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
680 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
681 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
682 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
684 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
686 if (_route->panner_shell()) {
687 update_panner_choices();
688 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
691 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
693 set_stuff_from_route ();
695 /* now force an update of all the various elements */
697 update_mute_display ();
698 update_solo_display ();
701 route_group_changed ();
702 update_track_number_visibility ();
705 panners.setup_pan ();
707 if (has_audio_outputs ()) {
713 update_diskstream_display ();
714 update_input_display ();
715 update_output_display ();
717 add_events (Gdk::BUTTON_RELEASE_MASK);
719 processor_box.show ();
721 if (!route()->is_master() && !route()->is_monitor()) {
722 /* we don't allow master or control routes to be hidden */
727 gpm.reset_peak_display ();
728 gpm.gain_display.show ();
729 gpm.peak_display.show ();
732 width_hide_box.show();
734 global_vpacker.show();
735 mute_solo_table.show();
736 bottom_button_table.show();
738 gpm.meter_point_button.show();
739 input_button_box.show_all();
740 output_button.show();
742 _comment_button.show();
744 gpm.gain_automation_state_button.show();
746 parameter_changed ("mixer-element-visibility");
753 MixerStrip::set_stuff_from_route ()
755 /* if width is not set, it will be set by the MixerUI or editor */
758 if (get_gui_property ("strip-width", width)) {
759 set_width_enum (width, this);
764 MixerStrip::set_width_enum (Width w, void* owner)
766 /* always set the gpm width again, things may be hidden */
769 panners.set_width (w);
771 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
773 _width_owner = owner;
777 if (_width_owner == this) {
778 set_gui_property ("strip-width", _width);
783 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
788 if (show_sends_button) {
789 show_sends_button->set_text (_("Aux"));
792 gpm.gain_automation_state_button.set_text (
793 gpm.astate_string(gain_automation->automation_state()));
795 if (_route->panner()) {
796 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
797 panners.astate_string(_route->panner()->automation_state()));
801 // panners expect an even number of horiz. pixels
802 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
804 set_size_request (width, -1);
810 if (show_sends_button) {
811 show_sends_button->set_text (_("Snd"));
814 gpm.gain_automation_state_button.set_text (
815 gpm.short_astate_string(gain_automation->automation_state()));
816 gain_meter().setup_meters (); // recalc meter width
818 if (_route->panner()) {
819 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
820 panners.short_astate_string(_route->panner()->automation_state()));
824 // panners expect an even number of horiz. pixels
825 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
827 set_size_request (width, -1);
832 processor_box.set_width (w);
834 update_input_display ();
835 update_output_display ();
836 setup_comment_button ();
837 route_group_changed ();
843 MixerStrip::set_packed (bool yn)
846 set_gui_property ("visible", _packed);
850 struct RouteCompareByName {
851 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
852 return a->name().compare (b->name()) < 0;
857 MixerStrip::output_release (GdkEventButton *ev)
859 switch (ev->button) {
861 edit_output_configuration ();
869 MixerStrip::output_press (GdkEventButton *ev)
871 using namespace Menu_Helpers;
872 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
876 MenuList& citems = output_menu.items();
877 switch (ev->button) {
880 return false; //wait for the mouse-up to pop the dialog
884 output_menu.set_name ("ArdourContextMenu");
886 output_menu_bundles.clear ();
888 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
890 citems.push_back (SeparatorElem());
891 uint32_t const n_with_separator = citems.size ();
893 ARDOUR::BundleList current = _route->output()->bundles_connected ();
895 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
897 /* guess the user-intended main type of the route output */
898 DataType intended_type = guess_main_type(false);
900 /* try adding the master bus first */
901 boost::shared_ptr<Route> master = _session->master_out();
903 maybe_add_bundle_to_output_menu (master->input()->bundle(), current, intended_type);
906 /* then other routes inputs */
907 RouteList copy = _session->get_routelist ();
908 copy.sort (RouteCompareByName ());
909 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
910 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current, intended_type);
913 /* then try adding user bundles, often labeled/grouped physical inputs */
914 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
915 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
916 maybe_add_bundle_to_output_menu (*i, current, intended_type);
920 /* then all other bundles, including physical outs or other sofware */
921 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
922 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
923 maybe_add_bundle_to_output_menu (*i, current, intended_type);
927 if (citems.size() == n_with_separator) {
928 /* no routes added; remove the separator */
932 citems.push_back (SeparatorElem());
934 if (!ARDOUR::Profile->get_mixbus()) {
935 bool need_separator = false;
936 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
937 if (!_route->output()->can_add_port (*i)) {
940 need_separator = true;
943 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
944 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
948 if (need_separator) {
949 citems.push_back (SeparatorElem());
953 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
955 Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
968 MixerStrip::input_release (GdkEventButton *ev)
970 switch (ev->button) {
973 edit_input_configuration ();
985 MixerStrip::input_press (GdkEventButton *ev)
987 using namespace Menu_Helpers;
989 MenuList& citems = input_menu.items();
990 input_menu.set_name ("ArdourContextMenu");
993 if (!ARDOUR_UI_UTILS::engine_is_running ()) {
997 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
1000 switch (ev->button) {
1003 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
1007 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
1009 citems.push_back (SeparatorElem());
1010 uint32_t const n_with_separator = citems.size ();
1012 input_menu_bundles.clear ();
1014 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1016 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1018 /* give user bundles first chance at being in the menu */
1020 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1021 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1022 maybe_add_bundle_to_input_menu (*i, current);
1026 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1027 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1028 maybe_add_bundle_to_input_menu (*i, current);
1032 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1033 RouteList copy = *routes;
1034 copy.sort (RouteCompareByName ());
1035 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1036 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1039 if (citems.size() == n_with_separator) {
1040 /* no routes added; remove the separator */
1044 citems.push_back (SeparatorElem());
1046 bool need_separator = false;
1047 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1048 if (!_route->input()->can_add_port (*i)) {
1051 need_separator = true;
1054 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1055 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1059 if (need_separator) {
1060 citems.push_back (SeparatorElem());
1063 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1065 Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
1077 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1079 if (ignore_toggle) {
1083 _route->input()->connect_ports_to_bundle (c, true, this);
1087 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1089 if (ignore_toggle) {
1093 _route->output()->connect_ports_to_bundle (c, true, true, this);
1097 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1099 using namespace Menu_Helpers;
1101 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1105 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1106 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1110 if (i != input_menu_bundles.end()) {
1114 input_menu_bundles.push_back (b);
1116 MenuList& citems = input_menu.items();
1117 citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1121 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/,
1124 using namespace Menu_Helpers;
1126 /* The bundle should be an input one, but not ours */
1127 if (b->ports_are_inputs() == false || *b == *_route->input()->bundle()) {
1131 /* Don't add the monitor input unless we are Master */
1132 boost::shared_ptr<Route> monitor = _session->monitor_out();
1133 if ((!_route->is_master()) && monitor && b->has_same_ports (monitor->input()->bundle()))
1136 /* It should either match exactly our outputs (if |type| is DataType::NIL)
1137 * or have the same number of |type| channels than our outputs. */
1138 if (type == DataType::NIL) {
1139 if(b->nchannels() != _route->n_outputs())
1142 if (b->nchannels().n(type) != _route->n_outputs().n(type))
1146 /* Avoid adding duplicates */
1147 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1148 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1151 if (i != output_menu_bundles.end()) {
1155 /* Now add the bundle to the menu */
1156 output_menu_bundles.push_back (b);
1158 MenuList& citems = output_menu.items();
1159 citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1163 MixerStrip::update_diskstream_display ()
1165 if (is_track() && input_selector) {
1166 input_selector->hide_all ();
1169 route_color_changed ();
1173 MixerStrip::connect_to_pan ()
1175 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1177 panstate_connection.disconnect ();
1178 panstyle_connection.disconnect ();
1180 if (!_route->panner()) {
1184 boost::shared_ptr<Pannable> p = _route->pannable ();
1186 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1188 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1189 * However, that only works a panner was previously set.
1191 * PannerUI must remain subscribed to _panshell->Changed() in case
1192 * we switch the panner eg. AUX-Send and back
1193 * _route->panner_shell()->Changed() vs _panshell->Changed
1195 if (panners._panner == 0) {
1196 panners.panshell_changed ();
1198 update_panner_choices();
1202 MixerStrip::update_panner_choices ()
1204 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1205 if (!_route->panner_shell()) { return; }
1207 uint32_t in = _route->output()->n_ports().n_audio();
1209 if (_route->panner()) {
1210 in = _route->panner()->in().n_audio();
1213 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1217 MixerStrip::guess_main_type(bool for_input, bool favor_connected) const
1219 /* The heuristic follows these principles:
1220 * A) If all ports that the user connected are of the same type, then he
1221 * very probably intends to use the IO with that type. A common subcase
1222 * is when the IO has only ports of the same type (connected or not).
1223 * B) If several types of ports are connected, then we should guess based
1224 * on the likeliness of the user wanting to use a given type.
1225 * We assume that the DataTypes are ordered from the most likely to the
1226 * least likely when iterating or comparing them with "<".
1227 * C) If no port is connected, the same logic can be applied with all ports
1228 * instead of connected ones. TODO: Try other ideas, for instance look at
1229 * the last plugin output when |for_input| is false (note: when StrictIO
1230 * the outs of the last plugin should be the same as the outs of the route
1231 * modulo the panner which forwards non-audio anyway).
1232 * All of these constraints are respected by the following algorithm that
1233 * just returns the most likely datatype found in connected ports if any, or
1234 * available ports if any (since if all ports are of the same type, the most
1235 * likely found will be that one obviously). */
1237 boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
1239 /* Find most likely type among connected ports */
1240 if (favor_connected) {
1241 DataType type = DataType::NIL; /* NIL is always last so least likely */
1242 for (PortSet::iterator p = io->ports().begin(); p != io->ports().end(); ++p) {
1243 if (p->connected() && p->type() < type)
1246 if (type != DataType::NIL) {
1247 /* There has been a connected port (necessarily non-NIL) */
1252 /* Find most likely type among available ports.
1253 * The iterator stops before NIL. */
1254 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
1255 if (io->n_ports().n(*t) > 0)
1259 /* No port at all, return the most likely datatype by default */
1260 return DataType::front();
1264 * Output port labelling
1266 * Case 1: Each output has one connection, all connections are to system:playback_%i
1267 * out 1 -> system:playback_1
1268 * out 2 -> system:playback_2
1269 * out 3 -> system:playback_3
1272 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1273 * out 1 -> ardour:track_x/in 1
1274 * out 2 -> ardour:track_x/in 2
1275 * Display as: track_x
1277 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1278 * out 1 -> program x:foo
1279 * out 2 -> program x:foo
1280 * Display as: program x
1282 * Case 4: No connections (Disconnected)
1285 * Default case (unusual routing):
1286 * Display as: *number of connections*
1291 * .-----------------------------------------------.
1293 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1294 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1295 * '-----------------------------------------------'
1296 * .-----------------------------------------------.
1299 * '-----------------------------------------------'
1303 MixerStrip::update_io_button (bool for_input)
1305 ostringstream tooltip;
1306 ostringstream label;
1307 bool have_label = false;
1309 uint32_t total_connection_count = 0;
1310 uint32_t typed_connection_count = 0;
1311 bool each_typed_port_has_one_connection = true;
1313 DataType dt = guess_main_type(for_input);
1314 boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
1316 /* Fill in the tooltip. Also count:
1317 * - The total number of connections.
1318 * - The number of main-typed connections.
1319 * - Whether each main-typed port has exactly one connection. */
1321 tooltip << string_compose (_("<b>INPUT</b> to %1"),
1322 Gtkmm2ext::markup_escape_text (_route->name()));
1324 tooltip << string_compose (_("<b>OUTPUT</b> from %1"),
1325 Gtkmm2ext::markup_escape_text (_route->name()));
1328 string arrow = Gtkmm2ext::markup_escape_text(for_input ? " <- " : " -> ");
1329 vector<string> port_connections;
1330 for (PortSet::iterator port = io->ports().begin();
1331 port != io->ports().end();
1333 port_connections.clear();
1334 port->get_connections(port_connections);
1336 uint32_t port_connection_count = 0;
1338 for (vector<string>::iterator i = port_connections.begin();
1339 i != port_connections.end();
1341 ++port_connection_count;
1343 if (port_connection_count == 1) {
1344 tooltip << endl << Gtkmm2ext::markup_escape_text (
1345 port->name().substr(port->name().find("/") + 1));
1351 tooltip << Gtkmm2ext::markup_escape_text(*i);
1354 total_connection_count += port_connection_count;
1355 if (port->type() == dt) {
1356 typed_connection_count += port_connection_count;
1357 each_typed_port_has_one_connection &= (port_connection_count == 1);
1362 if (total_connection_count == 0) {
1363 tooltip << endl << _("Disconnected");
1366 if (typed_connection_count == 0) {
1371 /* Are all main-typed channels connected to the same route ? */
1373 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1374 for (ARDOUR::RouteList::const_iterator route = routes->begin();
1375 route != routes->end();
1377 boost::shared_ptr<IO> dest_io =
1378 for_input ? (*route)->output() : (*route)->input();
1379 if (io->bundle()->connected_to(dest_io->bundle(),
1382 label << Gtkmm2ext::markup_escape_text ((*route)->name());
1389 /* Are all main-typed channels connected to the same (user) bundle ? */
1391 boost::shared_ptr<ARDOUR::BundleList> bundles = _session->bundles ();
1392 for (ARDOUR::BundleList::iterator bundle = bundles->begin();
1393 bundle != bundles->end();
1395 if (boost::dynamic_pointer_cast<UserBundle> (*bundle) == 0)
1397 if (io->bundle()->connected_to(*bundle, _session->engine(),
1399 label << Gtkmm2ext::markup_escape_text ((*bundle)->name());
1406 /* Is each main-typed channel only connected to a physical output ? */
1407 if (!have_label && each_typed_port_has_one_connection) {
1408 ostringstream temp_label;
1409 vector<string> phys;
1410 string playorcapture;
1412 _session->engine().get_physical_inputs(dt, phys);
1413 playorcapture = "capture_";
1415 _session->engine().get_physical_outputs(dt, phys);
1416 playorcapture = "playback_";
1418 for (PortSet::iterator port = io->ports().begin(dt);
1419 port != io->ports().end(dt);
1422 for (vector<string>::iterator s = phys.begin();
1425 if (!port->connected_to(*s))
1427 pn = AudioEngine::instance()->get_pretty_name_by_name(*s);
1429 string::size_type start = (*s).find(playorcapture);
1430 if (start != string::npos) {
1431 pn = (*s).substr(start + playorcapture.size());
1437 temp_label.str(""); /* erase the failed attempt */
1440 if (port != io->ports().begin(dt))
1445 if (!temp_label.str().empty()) {
1446 label << temp_label.str();
1451 /* Is each main-typed channel connected to a single and different port with
1452 * the same client name (e.g. another JACK client) ? */
1453 if (!have_label && each_typed_port_has_one_connection) {
1454 string maybe_client = "";
1455 vector<string> connections;
1456 for (PortSet::iterator port = io->ports().begin(dt);
1457 port != io->ports().end(dt);
1459 port_connections.clear();
1460 port->get_connections(port_connections);
1461 string connection = port_connections.front();
1463 vector<string>::iterator i = connections.begin();
1464 while (i != connections.end() && *i != connection) {
1467 if (i != connections.end())
1468 break; /* duplicate connection */
1469 connections.push_back(connection);
1471 connection = connection.substr(0, connection.find(":"));
1472 if (maybe_client.empty())
1473 maybe_client = connection;
1474 if (maybe_client != connection)
1477 if (connections.size() == io->n_ports().n(dt)) {
1478 label << maybe_client;
1483 /* Odd configuration */
1485 label << "*" << total_connection_count << "*";
1488 if (total_connection_count > typed_connection_count) {
1489 label << "\u2295"; /* circled plus */
1492 /* Actually set the properties of the button */
1493 char * cstr = new char[tooltip.str().size() + 1];
1494 strcpy(cstr, tooltip.str().c_str());
1497 input_button.set_text (label.str());
1498 set_tooltip (&input_button, cstr);
1500 output_button.set_text (label.str());
1501 set_tooltip (&output_button, cstr);
1508 MixerStrip::update_input_display ()
1510 update_io_button (true);
1511 panners.setup_pan ();
1513 if (has_audio_outputs ()) {
1514 panners.show_all ();
1516 panners.hide_all ();
1522 MixerStrip::update_output_display ()
1524 update_io_button (false);
1525 gpm.setup_meters ();
1526 panners.setup_pan ();
1528 if (has_audio_outputs ()) {
1529 panners.show_all ();
1531 panners.hide_all ();
1536 MixerStrip::fast_update ()
1538 gpm.update_meters ();
1542 MixerStrip::diskstream_changed ()
1544 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1548 MixerStrip::io_changed_proxy ()
1550 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1551 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_trim_control));
1555 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1557 boost::shared_ptr<Port> a = wa.lock ();
1558 boost::shared_ptr<Port> b = wb.lock ();
1560 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1561 update_input_display ();
1562 set_width_enum (_width, this);
1565 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1566 update_output_display ();
1567 set_width_enum (_width, this);
1572 MixerStrip::setup_comment_button ()
1574 std::string comment = _route->comment();
1576 set_tooltip (_comment_button, comment.empty() ? _("Click to add/edit comments") : _route->comment());
1578 if (comment.empty ()) {
1579 _comment_button.set_name ("generic button");
1580 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1584 _comment_button.set_name ("comment button");
1586 string::size_type pos = comment.find_first_of (" \t\n");
1587 if (pos != string::npos) {
1588 comment = comment.substr (0, pos);
1590 if (comment.empty()) {
1591 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1593 _comment_button.set_text (comment);
1598 MixerStrip::select_route_group (GdkEventButton *ev)
1600 using namespace Menu_Helpers;
1602 if (ev->button == 1) {
1604 if (group_menu == 0) {
1606 PropertyList* plist = new PropertyList();
1608 plist->add (Properties::group_gain, true);
1609 plist->add (Properties::group_mute, true);
1610 plist->add (Properties::group_solo, true);
1612 group_menu = new RouteGroupMenu (_session, plist);
1616 r.push_back (route ());
1617 group_menu->build (r);
1619 RouteGroup *rg = _route->route_group();
1621 Gtkmm2ext::anchored_menu_popup(group_menu->menu(), &group_button,
1622 rg ? rg->name() : _("No Group"),
1630 MixerStrip::route_group_changed ()
1632 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1634 RouteGroup *rg = _route->route_group();
1637 group_button.set_text (PBD::short_version (rg->name(), 5));
1641 group_button.set_text (_("Grp"));
1644 group_button.set_text (_("~G"));
1651 MixerStrip::route_color_changed ()
1653 using namespace ARDOUR_UI_UTILS;
1654 name_button.modify_bg (STATE_NORMAL, color());
1655 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1656 reset_strip_style ();
1660 MixerStrip::show_passthru_color ()
1662 reset_strip_style ();
1667 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1669 boost::shared_ptr<Processor> processor (p.lock ());
1670 if (!processor || !processor->display_to_user()) {
1673 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
1675 if (pi && pi->is_channelstrip ()) {
1680 ++_plugin_insert_cnt;
1684 MixerStrip::build_route_ops_menu ()
1686 using namespace Menu_Helpers;
1687 route_ops_menu = new Menu;
1688 route_ops_menu->set_name ("ArdourContextMenu");
1690 MenuList& items = route_ops_menu->items();
1692 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1694 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1696 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1698 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1700 if (!Profile->get_mixbus()) {
1701 items.push_back (SeparatorElem());
1704 if (!_route->is_master()
1706 && !_route->mixbus()
1709 if (Profile->get_mixbus()) {
1710 items.push_back (SeparatorElem());
1712 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1715 if (!Profile->get_mixbus()) {
1716 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1717 /* do not allow rename if the track is record-enabled */
1718 items.back().set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1721 items.push_back (SeparatorElem());
1722 items.push_back (CheckMenuElem (_("Active")));
1723 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1724 i->set_active (_route->active());
1725 i->set_sensitive(! _session->transport_rolling());
1726 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1728 if (!Profile->get_mixbus ()) {
1729 items.push_back (SeparatorElem());
1730 items.push_back (CheckMenuElem (_("Strict I/O")));
1731 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1732 i->set_active (_route->strict_io());
1733 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1737 items.push_back (SeparatorElem());
1739 Gtk::Menu* dio_menu = new Menu;
1740 MenuList& dio_items = dio_menu->items();
1741 dio_items.push_back (MenuElem (_("Record Pre-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPreFader)));
1742 dio_items.push_back (MenuElem (_("Record Post-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPostFader)));
1743 dio_items.push_back (MenuElem (_("Custom Record+Playback Positions"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOCustom)));
1745 items.push_back (MenuElem (_("Disk I/O..."), *dio_menu));
1748 _plugin_insert_cnt = 0;
1749 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1750 if (_plugin_insert_cnt > 0) {
1751 items.push_back (SeparatorElem());
1752 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1755 if (boost::dynamic_pointer_cast<MidiTrack>(_route) || _route->the_instrument ()) {
1756 items.push_back (MenuElem (_("Patch Selector..."),
1757 sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
1760 if (_route->the_instrument () && _route->the_instrument ()->output_streams().n_audio() > 2) {
1761 // TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
1762 items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
1763 items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
1766 items.push_back (SeparatorElem());
1767 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1769 items.push_back (SeparatorElem());
1770 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1771 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1772 denormal_menu_item->set_active (_route->denormal_protection());
1775 /* note that this relies on selection being shared across editor and
1776 mixer (or global to the backend, in the future), which is the only
1777 sane thing for users anyway.
1780 StripableTimeAxisView* stav = PublicEditor::instance().get_stripable_time_axis_by_id (_route->id());
1782 Selection& selection (PublicEditor::instance().get_selection());
1783 if (!selection.selected (stav)) {
1784 selection.set (stav);
1787 if (!_route->is_master()) {
1788 items.push_back (SeparatorElem());
1789 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1792 items.push_back (SeparatorElem());
1793 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1799 MixerStrip::name_button_button_press (GdkEventButton* ev)
1801 if (ev->button == 1 || ev->button == 3) {
1802 list_route_operations ();
1804 if (ev->button == 1) {
1805 Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
1808 route_ops_menu->popup (3, ev->time);
1818 MixerStrip::number_button_button_press (GdkEventButton* ev)
1820 if ( ev->button == 3 ) {
1821 list_route_operations ();
1823 route_ops_menu->popup (1, ev->time);
1832 MixerStrip::list_route_operations ()
1834 delete route_ops_menu;
1835 build_route_ops_menu ();
1839 MixerStrip::set_selected (bool yn)
1841 AxisView::set_selected (yn);
1844 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1845 global_frame.set_name ("MixerStripSelectedFrame");
1847 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1848 global_frame.set_name ("MixerStripFrame");
1851 global_frame.queue_draw ();
1854 // processor_box.deselect_all_processors();
1858 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1860 if (what_changed.contains (ARDOUR::Properties::name)) {
1866 MixerStrip::name_changed ()
1870 name_button.set_text (_route->name());
1873 name_button.set_text (PBD::short_version (_route->name(), 5));
1877 set_tooltip (name_button, Gtkmm2ext::markup_escape_text(_route->name()));
1879 if (_session->config.get_track_name_number()) {
1880 const int64_t track_number = _route->track_number ();
1881 if (track_number == 0) {
1882 number_label.set_text ("-");
1884 number_label.set_text (PBD::to_string (abs(_route->track_number ())));
1887 number_label.set_text ("");
1892 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1894 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1898 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1900 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1904 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1906 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1910 MixerStrip::comment_button_resized (Gtk::Allocation& alloc)
1912 _comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1916 MixerStrip::width_button_pressed (GdkEventButton* ev)
1918 if (ev->button != 1) {
1922 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1925 _mixer.set_strip_width (Narrow, true);
1929 _mixer.set_strip_width (Wide, true);
1935 set_width_enum (Narrow, this);
1938 set_width_enum (Wide, this);
1947 MixerStrip::hide_clicked ()
1949 // LAME fix to reset the button status for when it is redisplayed (part 1)
1950 hide_button.set_sensitive(false);
1953 Hiding(); /* EMIT_SIGNAL */
1955 _mixer.hide_strip (this);
1959 hide_button.set_sensitive(true);
1963 MixerStrip::set_embedded (bool yn)
1969 MixerStrip::map_frozen ()
1971 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1973 boost::shared_ptr<AudioTrack> at = audio_track();
1976 switch (at->freeze_state()) {
1977 case AudioTrack::Frozen:
1978 processor_box.set_sensitive (false);
1979 hide_redirect_editors ();
1982 processor_box.set_sensitive (true);
1983 // XXX need some way, maybe, to retoggle redirect editors
1987 processor_box.set_sensitive (true);
1989 RouteUI::map_frozen ();
1993 MixerStrip::hide_redirect_editors ()
1995 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1999 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
2001 boost::shared_ptr<Processor> processor (p.lock ());
2006 Gtk::Window* w = processor_box.get_processor_ui (processor);
2014 MixerStrip::reset_strip_style ()
2016 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2018 gpm.set_fader_name ("SendStripBase");
2022 if (is_midi_track()) {
2023 if (_route->active()) {
2024 set_name ("MidiTrackStripBase");
2026 set_name ("MidiTrackStripBaseInactive");
2028 gpm.set_fader_name ("MidiTrackFader");
2029 } else if (is_audio_track()) {
2030 if (_route->active()) {
2031 set_name ("AudioTrackStripBase");
2033 set_name ("AudioTrackStripBaseInactive");
2035 gpm.set_fader_name ("AudioTrackFader");
2037 if (_route->active()) {
2038 set_name ("AudioBusStripBase");
2040 set_name ("AudioBusStripBaseInactive");
2042 gpm.set_fader_name ("AudioBusFader");
2044 /* (no MIDI busses yet) */
2051 MixerStrip::meter_point_string (MeterPoint mp)
2064 case MeterPostFader:
2081 return S_("Meter|In");
2085 return S_("Meter|Pr");
2088 case MeterPostFader:
2089 return S_("Meter|Po");
2093 return S_("Meter|O");
2098 return S_("Meter|C");
2107 /** Called when the monitor-section state */
2109 MixerStrip::monitor_changed ()
2111 assert (monitor_section_button);
2112 if (_session->monitor_active()) {
2113 monitor_section_button->set_name ("master monitor section button active");
2115 monitor_section_button->set_name ("master monitor section button normal");
2120 MixerStrip::monitor_section_added_or_removed ()
2122 assert (monitor_section_button);
2123 if (mute_button->get_parent()) {
2124 mute_button->get_parent()->remove(*mute_button);
2126 if (monitor_section_button->get_parent()) {
2127 monitor_section_button->get_parent()->remove(*monitor_section_button);
2129 if (_session && _session->monitor_out ()) {
2130 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2131 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2132 mute_button->show();
2133 monitor_section_button->show();
2135 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2136 mute_button->show();
2140 /** Called when the metering point has changed */
2142 MixerStrip::meter_changed ()
2144 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2145 gpm.setup_meters ();
2146 // reset peak when meter point changes
2147 gpm.reset_peak_display();
2150 /** The bus that we are displaying sends to has changed, or been turned off.
2151 * @param send_to New bus that we are displaying sends to, or 0.
2154 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2156 RouteUI::bus_send_display_changed (send_to);
2159 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2164 revert_to_default_display ();
2167 revert_to_default_display ();
2172 MixerStrip::drop_send ()
2174 boost::shared_ptr<Send> current_send;
2176 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2177 current_send->set_metering (false);
2180 send_gone_connection.disconnect ();
2181 input_button.set_sensitive (true);
2182 output_button.set_sensitive (true);
2183 group_button.set_sensitive (true);
2184 set_invert_sensitive (true);
2185 gpm.meter_point_button.set_sensitive (true);
2186 mute_button->set_sensitive (true);
2187 solo_button->set_sensitive (true);
2188 solo_isolated_led->set_sensitive (true);
2189 solo_safe_led->set_sensitive (true);
2190 monitor_input_button->set_sensitive (true);
2191 monitor_disk_button->set_sensitive (true);
2192 _comment_button.set_sensitive (true);
2193 trim_control.set_sensitive (true);
2194 if (midi_input_enable_button) {
2195 midi_input_enable_button->set_sensitive (true);
2197 control_slave_ui.set_sensitive (true);
2198 RouteUI::check_rec_enable_sensitivity ();
2199 set_button_names (); // update solo button visual state
2203 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2205 _current_delivery = d;
2206 DeliveryChanged (_current_delivery);
2210 MixerStrip::show_send (boost::shared_ptr<Send> send)
2216 set_current_delivery (send);
2218 send->meter()->set_meter_type (_route->meter_type ());
2219 send->set_metering (true);
2220 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2222 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2223 gain_meter().setup_meters ();
2225 uint32_t const in = _current_delivery->pans_required();
2226 uint32_t const out = _current_delivery->pan_outs();
2228 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2229 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2230 panner_ui().setup_pan ();
2231 panner_ui().set_send_drawing_mode (true);
2232 panner_ui().show_all ();
2234 input_button.set_sensitive (false);
2235 group_button.set_sensitive (false);
2236 set_invert_sensitive (false);
2237 gpm.meter_point_button.set_sensitive (false);
2238 mute_button->set_sensitive (false);
2239 solo_button->set_sensitive (false);
2240 rec_enable_button->set_sensitive (false);
2241 solo_isolated_led->set_sensitive (false);
2242 solo_safe_led->set_sensitive (false);
2243 monitor_input_button->set_sensitive (false);
2244 monitor_disk_button->set_sensitive (false);
2245 _comment_button.set_sensitive (false);
2246 trim_control.set_sensitive (false);
2247 if (midi_input_enable_button) {
2248 midi_input_enable_button->set_sensitive (false);
2250 control_slave_ui.set_sensitive (false);
2252 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2253 output_button.set_sensitive (false);
2256 reset_strip_style ();
2260 MixerStrip::revert_to_default_display ()
2264 set_current_delivery (_route->main_outs ());
2266 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2267 gain_meter().setup_meters ();
2269 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2270 update_panner_choices();
2271 panner_ui().setup_pan ();
2272 panner_ui().set_send_drawing_mode (false);
2274 if (has_audio_outputs ()) {
2275 panners.show_all ();
2277 panners.hide_all ();
2280 reset_strip_style ();
2284 MixerStrip::set_button_names ()
2288 mute_button->set_text (_("Mute"));
2289 monitor_input_button->set_text (_("In"));
2290 monitor_disk_button->set_text (_("Disk"));
2291 if (monitor_section_button) {
2292 monitor_section_button->set_text (_("Mon"));
2295 if (_route && _route->solo_safe_control()->solo_safe()) {
2296 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2298 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2300 if (!Config->get_solo_control_is_listen_control()) {
2301 solo_button->set_text (_("Solo"));
2303 switch (Config->get_listen_position()) {
2304 case AfterFaderListen:
2305 solo_button->set_text (_("AFL"));
2307 case PreFaderListen:
2308 solo_button->set_text (_("PFL"));
2312 solo_isolated_led->set_text (_("Iso"));
2313 solo_safe_led->set_text (S_("SoloLock|Lock"));
2317 mute_button->set_text (S_("Mute|M"));
2318 monitor_input_button->set_text (S_("MonitorInput|I"));
2319 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2320 if (monitor_section_button) {
2321 monitor_section_button->set_text (S_("Mon|O"));
2324 if (_route && _route->solo_safe_control()->solo_safe()) {
2325 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2327 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2329 if (!Config->get_solo_control_is_listen_control()) {
2330 solo_button->set_text (S_("Solo|S"));
2332 switch (Config->get_listen_position()) {
2333 case AfterFaderListen:
2334 solo_button->set_text (S_("AfterFader|A"));
2336 case PreFaderListen:
2337 solo_button->set_text (S_("Prefader|P"));
2342 solo_isolated_led->set_text (S_("SoloIso|I"));
2343 solo_safe_led->set_text (S_("SoloLock|L"));
2348 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2350 gpm.meter_point_button.set_text ("");
2355 MixerStrip::plugin_selector()
2357 return _mixer.plugin_selector();
2361 MixerStrip::hide_things ()
2363 processor_box.hide_things ();
2367 MixerStrip::input_active_button_press (GdkEventButton*)
2369 /* nothing happens on press */
2374 MixerStrip::input_active_button_release (GdkEventButton* ev)
2376 boost::shared_ptr<MidiTrack> mt = midi_track ();
2382 boost::shared_ptr<RouteList> rl (new RouteList);
2384 rl->push_back (route());
2386 _session->set_exclusive_input_active (rl, !mt->input_active(),
2387 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2393 MixerStrip::midi_input_status_changed ()
2395 if (midi_input_enable_button) {
2396 boost::shared_ptr<MidiTrack> mt = midi_track ();
2398 midi_input_enable_button->set_active (mt->input_active ());
2403 MixerStrip::state_id () const
2405 return string_compose ("strip %1", _route->id().to_s());
2409 MixerStrip::parameter_changed (string p)
2411 if (p == _visibility.get_state_name()) {
2412 /* The user has made changes to the mixer strip visibility, so get
2413 our VisibilityGroup to reflect these changes in our widgets.
2415 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2416 } else if (p == "track-name-number") {
2418 update_track_number_visibility();
2422 /** Called to decide whether the solo isolate / solo lock button visibility should
2423 * be overridden from that configured by the user. We do this for the master bus.
2425 * @return optional value that is present if visibility state should be overridden.
2427 boost::optional<bool>
2428 MixerStrip::override_solo_visibility () const
2430 if (_route && _route->is_master ()) {
2431 return boost::optional<bool> (false);
2434 return boost::optional<bool> ();
2438 MixerStrip::add_input_port (DataType t)
2440 _route->input()->add_port ("", this, t);
2444 MixerStrip::add_output_port (DataType t)
2446 _route->output()->add_port ("", this, t);
2450 MixerStrip::route_active_changed ()
2452 reset_strip_style ();
2456 MixerStrip::copy_processors ()
2458 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2462 MixerStrip::cut_processors ()
2464 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2468 MixerStrip::paste_processors ()
2470 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2474 MixerStrip::select_all_processors ()
2476 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2480 MixerStrip::deselect_all_processors ()
2482 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2486 MixerStrip::delete_processors ()
2488 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2492 MixerStrip::toggle_processors ()
2494 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2498 MixerStrip::ab_plugins ()
2500 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2504 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2506 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2509 if (ev->button == 3) {
2510 popup_level_meter_menu (ev);
2518 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2520 using namespace Gtk::Menu_Helpers;
2522 Gtk::Menu* m = ARDOUR_UI_UTILS::shared_popup_menu ();
2523 MenuList& items = m->items ();
2525 RadioMenuItem::Group group;
2527 PBD::Unwinder<bool> uw (_suspend_menu_callbacks, true);
2528 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2529 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2530 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2531 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2532 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2534 if (gpm.meter_channels().n_audio() == 0) {
2535 m->popup (ev->button, ev->time);
2539 RadioMenuItem::Group tgroup;
2540 items.push_back (SeparatorElem());
2542 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2543 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2544 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2545 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2546 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2547 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2548 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2549 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2550 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2551 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2552 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2555 if (_route->is_master()) {
2558 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2559 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2560 /* non-master bus */
2563 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2570 MeterType cmt = _route->meter_type();
2571 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2573 items.push_back (SeparatorElem());
2574 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2575 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2576 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2577 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2578 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2579 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2581 m->popup (ev->button, ev->time);
2585 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2586 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2588 using namespace Menu_Helpers;
2590 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2591 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2592 i->set_active (_route->meter_point() == point);
2596 MixerStrip::set_meter_point (MeterPoint p)
2598 if (_suspend_menu_callbacks) return;
2599 _route->set_meter_point (p);
2603 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2604 RadioMenuItem::Group& group, string const & name, MeterType type)
2606 using namespace Menu_Helpers;
2608 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2609 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2610 i->set_active (_route->meter_type() == type);
2614 MixerStrip::set_meter_type (MeterType t)
2616 if (_suspend_menu_callbacks) return;
2617 _route->set_meter_type (t);
2621 MixerStrip::update_track_number_visibility ()
2623 DisplaySuspender ds;
2624 bool show_label = _session->config.get_track_name_number();
2626 if (_route && _route->is_master()) {
2631 number_label.show ();
2632 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2633 // except the width of the number label is subtracted from the name-hbox, so we
2634 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2635 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2637 number_label.set_size_request(tnw, -1);
2638 number_label.show ();
2640 number_label.hide ();
2645 MixerStrip::color () const
2647 return route_color ();
2651 MixerStrip::marked_for_display () const
2653 return !_route->presentation_info().hidden();
2657 MixerStrip::set_marked_for_display (bool yn)
2659 return RouteUI::mark_hidden (!yn);
2663 MixerStrip::hide_master_spacer (bool yn)
2665 if (_mixer_owned && route()->is_master() && !yn) {