2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <sigc++/bind.h>
25 #include <gtkmm/messagedialog.h>
27 #include "pbd/convert.h"
28 #include "pbd/enumwriter.h"
29 #include "pbd/replace_all.h"
30 #include "pbd/stacktrace.h"
32 #include "ardour/amp.h"
33 #include "ardour/audio_track.h"
34 #include "ardour/audioengine.h"
35 #include "ardour/internal_send.h"
36 #include "ardour/io.h"
37 #include "ardour/meter.h"
38 #include "ardour/midi_track.h"
39 #include "ardour/pannable.h"
40 #include "ardour/panner.h"
41 #include "ardour/panner_shell.h"
42 #include "ardour/panner_manager.h"
43 #include "ardour/port.h"
44 #include "ardour/profile.h"
45 #include "ardour/route.h"
46 #include "ardour/route_group.h"
47 #include "ardour/send.h"
48 #include "ardour/session.h"
49 #include "ardour/types.h"
50 #include "ardour/user_bundle.h"
51 #include "ardour/vca.h"
52 #include "ardour/vca_manager.h"
54 #include "gtkmm2ext/gtk_ui.h"
55 #include "gtkmm2ext/menu_elems.h"
56 #include "gtkmm2ext/utils.h"
57 #include "gtkmm2ext/doi.h"
59 #include "widgets/tooltips.h"
61 #include "ardour_window.h"
62 #include "enums_convert.h"
63 #include "mixer_strip.h"
66 #include "public_editor.h"
68 #include "io_selector.h"
70 #include "gui_thread.h"
71 #include "route_group_menu.h"
72 #include "meter_patterns.h"
73 #include "ui_config.h"
77 using namespace ARDOUR;
78 using namespace ArdourWidgets;
81 using namespace Gtkmm2ext;
83 using namespace ArdourMeter;
85 MixerStrip* MixerStrip::_entered_mixer_strip;
86 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
88 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
89 : SessionHandlePtr (sess)
92 , _mixer_owned (in_mixer)
93 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
96 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
97 , rec_mon_table (2, 2)
98 , solo_iso_table (1, 2)
99 , mute_solo_table (1, 2)
100 , bottom_button_table (1, 3)
101 , monitor_section_button (0)
102 , midi_input_enable_button (0)
103 , _plugin_insert_cnt (0)
104 , _comment_button (_("Comments"))
105 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
106 , _visibility (X_("mixer-element-visibility"))
107 , control_slave_ui (sess)
112 /* the editor mixer strip: don't destroy it every time
113 the underlying route goes away.
116 self_destruct = false;
120 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
121 : SessionHandlePtr (sess)
124 , _mixer_owned (in_mixer)
125 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
128 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
129 , rec_mon_table (2, 2)
130 , solo_iso_table (1, 2)
131 , mute_solo_table (1, 2)
132 , bottom_button_table (1, 3)
133 , monitor_section_button (0)
134 , midi_input_enable_button (0)
135 , _plugin_insert_cnt (0)
136 , _comment_button (_("Comments"))
137 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
138 , _visibility (X_("mixer-element-visibility"))
139 , control_slave_ui (sess)
148 _entered_mixer_strip= 0;
151 ignore_comment_edit = false;
152 ignore_toggle = false;
156 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
157 longest_label = "longest label";
159 string t = _("Click to toggle the width of this mixer strip.");
161 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
164 width_button.set_icon (ArdourIcon::StripWidth);
165 hide_button.set_tweaks (ArdourButton::Square);
166 set_tooltip (width_button, t);
168 hide_button.set_icon (ArdourIcon::CloseCross);
169 hide_button.set_tweaks (ArdourButton::Square);
170 set_tooltip (&hide_button, _("Hide this mixer strip"));
172 input_button_box.set_spacing(2);
174 input_button.set_text (_("Input"));
175 input_button.set_name ("mixer strip button");
176 input_button_box.pack_start (input_button, true, true);
178 output_button.set_text (_("Output"));
179 output_button.set_name ("mixer strip button");
181 bottom_button_table.attach (gpm.meter_point_button, 2, 3, 0, 1);
183 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
185 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
186 solo_isolated_led->show ();
187 solo_isolated_led->set_no_show_all (true);
188 solo_isolated_led->set_name (X_("solo isolate"));
189 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
190 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
191 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
193 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
194 solo_safe_led->show ();
195 solo_safe_led->set_no_show_all (true);
196 solo_safe_led->set_name (X_("solo safe"));
197 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
198 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
199 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
201 solo_safe_led->set_text (S_("SoloLock|Lock"));
202 solo_isolated_led->set_text (_("Iso"));
204 solo_iso_table.set_homogeneous (true);
205 solo_iso_table.set_spacings (2);
206 if (!ARDOUR::Profile->get_trx()) {
207 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
208 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
210 solo_iso_table.show ();
212 rec_mon_table.set_homogeneous (true);
213 rec_mon_table.set_row_spacings (2);
214 rec_mon_table.set_col_spacings (2);
215 if (ARDOUR::Profile->get_mixbus()) {
216 rec_mon_table.resize (1, 3);
217 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
218 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
219 } else if (!ARDOUR::Profile->get_trx()) {
220 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
221 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
223 rec_mon_table.show ();
225 if (solo_isolated_led) {
226 button_size_group->add_widget (*solo_isolated_led);
229 button_size_group->add_widget (*solo_safe_led);
232 if (!ARDOUR::Profile->get_mixbus()) {
233 if (rec_enable_button) {
234 button_size_group->add_widget (*rec_enable_button);
236 if (monitor_disk_button) {
237 button_size_group->add_widget (*monitor_disk_button);
239 if (monitor_input_button) {
240 button_size_group->add_widget (*monitor_input_button);
244 mute_solo_table.set_homogeneous (true);
245 mute_solo_table.set_spacings (2);
247 bottom_button_table.set_spacings (2);
248 bottom_button_table.set_homogeneous (true);
249 bottom_button_table.attach (group_button, 1, 2, 0, 1);
250 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
252 name_button.set_name ("mixer strip button");
253 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
254 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
256 set_tooltip (&group_button, _("Mix group"));
257 group_button.set_name ("mixer strip button");
259 _comment_button.set_name (X_("mixer strip button"));
260 _comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
261 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
262 _comment_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::comment_button_resized));
264 // TODO implement ArdourKnob::on_size_request properly
265 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
266 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
268 trim_control.set_tooltip_prefix (_("Trim: "));
269 trim_control.set_name ("trim knob");
270 trim_control.set_no_show_all (true);
271 trim_control.StartGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_start_touch));
272 trim_control.StopGesture.connect(sigc::mem_fun(*this, &MixerStrip::trim_end_touch));
273 input_button_box.pack_start (trim_control, false, false);
275 global_vpacker.set_border_width (1);
276 global_vpacker.set_spacing (0);
278 width_button.set_name ("mixer strip button");
279 hide_button.set_name ("mixer strip button");
281 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
282 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
284 width_hide_box.set_spacing (2);
285 width_hide_box.pack_start (width_button, false, true);
286 width_hide_box.pack_start (number_label, true, true);
287 width_hide_box.pack_end (hide_button, false, true);
289 number_label.set_text ("-");
290 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
291 number_label.set_no_show_all ();
292 number_label.set_name ("tracknumber label");
293 number_label.set_fixed_colors (0x80808080, 0x80808080);
294 number_label.set_alignment (.5, .5);
295 number_label.set_fallthrough_to_parent (true);
296 number_label.set_tweaks (ArdourButton::OccasionalText);
298 global_vpacker.set_spacing (2);
299 if (!ARDOUR::Profile->get_trx()) {
300 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
301 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
302 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (processor_box, true, true);
306 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
308 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
311 global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
313 if (!ARDOUR::Profile->get_trx()) {
314 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
315 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
317 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
321 //add a spacer underneath the master bus;
322 //this fills the area that is taken up by the scrollbar on the tracks;
323 //and therefore keeps the faders "even" across the bottom
324 int scrollbar_height = 0;
326 Gtk::Window window (WINDOW_TOPLEVEL);
327 HScrollbar scrollbar;
328 window.add (scrollbar);
329 scrollbar.set_name ("MixerWindow");
330 scrollbar.ensure_style();
331 Gtk::Requisition requisition(scrollbar.size_request ());
332 scrollbar_height = requisition.height;
334 spacer.set_size_request (-1, scrollbar_height);
335 global_vpacker.pack_end (spacer, false, false);
338 global_frame.add (global_vpacker);
339 global_frame.set_shadow_type (Gtk::SHADOW_IN);
340 global_frame.set_name ("BaseFrame");
344 /* force setting of visible selected status */
347 set_selected (false);
352 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
353 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
355 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
356 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
357 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
359 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
360 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
362 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
363 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
364 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
366 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
368 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
370 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
374 /* start off as a passthru strip. we'll correct this, if necessary,
375 in update_diskstream_display().
378 /* start off as a passthru strip. we'll correct this, if necessary,
379 in update_diskstream_display().
382 if (is_midi_track()) {
383 set_name ("MidiTrackStripBase");
385 set_name ("AudioTrackStripBase");
388 add_events (Gdk::BUTTON_RELEASE_MASK|
389 Gdk::ENTER_NOTIFY_MASK|
390 Gdk::LEAVE_NOTIFY_MASK|
392 Gdk::KEY_RELEASE_MASK);
394 set_flags (get_flags() | Gtk::CAN_FOCUS);
396 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
397 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
400 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
401 must be the same as those used in RCOptionEditor so that the configuration changes
402 are recognised when they occur.
404 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
405 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
406 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
407 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
408 _visibility.add (&output_button, X_("Output"), _("Output"), false);
409 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
410 _visibility.add (&control_slave_ui, X_("VCA"), _("VCA Assigns"), false);
412 parameter_changed (X_("mixer-element-visibility"));
413 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
414 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
415 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
417 //watch for mouse enter/exit so we can do some stuff
418 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
419 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
421 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
424 MixerStrip::~MixerStrip ()
426 CatchDeletion (this);
428 if (this ==_entered_mixer_strip)
429 _entered_mixer_strip = NULL;
433 MixerStrip::vca_assign (boost::shared_ptr<ARDOUR::VCA> vca)
435 boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
441 MixerStrip::vca_unassign (boost::shared_ptr<ARDOUR::VCA> vca)
443 boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
449 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
451 _entered_mixer_strip = this;
453 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
454 //because the mixerstrip control is a parent that encompasses the strip
455 deselect_all_processors();
461 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
463 //if we have moved outside our strip, but not into a child view, then deselect ourselves
464 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
465 _entered_mixer_strip= 0;
467 //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
468 gpm.gain_display.set_sensitive(false);
470 gpm.gain_display.set_sensitive(true);
472 //if we leave this mixer strip we need to clear out any selections
473 //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
480 MixerStrip::name() const
483 return _route->name();
489 MixerStrip::update_trim_control ()
491 if (route()->trim() && route()->trim()->active() &&
492 route()->n_inputs().n_audio() > 0) {
493 trim_control.show ();
494 trim_control.set_controllable (route()->trim()->gain_control());
496 trim_control.hide ();
497 boost::shared_ptr<Controllable> none;
498 trim_control.set_controllable (none);
503 MixerStrip::trim_start_touch ()
505 assert (_route && _session);
506 if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
507 route()->trim()->gain_control ()->start_touch (_session->transport_sample());
512 MixerStrip::trim_end_touch ()
514 assert (_route && _session);
515 if (route()->trim() && route()->trim()->active() && route()->n_inputs().n_audio() > 0) {
516 route()->trim()->gain_control ()->stop_touch (_session->transport_sample());
521 MixerStrip::set_route (boost::shared_ptr<Route> rt)
523 //the rec/monitor stuff only shows up for tracks.
524 //the show_sends only shows up for buses.
525 //remove them all here, and we may add them back later
526 if (show_sends_button->get_parent()) {
527 rec_mon_table.remove (*show_sends_button);
529 if (rec_enable_button->get_parent()) {
530 rec_mon_table.remove (*rec_enable_button);
532 if (monitor_input_button->get_parent()) {
533 rec_mon_table.remove (*monitor_input_button);
535 if (monitor_disk_button->get_parent()) {
536 rec_mon_table.remove (*monitor_disk_button);
538 if (group_button.get_parent()) {
539 bottom_button_table.remove (group_button);
542 RouteUI::set_route (rt);
544 control_slave_ui.set_stripable (boost::dynamic_pointer_cast<Stripable> (rt));
546 /* ProcessorBox needs access to _route so that it can read
549 processor_box.set_route (rt);
551 revert_to_default_display ();
553 /* unpack these from the parent and stuff them into our own
557 if (gpm.peak_display.get_parent()) {
558 gpm.peak_display.get_parent()->remove (gpm.peak_display);
560 if (gpm.gain_display.get_parent()) {
561 gpm.gain_display.get_parent()->remove (gpm.gain_display);
564 gpm.set_type (rt->meter_type());
566 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
567 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
569 if (solo_button->get_parent()) {
570 mute_solo_table.remove (*solo_button);
573 if (mute_button->get_parent()) {
574 mute_solo_table.remove (*mute_button);
577 if (route()->is_master()) {
578 solo_button->hide ();
579 mute_button->show ();
580 rec_mon_table.hide ();
581 solo_iso_table.set_sensitive(false);
582 control_slave_ui.set_sensitive(false);
583 if (monitor_section_button == 0) {
584 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
585 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
587 monitor_section_button = manage (new ArdourButton);
589 monitor_section_button->set_related_action (act);
590 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
591 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
592 monitor_section_button->show();
593 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
595 parameter_changed ("use-monitor-bus");
597 bottom_button_table.attach (group_button, 1, 2, 0, 1);
598 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
599 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
600 mute_button->show ();
601 solo_button->show ();
602 rec_mon_table.show ();
603 solo_iso_table.set_sensitive(true);
604 control_slave_ui.set_sensitive(true);
607 if (_mixer_owned && route()->is_master() ) {
614 monitor_input_button->show ();
615 monitor_disk_button->show ();
617 monitor_input_button->hide();
618 monitor_disk_button->hide ();
621 update_trim_control();
623 if (is_midi_track()) {
624 if (midi_input_enable_button == 0) {
625 midi_input_enable_button = manage (new ArdourButton);
626 midi_input_enable_button->set_name ("midi input button");
627 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
628 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
629 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
630 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
631 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
633 input_button_box.remove (*midi_input_enable_button);
635 /* get current state */
636 midi_input_status_changed ();
637 input_button_box.pack_start (*midi_input_enable_button, false, false);
639 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
641 if (midi_input_enable_button) {
642 /* removal from the container will delete it */
643 input_button_box.remove (*midi_input_enable_button);
644 midi_input_enable_button = 0;
648 if (is_audio_track()) {
649 boost::shared_ptr<AudioTrack> at = audio_track();
650 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
655 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
656 rec_enable_button->show();
658 if (ARDOUR::Profile->get_mixbus()) {
659 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
660 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
661 } else if (ARDOUR::Profile->get_trx()) {
662 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
664 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
665 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
672 if (!_route->is_master()) {
673 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
674 show_sends_button->show();
678 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
680 delete route_ops_menu;
683 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
684 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
685 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
686 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
688 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
690 if (_route->panner_shell()) {
691 update_panner_choices();
692 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
695 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
697 set_stuff_from_route ();
699 /* now force an update of all the various elements */
701 update_mute_display ();
702 update_solo_display ();
705 route_group_changed ();
706 update_track_number_visibility ();
709 panners.setup_pan ();
711 if (has_audio_outputs ()) {
717 update_diskstream_display ();
718 update_input_display ();
719 update_output_display ();
721 add_events (Gdk::BUTTON_RELEASE_MASK);
723 processor_box.show ();
725 if (!route()->is_master() && !route()->is_monitor()) {
726 /* we don't allow master or control routes to be hidden */
731 gpm.reset_peak_display ();
732 gpm.gain_display.show ();
733 gpm.peak_display.show ();
736 width_hide_box.show();
738 global_vpacker.show();
739 mute_solo_table.show();
740 bottom_button_table.show();
742 gpm.meter_point_button.show();
743 input_button_box.show_all();
744 output_button.show();
746 _comment_button.show();
748 gpm.gain_automation_state_button.show();
750 parameter_changed ("mixer-element-visibility");
757 MixerStrip::set_stuff_from_route ()
759 /* if width is not set, it will be set by the MixerUI or editor */
762 if (get_gui_property ("strip-width", width)) {
763 set_width_enum (width, this);
768 MixerStrip::set_width_enum (Width w, void* owner)
770 /* always set the gpm width again, things may be hidden */
773 panners.set_width (w);
775 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
777 _width_owner = owner;
781 if (_width_owner == this) {
782 set_gui_property ("strip-width", _width);
787 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
792 if (show_sends_button) {
793 show_sends_button->set_text (_("Aux"));
796 gpm.gain_automation_state_button.set_text (
797 gpm.astate_string(gain_automation->automation_state()));
799 if (_route->panner()) {
800 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
801 panners.astate_string(_route->panner()->automation_state()));
805 // panners expect an even number of horiz. pixels
806 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
808 set_size_request (width, -1);
814 if (show_sends_button) {
815 show_sends_button->set_text (_("Snd"));
818 gpm.gain_automation_state_button.set_text (
819 gpm.short_astate_string(gain_automation->automation_state()));
820 gain_meter().setup_meters (); // recalc meter width
822 if (_route->panner()) {
823 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
824 panners.short_astate_string(_route->panner()->automation_state()));
828 // panners expect an even number of horiz. pixels
829 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
831 set_size_request (width, -1);
836 processor_box.set_width (w);
838 update_input_display ();
839 update_output_display ();
840 setup_comment_button ();
841 route_group_changed ();
847 MixerStrip::set_packed (bool yn)
850 set_gui_property ("visible", _packed);
854 struct RouteCompareByName {
855 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
856 return a->name().compare (b->name()) < 0;
861 MixerStrip::output_release (GdkEventButton *ev)
863 switch (ev->button) {
865 edit_output_configuration ();
873 MixerStrip::output_press (GdkEventButton *ev)
875 using namespace Menu_Helpers;
876 if (!_session->engine().connected()) {
877 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
882 MenuList& citems = output_menu.items();
883 switch (ev->button) {
886 return false; //wait for the mouse-up to pop the dialog
890 output_menu.set_name ("ArdourContextMenu");
892 output_menu_bundles.clear ();
894 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
896 citems.push_back (SeparatorElem());
897 uint32_t const n_with_separator = citems.size ();
899 ARDOUR::BundleList current = _route->output()->bundles_connected ();
901 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
903 /* guess the user-intended main type of the route output */
904 DataType intended_type = guess_main_type(false);
906 /* try adding the master bus first */
907 boost::shared_ptr<Route> master = _session->master_out();
909 maybe_add_bundle_to_output_menu (master->input()->bundle(), current, intended_type);
912 /* then other routes inputs */
913 RouteList copy = _session->get_routelist ();
914 copy.sort (RouteCompareByName ());
915 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
916 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current, intended_type);
919 /* then try adding user bundles, often labeled/grouped physical inputs */
920 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
921 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
922 maybe_add_bundle_to_output_menu (*i, current, intended_type);
926 /* then all other bundles, including physical outs or other sofware */
927 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
928 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
929 maybe_add_bundle_to_output_menu (*i, current, intended_type);
933 if (citems.size() == n_with_separator) {
934 /* no routes added; remove the separator */
938 if (!ARDOUR::Profile->get_mixbus()) {
939 citems.push_back (SeparatorElem());
941 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
944 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
945 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
951 citems.push_back (SeparatorElem());
952 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
954 Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
967 MixerStrip::input_release (GdkEventButton *ev)
969 switch (ev->button) {
972 edit_input_configuration ();
984 MixerStrip::input_press (GdkEventButton *ev)
986 using namespace Menu_Helpers;
988 MenuList& citems = input_menu.items();
989 input_menu.set_name ("ArdourContextMenu");
992 if (!_session->engine().connected()) {
993 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
998 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
1001 switch (ev->button) {
1004 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
1008 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
1010 citems.push_back (SeparatorElem());
1011 uint32_t const n_with_separator = citems.size ();
1013 input_menu_bundles.clear ();
1015 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1017 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1019 /* give user bundles first chance at being in the menu */
1021 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1022 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1023 maybe_add_bundle_to_input_menu (*i, current);
1027 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1028 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1029 maybe_add_bundle_to_input_menu (*i, current);
1033 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1034 RouteList copy = *routes;
1035 copy.sort (RouteCompareByName ());
1036 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1037 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1040 if (citems.size() == n_with_separator) {
1041 /* no routes added; remove the separator */
1045 citems.push_back (SeparatorElem());
1046 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1049 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1050 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1055 citems.push_back (SeparatorElem());
1056 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1058 Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
1070 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1072 if (ignore_toggle) {
1076 _route->input()->connect_ports_to_bundle (c, true, this);
1080 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1082 if (ignore_toggle) {
1086 _route->output()->connect_ports_to_bundle (c, true, true, this);
1090 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1092 using namespace Menu_Helpers;
1094 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1098 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1099 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1103 if (i != input_menu_bundles.end()) {
1107 input_menu_bundles.push_back (b);
1109 MenuList& citems = input_menu.items();
1110 citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1114 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/,
1117 using namespace Menu_Helpers;
1119 /* The bundle should be an input one, but not ours */
1120 if (b->ports_are_inputs() == false || *b == *_route->input()->bundle()) {
1124 /* Don't add the monitor input unless we are Master */
1125 boost::shared_ptr<Route> monitor = _session->monitor_out();
1126 if ((!_route->is_master()) && monitor && b->has_same_ports (monitor->input()->bundle()))
1129 /* It should either match exactly our outputs (if |type| is DataType::NIL)
1130 * or have the same number of |type| channels than our outputs. */
1131 if (type == DataType::NIL) {
1132 if(b->nchannels() != _route->n_outputs())
1135 if (b->nchannels().n(type) != _route->n_outputs().n(type))
1139 /* Avoid adding duplicates */
1140 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1141 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1144 if (i != output_menu_bundles.end()) {
1148 /* Now add the bundle to the menu */
1149 output_menu_bundles.push_back (b);
1151 MenuList& citems = output_menu.items();
1152 citems.push_back (MenuElemNoMnemonic (b->name (), sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1156 MixerStrip::update_diskstream_display ()
1158 if (is_track() && input_selector) {
1159 input_selector->hide_all ();
1162 route_color_changed ();
1166 MixerStrip::connect_to_pan ()
1168 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1170 panstate_connection.disconnect ();
1171 panstyle_connection.disconnect ();
1173 if (!_route->panner()) {
1177 boost::shared_ptr<Pannable> p = _route->pannable ();
1179 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1181 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1182 * However, that only works a panner was previously set.
1184 * PannerUI must remain subscribed to _panshell->Changed() in case
1185 * we switch the panner eg. AUX-Send and back
1186 * _route->panner_shell()->Changed() vs _panshell->Changed
1188 if (panners._panner == 0) {
1189 panners.panshell_changed ();
1191 update_panner_choices();
1195 MixerStrip::update_panner_choices ()
1197 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1198 if (!_route->panner_shell()) { return; }
1200 uint32_t in = _route->output()->n_ports().n_audio();
1202 if (_route->panner()) {
1203 in = _route->panner()->in().n_audio();
1206 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1210 MixerStrip::guess_main_type(bool for_input, bool favor_connected) const
1212 /* The heuristic follows these principles:
1213 * A) If all ports that the user connected are of the same type, then he
1214 * very probably intends to use the IO with that type. A common subcase
1215 * is when the IO has only ports of the same type (connected or not).
1216 * B) If several types of ports are connected, then we should guess based
1217 * on the likeliness of the user wanting to use a given type.
1218 * We assume that the DataTypes are ordered from the most likely to the
1219 * least likely when iterating or comparing them with "<".
1220 * C) If no port is connected, the same logic can be applied with all ports
1221 * instead of connected ones. TODO: Try other ideas, for instance look at
1222 * the last plugin output when |for_input| is false (note: when StrictIO
1223 * the outs of the last plugin should be the same as the outs of the route
1224 * modulo the panner which forwards non-audio anyway).
1225 * All of these constraints are respected by the following algorithm that
1226 * just returns the most likely datatype found in connected ports if any, or
1227 * available ports if any (since if all ports are of the same type, the most
1228 * likely found will be that one obviously). */
1230 boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
1232 /* Find most likely type among connected ports */
1233 if (favor_connected) {
1234 DataType type = DataType::NIL; /* NIL is always last so least likely */
1235 for (PortSet::iterator p = io->ports().begin(); p != io->ports().end(); ++p) {
1236 if (p->connected() && p->type() < type)
1239 if (type != DataType::NIL) {
1240 /* There has been a connected port (necessarily non-NIL) */
1245 /* Find most likely type among available ports.
1246 * The iterator stops before NIL. */
1247 for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
1248 if (io->n_ports().n(*t) > 0)
1252 /* No port at all, return the most likely datatype by default */
1253 return DataType::front();
1257 * Output port labelling
1259 * Case 1: Each output has one connection, all connections are to system:playback_%i
1260 * out 1 -> system:playback_1
1261 * out 2 -> system:playback_2
1262 * out 3 -> system:playback_3
1265 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1266 * out 1 -> ardour:track_x/in 1
1267 * out 2 -> ardour:track_x/in 2
1268 * Display as: track_x
1270 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1271 * out 1 -> program x:foo
1272 * out 2 -> program x:foo
1273 * Display as: program x
1275 * Case 4: No connections (Disconnected)
1278 * Default case (unusual routing):
1279 * Display as: *number of connections*
1284 * .-----------------------------------------------.
1286 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1287 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1288 * '-----------------------------------------------'
1289 * .-----------------------------------------------.
1292 * '-----------------------------------------------'
1296 MixerStrip::update_io_button (bool for_input)
1298 ostringstream tooltip;
1299 ostringstream label;
1300 bool have_label = false;
1302 uint32_t total_connection_count = 0;
1303 uint32_t typed_connection_count = 0;
1304 bool each_typed_port_has_one_connection = true;
1306 DataType dt = guess_main_type(for_input);
1307 boost::shared_ptr<IO> io = for_input ? _route->input() : _route->output();
1309 /* Fill in the tooltip. Also count:
1310 * - The total number of connections.
1311 * - The number of main-typed connections.
1312 * - Whether each main-typed port has exactly one connection. */
1314 tooltip << string_compose (_("<b>INPUT</b> to %1"),
1315 Gtkmm2ext::markup_escape_text (_route->name()));
1317 tooltip << string_compose (_("<b>OUTPUT</b> from %1"),
1318 Gtkmm2ext::markup_escape_text (_route->name()));
1321 string arrow = Gtkmm2ext::markup_escape_text(for_input ? " <- " : " -> ");
1322 vector<string> port_connections;
1323 for (PortSet::iterator port = io->ports().begin();
1324 port != io->ports().end();
1326 port_connections.clear();
1327 port->get_connections(port_connections);
1329 uint32_t port_connection_count = 0;
1331 for (vector<string>::iterator i = port_connections.begin();
1332 i != port_connections.end();
1334 ++port_connection_count;
1336 if (port_connection_count == 1) {
1337 tooltip << endl << Gtkmm2ext::markup_escape_text (
1338 port->name().substr(port->name().find("/") + 1));
1344 tooltip << Gtkmm2ext::markup_escape_text(*i);
1347 total_connection_count += port_connection_count;
1348 if (port->type() == dt) {
1349 typed_connection_count += port_connection_count;
1350 each_typed_port_has_one_connection &= (port_connection_count == 1);
1355 if (total_connection_count == 0) {
1356 tooltip << endl << _("Disconnected");
1359 if (typed_connection_count == 0) {
1364 /* Are all main-typed channels connected to the same route ? */
1366 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1367 for (ARDOUR::RouteList::const_iterator route = routes->begin();
1368 route != routes->end();
1370 boost::shared_ptr<IO> dest_io =
1371 for_input ? (*route)->output() : (*route)->input();
1372 if (io->bundle()->connected_to(dest_io->bundle(),
1375 label << Gtkmm2ext::markup_escape_text ((*route)->name());
1382 /* Are all main-typed channels connected to the same (user) bundle ? */
1384 boost::shared_ptr<ARDOUR::BundleList> bundles = _session->bundles ();
1385 for (ARDOUR::BundleList::iterator bundle = bundles->begin();
1386 bundle != bundles->end();
1388 if (boost::dynamic_pointer_cast<UserBundle> (*bundle) == 0)
1390 if (io->bundle()->connected_to(*bundle, _session->engine(),
1392 label << Gtkmm2ext::markup_escape_text ((*bundle)->name());
1399 /* Is each main-typed channel only connected to a physical output ? */
1400 if (!have_label && each_typed_port_has_one_connection) {
1401 ostringstream temp_label;
1402 vector<string> phys;
1403 string playorcapture;
1405 _session->engine().get_physical_inputs(dt, phys);
1406 playorcapture = "capture_";
1408 _session->engine().get_physical_outputs(dt, phys);
1409 playorcapture = "playback_";
1411 for (PortSet::iterator port = io->ports().begin(dt);
1412 port != io->ports().end(dt);
1415 for (vector<string>::iterator s = phys.begin();
1418 if (!port->connected_to(*s))
1420 pn = AudioEngine::instance()->get_pretty_name_by_name(*s);
1422 string::size_type start = (*s).find(playorcapture);
1423 if (start != string::npos) {
1424 pn = (*s).substr(start + playorcapture.size());
1430 temp_label.str(""); /* erase the failed attempt */
1433 if (port != io->ports().begin(dt))
1438 if (!temp_label.str().empty()) {
1439 label << temp_label.str();
1444 /* Is each main-typed channel connected to a single and different port with
1445 * the same client name (e.g. another JACK client) ? */
1446 if (!have_label && each_typed_port_has_one_connection) {
1447 string maybe_client = "";
1448 vector<string> connections;
1449 for (PortSet::iterator port = io->ports().begin(dt);
1450 port != io->ports().end(dt);
1452 port_connections.clear();
1453 port->get_connections(port_connections);
1454 string connection = port_connections.front();
1456 vector<string>::iterator i = connections.begin();
1457 while (i != connections.end() && *i != connection) {
1460 if (i != connections.end())
1461 break; /* duplicate connection */
1462 connections.push_back(connection);
1464 connection = connection.substr(0, connection.find(":"));
1465 if (maybe_client.empty())
1466 maybe_client = connection;
1467 if (maybe_client != connection)
1470 if (connections.size() == io->n_ports().n(dt)) {
1471 label << maybe_client;
1476 /* Odd configuration */
1478 label << "*" << total_connection_count << "*";
1481 if (total_connection_count > typed_connection_count) {
1482 label << "\u2295"; /* circled plus */
1485 /* Actually set the properties of the button */
1486 char * cstr = new char[tooltip.str().size() + 1];
1487 strcpy(cstr, tooltip.str().c_str());
1490 input_button.set_text (label.str());
1491 set_tooltip (&input_button, cstr);
1493 output_button.set_text (label.str());
1494 set_tooltip (&output_button, cstr);
1501 MixerStrip::update_input_display ()
1503 update_io_button (true);
1504 panners.setup_pan ();
1506 if (has_audio_outputs ()) {
1507 panners.show_all ();
1509 panners.hide_all ();
1515 MixerStrip::update_output_display ()
1517 update_io_button (false);
1518 gpm.setup_meters ();
1519 panners.setup_pan ();
1521 if (has_audio_outputs ()) {
1522 panners.show_all ();
1524 panners.hide_all ();
1529 MixerStrip::fast_update ()
1531 gpm.update_meters ();
1535 MixerStrip::diskstream_changed ()
1537 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1541 MixerStrip::io_changed_proxy ()
1543 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1544 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_trim_control));
1548 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1550 boost::shared_ptr<Port> a = wa.lock ();
1551 boost::shared_ptr<Port> b = wb.lock ();
1553 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1554 update_input_display ();
1555 set_width_enum (_width, this);
1558 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1559 update_output_display ();
1560 set_width_enum (_width, this);
1565 MixerStrip::setup_comment_button ()
1567 std::string comment = _route->comment();
1569 set_tooltip (_comment_button, comment.empty() ? _("Click to add/edit comments") : _route->comment());
1571 if (comment.empty ()) {
1572 _comment_button.set_name ("generic button");
1573 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1577 _comment_button.set_name ("comment button");
1579 string::size_type pos = comment.find_first_of (" \t\n");
1580 if (pos != string::npos) {
1581 comment = comment.substr (0, pos);
1583 if (comment.empty()) {
1584 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1586 _comment_button.set_text (comment);
1591 MixerStrip::select_route_group (GdkEventButton *ev)
1593 using namespace Menu_Helpers;
1595 if (ev->button == 1) {
1597 if (group_menu == 0) {
1599 PropertyList* plist = new PropertyList();
1601 plist->add (Properties::group_gain, true);
1602 plist->add (Properties::group_mute, true);
1603 plist->add (Properties::group_solo, true);
1605 group_menu = new RouteGroupMenu (_session, plist);
1609 r.push_back (route ());
1610 group_menu->build (r);
1612 RouteGroup *rg = _route->route_group();
1614 Gtkmm2ext::anchored_menu_popup(group_menu->menu(), &group_button,
1615 rg ? rg->name() : _("No Group"),
1623 MixerStrip::route_group_changed ()
1625 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1627 RouteGroup *rg = _route->route_group();
1630 group_button.set_text (PBD::short_version (rg->name(), 5));
1634 group_button.set_text (_("Grp"));
1637 group_button.set_text (_("~G"));
1644 MixerStrip::route_color_changed ()
1646 using namespace ARDOUR_UI_UTILS;
1647 name_button.modify_bg (STATE_NORMAL, color());
1648 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1649 reset_strip_style ();
1653 MixerStrip::show_passthru_color ()
1655 reset_strip_style ();
1660 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1662 boost::shared_ptr<Processor> processor (p.lock ());
1663 if (!processor || !processor->display_to_user()) {
1666 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
1668 if (pi && pi->is_channelstrip ()) {
1673 ++_plugin_insert_cnt;
1677 MixerStrip::build_route_ops_menu ()
1679 using namespace Menu_Helpers;
1680 route_ops_menu = new Menu;
1681 route_ops_menu->set_name ("ArdourContextMenu");
1683 MenuList& items = route_ops_menu->items();
1685 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1687 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1689 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1691 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1693 if (!Profile->get_mixbus()) {
1694 items.push_back (SeparatorElem());
1697 if (!_route->is_master()
1699 && !_route->mixbus()
1702 if (Profile->get_mixbus()) {
1703 items.push_back (SeparatorElem());
1705 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1708 if (!Profile->get_mixbus()) {
1709 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1710 /* do not allow rename if the track is record-enabled */
1711 items.back().set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1714 items.push_back (SeparatorElem());
1715 items.push_back (CheckMenuElem (_("Active")));
1716 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1717 i->set_active (_route->active());
1718 i->set_sensitive(! _session->transport_rolling());
1719 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1721 if (!Profile->get_mixbus ()) {
1722 items.push_back (SeparatorElem());
1723 items.push_back (CheckMenuElem (_("Strict I/O")));
1724 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1725 i->set_active (_route->strict_io());
1726 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1730 items.push_back (SeparatorElem());
1732 Gtk::Menu* dio_menu = new Menu;
1733 MenuList& dio_items = dio_menu->items();
1734 dio_items.push_back (MenuElem (_("Record Pre-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPreFader)));
1735 dio_items.push_back (MenuElem (_("Record Post-Fader"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOPostFader)));
1736 dio_items.push_back (MenuElem (_("Custom Record+Playback Positions"), sigc::bind (sigc::mem_fun (*this, &RouteUI::set_disk_io_point), DiskIOCustom)));
1738 items.push_back (MenuElem (_("Disk I/O..."), *dio_menu));
1741 _plugin_insert_cnt = 0;
1742 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1743 if (_plugin_insert_cnt > 0) {
1744 items.push_back (SeparatorElem());
1745 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1748 if (boost::dynamic_pointer_cast<MidiTrack>(_route) || _route->the_instrument ()) {
1749 items.push_back (MenuElem (_("Patch Selector..."),
1750 sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
1753 if (_route->the_instrument () && _route->the_instrument ()->output_streams().n_audio() > 2) {
1754 // TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
1755 items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
1756 items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
1759 items.push_back (SeparatorElem());
1760 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1762 items.push_back (SeparatorElem());
1763 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1764 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1765 denormal_menu_item->set_active (_route->denormal_protection());
1768 /* note that this relies on selection being shared across editor and
1769 mixer (or global to the backend, in the future), which is the only
1770 sane thing for users anyway.
1773 StripableTimeAxisView* stav = PublicEditor::instance().get_stripable_time_axis_by_id (_route->id());
1775 Selection& selection (PublicEditor::instance().get_selection());
1776 if (!selection.selected (stav)) {
1777 selection.set (stav);
1780 if (!_route->is_master()) {
1781 items.push_back (SeparatorElem());
1782 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1785 items.push_back (SeparatorElem());
1786 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1792 MixerStrip::name_button_button_press (GdkEventButton* ev)
1794 if (ev->button == 1 || ev->button == 3) {
1795 list_route_operations ();
1797 if (ev->button == 1) {
1798 Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
1801 route_ops_menu->popup (3, ev->time);
1811 MixerStrip::number_button_button_press (GdkEventButton* ev)
1813 if ( ev->button == 3 ) {
1814 list_route_operations ();
1816 route_ops_menu->popup (1, ev->time);
1825 MixerStrip::list_route_operations ()
1827 delete route_ops_menu;
1828 build_route_ops_menu ();
1832 MixerStrip::set_selected (bool yn)
1834 AxisView::set_selected (yn);
1837 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1838 global_frame.set_name ("MixerStripSelectedFrame");
1840 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1841 global_frame.set_name ("MixerStripFrame");
1844 global_frame.queue_draw ();
1847 // processor_box.deselect_all_processors();
1851 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1853 if (what_changed.contains (ARDOUR::Properties::name)) {
1859 MixerStrip::name_changed ()
1863 name_button.set_text (_route->name());
1866 name_button.set_text (PBD::short_version (_route->name(), 5));
1870 set_tooltip (name_button, Gtkmm2ext::markup_escape_text(_route->name()));
1872 if (_session->config.get_track_name_number()) {
1873 const int64_t track_number = _route->track_number ();
1874 if (track_number == 0) {
1875 number_label.set_text ("-");
1877 number_label.set_text (PBD::to_string (abs(_route->track_number ())));
1880 number_label.set_text ("");
1885 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1887 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1891 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1893 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1897 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1899 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1903 MixerStrip::comment_button_resized (Gtk::Allocation& alloc)
1905 _comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1909 MixerStrip::width_button_pressed (GdkEventButton* ev)
1911 if (ev->button != 1) {
1915 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1918 _mixer.set_strip_width (Narrow, true);
1922 _mixer.set_strip_width (Wide, true);
1928 set_width_enum (Narrow, this);
1931 set_width_enum (Wide, this);
1940 MixerStrip::hide_clicked ()
1942 // LAME fix to reset the button status for when it is redisplayed (part 1)
1943 hide_button.set_sensitive(false);
1946 Hiding(); /* EMIT_SIGNAL */
1948 _mixer.hide_strip (this);
1952 hide_button.set_sensitive(true);
1956 MixerStrip::set_embedded (bool yn)
1962 MixerStrip::map_frozen ()
1964 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1966 boost::shared_ptr<AudioTrack> at = audio_track();
1969 switch (at->freeze_state()) {
1970 case AudioTrack::Frozen:
1971 processor_box.set_sensitive (false);
1972 hide_redirect_editors ();
1975 processor_box.set_sensitive (true);
1976 // XXX need some way, maybe, to retoggle redirect editors
1980 processor_box.set_sensitive (true);
1982 RouteUI::map_frozen ();
1986 MixerStrip::hide_redirect_editors ()
1988 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1992 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1994 boost::shared_ptr<Processor> processor (p.lock ());
1999 Gtk::Window* w = processor_box.get_processor_ui (processor);
2007 MixerStrip::reset_strip_style ()
2009 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2011 gpm.set_fader_name ("SendStripBase");
2015 if (is_midi_track()) {
2016 if (_route->active()) {
2017 set_name ("MidiTrackStripBase");
2019 set_name ("MidiTrackStripBaseInactive");
2021 gpm.set_fader_name ("MidiTrackFader");
2022 } else if (is_audio_track()) {
2023 if (_route->active()) {
2024 set_name ("AudioTrackStripBase");
2026 set_name ("AudioTrackStripBaseInactive");
2028 gpm.set_fader_name ("AudioTrackFader");
2030 if (_route->active()) {
2031 set_name ("AudioBusStripBase");
2033 set_name ("AudioBusStripBaseInactive");
2035 gpm.set_fader_name ("AudioBusFader");
2037 /* (no MIDI busses yet) */
2044 MixerStrip::engine_stopped ()
2049 MixerStrip::engine_running ()
2054 MixerStrip::meter_point_string (MeterPoint mp)
2067 case MeterPostFader:
2084 return S_("Meter|In");
2088 return S_("Meter|Pr");
2091 case MeterPostFader:
2092 return S_("Meter|Po");
2096 return S_("Meter|O");
2101 return S_("Meter|C");
2110 /** Called when the monitor-section state */
2112 MixerStrip::monitor_changed ()
2114 assert (monitor_section_button);
2115 if (_session->monitor_active()) {
2116 monitor_section_button->set_name ("master monitor section button active");
2118 monitor_section_button->set_name ("master monitor section button normal");
2122 /** Called when the metering point has changed */
2124 MixerStrip::meter_changed ()
2126 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2127 gpm.setup_meters ();
2128 // reset peak when meter point changes
2129 gpm.reset_peak_display();
2132 /** The bus that we are displaying sends to has changed, or been turned off.
2133 * @param send_to New bus that we are displaying sends to, or 0.
2136 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2138 RouteUI::bus_send_display_changed (send_to);
2141 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2146 revert_to_default_display ();
2149 revert_to_default_display ();
2154 MixerStrip::drop_send ()
2156 boost::shared_ptr<Send> current_send;
2158 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2159 current_send->set_metering (false);
2162 send_gone_connection.disconnect ();
2163 input_button.set_sensitive (true);
2164 output_button.set_sensitive (true);
2165 group_button.set_sensitive (true);
2166 set_invert_sensitive (true);
2167 gpm.meter_point_button.set_sensitive (true);
2168 mute_button->set_sensitive (true);
2169 solo_button->set_sensitive (true);
2170 solo_isolated_led->set_sensitive (true);
2171 solo_safe_led->set_sensitive (true);
2172 monitor_input_button->set_sensitive (true);
2173 monitor_disk_button->set_sensitive (true);
2174 _comment_button.set_sensitive (true);
2175 trim_control.set_sensitive (true);
2176 if (midi_input_enable_button) {
2177 midi_input_enable_button->set_sensitive (true);
2179 control_slave_ui.set_sensitive (true);
2180 RouteUI::check_rec_enable_sensitivity ();
2181 set_button_names (); // update solo button visual state
2185 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2187 _current_delivery = d;
2188 DeliveryChanged (_current_delivery);
2192 MixerStrip::show_send (boost::shared_ptr<Send> send)
2198 set_current_delivery (send);
2200 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2201 send->set_metering (true);
2202 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2204 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2205 gain_meter().setup_meters ();
2207 uint32_t const in = _current_delivery->pans_required();
2208 uint32_t const out = _current_delivery->pan_outs();
2210 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2211 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2212 panner_ui().setup_pan ();
2213 panner_ui().set_send_drawing_mode (true);
2214 panner_ui().show_all ();
2216 input_button.set_sensitive (false);
2217 group_button.set_sensitive (false);
2218 set_invert_sensitive (false);
2219 gpm.meter_point_button.set_sensitive (false);
2220 mute_button->set_sensitive (false);
2221 solo_button->set_sensitive (false);
2222 rec_enable_button->set_sensitive (false);
2223 solo_isolated_led->set_sensitive (false);
2224 solo_safe_led->set_sensitive (false);
2225 monitor_input_button->set_sensitive (false);
2226 monitor_disk_button->set_sensitive (false);
2227 _comment_button.set_sensitive (false);
2228 trim_control.set_sensitive (false);
2229 if (midi_input_enable_button) {
2230 midi_input_enable_button->set_sensitive (false);
2232 control_slave_ui.set_sensitive (false);
2234 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2235 output_button.set_sensitive (false);
2238 reset_strip_style ();
2242 MixerStrip::revert_to_default_display ()
2246 set_current_delivery (_route->main_outs ());
2248 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2249 gain_meter().setup_meters ();
2251 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2252 update_panner_choices();
2253 panner_ui().setup_pan ();
2254 panner_ui().set_send_drawing_mode (false);
2256 if (has_audio_outputs ()) {
2257 panners.show_all ();
2259 panners.hide_all ();
2262 reset_strip_style ();
2266 MixerStrip::set_button_names ()
2270 mute_button->set_text (_("Mute"));
2271 monitor_input_button->set_text (_("In"));
2272 monitor_disk_button->set_text (_("Disk"));
2273 if (monitor_section_button) {
2274 monitor_section_button->set_text (_("Mon"));
2277 if (_route && _route->solo_safe_control()->solo_safe()) {
2278 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2280 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2282 if (!Config->get_solo_control_is_listen_control()) {
2283 solo_button->set_text (_("Solo"));
2285 switch (Config->get_listen_position()) {
2286 case AfterFaderListen:
2287 solo_button->set_text (_("AFL"));
2289 case PreFaderListen:
2290 solo_button->set_text (_("PFL"));
2294 solo_isolated_led->set_text (_("Iso"));
2295 solo_safe_led->set_text (S_("SoloLock|Lock"));
2299 mute_button->set_text (S_("Mute|M"));
2300 monitor_input_button->set_text (S_("MonitorInput|I"));
2301 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2302 if (monitor_section_button) {
2303 monitor_section_button->set_text (S_("Mon|O"));
2306 if (_route && _route->solo_safe_control()->solo_safe()) {
2307 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2309 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2311 if (!Config->get_solo_control_is_listen_control()) {
2312 solo_button->set_text (S_("Solo|S"));
2314 switch (Config->get_listen_position()) {
2315 case AfterFaderListen:
2316 solo_button->set_text (S_("AfterFader|A"));
2318 case PreFaderListen:
2319 solo_button->set_text (S_("Prefader|P"));
2324 solo_isolated_led->set_text (S_("SoloIso|I"));
2325 solo_safe_led->set_text (S_("SoloLock|L"));
2330 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2332 gpm.meter_point_button.set_text ("");
2337 MixerStrip::plugin_selector()
2339 return _mixer.plugin_selector();
2343 MixerStrip::hide_things ()
2345 processor_box.hide_things ();
2349 MixerStrip::input_active_button_press (GdkEventButton*)
2351 /* nothing happens on press */
2356 MixerStrip::input_active_button_release (GdkEventButton* ev)
2358 boost::shared_ptr<MidiTrack> mt = midi_track ();
2364 boost::shared_ptr<RouteList> rl (new RouteList);
2366 rl->push_back (route());
2368 _session->set_exclusive_input_active (rl, !mt->input_active(),
2369 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2375 MixerStrip::midi_input_status_changed ()
2377 if (midi_input_enable_button) {
2378 boost::shared_ptr<MidiTrack> mt = midi_track ();
2380 midi_input_enable_button->set_active (mt->input_active ());
2385 MixerStrip::state_id () const
2387 return string_compose ("strip %1", _route->id().to_s());
2391 MixerStrip::parameter_changed (string p)
2393 if (p == _visibility.get_state_name()) {
2394 /* The user has made changes to the mixer strip visibility, so get
2395 our VisibilityGroup to reflect these changes in our widgets.
2397 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2398 } else if (p == "track-name-number") {
2400 update_track_number_visibility();
2401 } else if (p == "use-monitor-bus") {
2402 if (monitor_section_button) {
2403 if (mute_button->get_parent()) {
2404 mute_button->get_parent()->remove(*mute_button);
2406 if (monitor_section_button->get_parent()) {
2407 monitor_section_button->get_parent()->remove(*monitor_section_button);
2409 if (Config->get_use_monitor_bus ()) {
2410 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2411 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2412 mute_button->show();
2413 monitor_section_button->show();
2415 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2416 mute_button->show();
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 = manage (new Menu);
2523 MenuList& items = m->items ();
2525 RadioMenuItem::Group group;
2527 _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);
2536 _suspend_menu_callbacks = false;
2540 RadioMenuItem::Group tgroup;
2541 items.push_back (SeparatorElem());
2543 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2544 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2545 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2546 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2547 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2548 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2549 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2550 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2551 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2552 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2553 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2556 if (_route->is_master()) {
2559 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2560 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2561 /* non-master bus */
2564 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2571 MeterType cmt = _route->meter_type();
2572 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2574 items.push_back (SeparatorElem());
2575 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2576 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2577 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2578 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2579 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2580 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2582 m->popup (ev->button, ev->time);
2583 _suspend_menu_callbacks = false;
2587 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2588 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2590 using namespace Menu_Helpers;
2592 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2593 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2594 i->set_active (_route->meter_point() == point);
2598 MixerStrip::set_meter_point (MeterPoint p)
2600 if (_suspend_menu_callbacks) return;
2601 _route->set_meter_point (p);
2605 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2606 RadioMenuItem::Group& group, string const & name, MeterType type)
2608 using namespace Menu_Helpers;
2610 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2611 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2612 i->set_active (_route->meter_type() == type);
2616 MixerStrip::set_meter_type (MeterType t)
2618 if (_suspend_menu_callbacks) return;
2623 MixerStrip::update_track_number_visibility ()
2625 DisplaySuspender ds;
2626 bool show_label = _session->config.get_track_name_number();
2628 if (_route && _route->is_master()) {
2633 number_label.show ();
2634 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2635 // except the width of the number label is subtracted from the name-hbox, so we
2636 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2637 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2639 number_label.set_size_request(tnw, -1);
2640 number_label.show ();
2642 number_label.hide ();
2647 MixerStrip::color () const
2649 return route_color ();
2653 MixerStrip::marked_for_display () const
2655 return !_route->presentation_info().hidden();
2659 MixerStrip::set_marked_for_display (bool yn)
2661 return RouteUI::mark_hidden (!yn);