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 "pbd/convert.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/replace_all.h"
28 #include "pbd/stacktrace.h"
30 #include <gtkmm2ext/gtk_ui.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/choice.h>
33 #include <gtkmm2ext/doi.h>
34 #include <gtkmm2ext/slider_controller.h>
35 #include <gtkmm2ext/bindable_button.h>
37 #include "ardour/amp.h"
38 #include "ardour/audio_track.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/internal_send.h"
41 #include "ardour/io.h"
42 #include "ardour/meter.h"
43 #include "ardour/midi_track.h"
44 #include "ardour/pannable.h"
45 #include "ardour/panner.h"
46 #include "ardour/panner_shell.h"
47 #include "ardour/panner_manager.h"
48 #include "ardour/port.h"
49 #include "ardour/profile.h"
50 #include "ardour/route.h"
51 #include "ardour/route_group.h"
52 #include "ardour/send.h"
53 #include "ardour/session.h"
54 #include "ardour/types.h"
55 #include "ardour/user_bundle.h"
56 #include "ardour/vca.h"
57 #include "ardour/vca_manager.h"
59 #include "ardour_window.h"
60 #include "mixer_strip.h"
63 #include "ardour_button.h"
64 #include "public_editor.h"
66 #include "io_selector.h"
68 #include "gui_thread.h"
69 #include "route_group_menu.h"
70 #include "meter_patterns.h"
72 #include "ui_config.h"
76 using namespace ARDOUR;
77 using namespace ARDOUR_UI_UTILS;
80 using namespace Gtkmm2ext;
82 using namespace ArdourMeter;
84 MixerStrip* MixerStrip::_entered_mixer_strip;
85 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
87 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
88 : SessionHandlePtr (sess)
91 , _mixer_owned (in_mixer)
92 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
95 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
96 , rec_mon_table (2, 2)
97 , solo_iso_table (1, 2)
98 , mute_solo_table (1, 2)
99 , bottom_button_table (1, 3)
100 , meter_point_button (_("pre"))
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 , meter_point_button (_("pre"))
134 , monitor_section_button (0)
135 , midi_input_enable_button (0)
136 , _plugin_insert_cnt (0)
137 , _comment_button (_("Comments"))
138 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
139 , _visibility (X_("mixer-element-visibility"))
140 , control_slave_ui (sess)
149 _entered_mixer_strip= 0;
152 ignore_comment_edit = false;
153 ignore_toggle = false;
157 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
158 longest_label = "longest label";
160 string t = _("Click to toggle the width of this mixer strip.");
162 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
165 width_button.set_icon (ArdourIcon::StripWidth);
166 hide_button.set_tweaks (ArdourButton::Square);
167 set_tooltip (width_button, t);
169 hide_button.set_icon (ArdourIcon::CloseCross);
170 hide_button.set_tweaks (ArdourButton::Square);
171 set_tooltip (&hide_button, _("Hide this mixer strip"));
173 input_button_box.set_spacing(2);
175 input_button.set_text (_("Input"));
176 input_button.set_name ("mixer strip button");
177 input_button_box.pack_start (input_button, true, true);
179 output_button.set_text (_("Output"));
180 output_button.set_name ("mixer strip button");
182 set_tooltip (&meter_point_button, _("Click to select metering point"));
183 meter_point_button.set_name ("mixer strip button");
185 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
187 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
188 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
190 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
192 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
193 solo_isolated_led->show ();
194 solo_isolated_led->set_no_show_all (true);
195 solo_isolated_led->set_name (X_("solo isolate"));
196 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
197 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
198 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
200 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
201 solo_safe_led->show ();
202 solo_safe_led->set_no_show_all (true);
203 solo_safe_led->set_name (X_("solo safe"));
204 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
205 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
206 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
208 solo_safe_led->set_text (S_("SoloLock|Lock"));
209 solo_isolated_led->set_text (_("Iso"));
211 solo_iso_table.set_homogeneous (true);
212 solo_iso_table.set_spacings (2);
213 if (!ARDOUR::Profile->get_trx()) {
214 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
215 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
217 solo_iso_table.show ();
219 rec_mon_table.set_homogeneous (true);
220 rec_mon_table.set_row_spacings (2);
221 rec_mon_table.set_col_spacings (2);
222 if (ARDOUR::Profile->get_mixbus()) {
223 rec_mon_table.resize (1, 3);
224 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
225 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
226 } else if (!ARDOUR::Profile->get_trx()) {
227 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
228 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
230 rec_mon_table.show ();
232 if (solo_isolated_led) {
233 button_size_group->add_widget (*solo_isolated_led);
236 button_size_group->add_widget (*solo_safe_led);
239 if (!ARDOUR::Profile->get_mixbus()) {
240 if (rec_enable_button) {
241 button_size_group->add_widget (*rec_enable_button);
243 if (monitor_disk_button) {
244 button_size_group->add_widget (*monitor_disk_button);
246 if (monitor_input_button) {
247 button_size_group->add_widget (*monitor_input_button);
251 mute_solo_table.set_homogeneous (true);
252 mute_solo_table.set_spacings (2);
254 bottom_button_table.set_spacings (2);
255 bottom_button_table.set_homogeneous (true);
256 bottom_button_table.attach (group_button, 1, 2, 0, 1);
257 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
259 name_button.set_name ("mixer strip button");
260 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
261 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
263 set_tooltip (&group_button, _("Mix group"));
264 group_button.set_name ("mixer strip button");
266 _comment_button.set_name (X_("mixer strip button"));
267 _comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
268 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
269 _comment_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::comment_button_resized));
271 // TODO implement ArdourKnob::on_size_request properly
272 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
273 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
275 trim_control.set_tooltip_prefix (_("Trim: "));
276 trim_control.set_name ("trim knob");
277 trim_control.set_no_show_all (true);
278 input_button_box.pack_start (trim_control, false, false);
280 global_vpacker.set_border_width (1);
281 global_vpacker.set_spacing (0);
283 width_button.set_name ("mixer strip button");
284 hide_button.set_name ("mixer strip button");
286 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
287 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
289 width_hide_box.set_spacing (2);
290 width_hide_box.pack_start (width_button, false, true);
291 width_hide_box.pack_start (number_label, true, true);
292 width_hide_box.pack_end (hide_button, false, true);
294 number_label.set_text ("-");
295 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
296 number_label.set_no_show_all ();
297 number_label.set_name ("tracknumber label");
298 number_label.set_fixed_colors (0x80808080, 0x80808080);
299 number_label.set_alignment (.5, .5);
300 number_label.set_fallthrough_to_parent (true);
301 number_label.set_tweaks (ArdourButton::OccasionalText);
303 global_vpacker.set_spacing (2);
304 if (!ARDOUR::Profile->get_trx()) {
305 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
306 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
308 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (processor_box, true, true);
311 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
313 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
314 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
315 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
316 global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
317 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
318 if (!ARDOUR::Profile->get_trx()) {
319 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
320 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
322 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
326 //add a spacer underneath the master bus;
327 //this fills the area that is taken up by the scrollbar on the tracks;
328 //and therefore keeps the faders "even" across the bottom
329 int scrollbar_height = 0;
331 Gtk::Window window (WINDOW_TOPLEVEL);
332 HScrollbar scrollbar;
333 window.add (scrollbar);
334 scrollbar.set_name ("MixerWindow");
335 scrollbar.ensure_style();
336 Gtk::Requisition requisition(scrollbar.size_request ());
337 scrollbar_height = requisition.height;
339 spacer.set_size_request (-1, scrollbar_height);
340 global_vpacker.pack_end (spacer, false, false);
343 global_frame.add (global_vpacker);
344 global_frame.set_shadow_type (Gtk::SHADOW_IN);
345 global_frame.set_name ("BaseFrame");
349 /* force setting of visible selected status */
352 set_selected (false);
357 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
358 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
360 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
361 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
362 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
364 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
365 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
367 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
368 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
369 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
371 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
373 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
375 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
379 /* start off as a passthru strip. we'll correct this, if necessary,
380 in update_diskstream_display().
383 /* start off as a passthru strip. we'll correct this, if necessary,
384 in update_diskstream_display().
387 if (is_midi_track()) {
388 set_name ("MidiTrackStripBase");
390 set_name ("AudioTrackStripBase");
393 add_events (Gdk::BUTTON_RELEASE_MASK|
394 Gdk::ENTER_NOTIFY_MASK|
395 Gdk::LEAVE_NOTIFY_MASK|
397 Gdk::KEY_RELEASE_MASK);
399 set_flags (get_flags() | Gtk::CAN_FOCUS);
401 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
402 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
405 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
406 must be the same as those used in RCOptionEditor so that the configuration changes
407 are recognised when they occur.
409 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
410 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
411 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
412 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
413 _visibility.add (&output_button, X_("Output"), _("Output"), false);
414 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
415 _visibility.add (&control_slave_ui, X_("VCA"), _("VCA Assigns"), false);
417 parameter_changed (X_("mixer-element-visibility"));
418 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
419 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
420 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
422 //watch for mouse enter/exit so we can do some stuff
423 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
424 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
426 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
429 MixerStrip::~MixerStrip ()
431 CatchDeletion (this);
433 if (this ==_entered_mixer_strip)
434 _entered_mixer_strip = NULL;
438 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
440 _entered_mixer_strip = this;
442 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
443 //because the mixerstrip control is a parent that encompasses the strip
444 deselect_all_processors();
450 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
452 //if we have moved outside our strip, but not into a child view, then deselect ourselves
453 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
454 _entered_mixer_strip= 0;
456 //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
457 gpm.gain_display.set_sensitive(false);
459 gpm.gain_display.set_sensitive(true);
461 //if we leave this mixer strip we need to clear out any selections
462 //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
469 MixerStrip::name() const
472 return _route->name();
478 MixerStrip::update_trim_control ()
480 if (route()->trim() && route()->trim()->active() &&
481 route()->n_inputs().n_audio() > 0) {
482 trim_control.show ();
483 trim_control.set_controllable (route()->trim()->gain_control());
485 trim_control.hide ();
486 boost::shared_ptr<Controllable> none;
487 trim_control.set_controllable (none);
492 MixerStrip::set_route (boost::shared_ptr<Route> rt)
494 //the rec/monitor stuff only shows up for tracks.
495 //the show_sends only shows up for buses.
496 //remove them all here, and we may add them back later
497 if (show_sends_button->get_parent()) {
498 rec_mon_table.remove (*show_sends_button);
500 if (rec_enable_button->get_parent()) {
501 rec_mon_table.remove (*rec_enable_button);
503 if (monitor_input_button->get_parent()) {
504 rec_mon_table.remove (*monitor_input_button);
506 if (monitor_disk_button->get_parent()) {
507 rec_mon_table.remove (*monitor_disk_button);
509 if (group_button.get_parent()) {
510 bottom_button_table.remove (group_button);
513 RouteUI::set_route (rt);
515 control_slave_ui.set_stripable (boost::dynamic_pointer_cast<Stripable> (rt));
517 /* ProcessorBox needs access to _route so that it can read
520 processor_box.set_route (rt);
522 revert_to_default_display ();
524 /* unpack these from the parent and stuff them into our own
528 if (gpm.peak_display.get_parent()) {
529 gpm.peak_display.get_parent()->remove (gpm.peak_display);
531 if (gpm.gain_display.get_parent()) {
532 gpm.gain_display.get_parent()->remove (gpm.gain_display);
535 gpm.set_type (rt->meter_type());
537 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
538 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
540 if (solo_button->get_parent()) {
541 mute_solo_table.remove (*solo_button);
544 if (mute_button->get_parent()) {
545 mute_solo_table.remove (*mute_button);
548 if (route()->is_master()) {
549 solo_button->hide ();
550 mute_button->show ();
551 rec_mon_table.hide ();
552 solo_iso_table.set_sensitive(false);
553 control_slave_ui.set_sensitive(false);
554 if (monitor_section_button == 0) {
555 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
556 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
558 monitor_section_button = manage (new ArdourButton);
560 monitor_section_button->set_related_action (act);
561 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
562 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
563 monitor_section_button->show();
564 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
566 parameter_changed ("use-monitor-bus");
568 bottom_button_table.attach (group_button, 1, 2, 0, 1);
569 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
570 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
571 mute_button->show ();
572 solo_button->show ();
573 rec_mon_table.show ();
574 solo_iso_table.set_sensitive(true);
575 control_slave_ui.set_sensitive(true);
578 if (_mixer_owned && route()->is_master() ) {
585 monitor_input_button->show ();
586 monitor_disk_button->show ();
588 monitor_input_button->hide();
589 monitor_disk_button->hide ();
592 update_trim_control();
594 if (is_midi_track()) {
595 if (midi_input_enable_button == 0) {
596 midi_input_enable_button = manage (new ArdourButton);
597 midi_input_enable_button->set_name ("midi input button");
598 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
599 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
600 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
601 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
602 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
604 input_button_box.remove (*midi_input_enable_button);
606 /* get current state */
607 midi_input_status_changed ();
608 input_button_box.pack_start (*midi_input_enable_button, false, false);
610 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
612 if (midi_input_enable_button) {
613 /* removal from the container will delete it */
614 input_button_box.remove (*midi_input_enable_button);
615 midi_input_enable_button = 0;
619 if (is_audio_track()) {
620 boost::shared_ptr<AudioTrack> at = audio_track();
621 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
626 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
627 rec_enable_button->show();
629 if (ARDOUR::Profile->get_mixbus()) {
630 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
631 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
632 } else if (ARDOUR::Profile->get_trx()) {
633 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
635 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
636 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
643 if (!_route->is_master()) {
644 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
645 show_sends_button->show();
649 meter_point_button.set_text (meter_point_string (_route->meter_point()));
651 delete route_ops_menu;
654 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
655 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
656 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
657 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
659 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
661 if (_route->panner_shell()) {
662 update_panner_choices();
663 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
666 if (is_audio_track()) {
667 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
670 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
672 set_stuff_from_route ();
674 /* now force an update of all the various elements */
676 update_mute_display ();
677 update_solo_display ();
680 route_group_changed ();
681 update_track_number_visibility ();
684 panners.setup_pan ();
686 if (has_audio_outputs ()) {
692 update_diskstream_display ();
693 update_input_display ();
694 update_output_display ();
696 add_events (Gdk::BUTTON_RELEASE_MASK);
698 processor_box.show ();
700 if (!route()->is_master() && !route()->is_monitor()) {
701 /* we don't allow master or control routes to be hidden */
706 gpm.reset_peak_display ();
707 gpm.gain_display.show ();
708 gpm.peak_display.show ();
711 width_hide_box.show();
713 global_vpacker.show();
714 mute_solo_table.show();
715 bottom_button_table.show();
717 meter_point_button.show();
718 input_button_box.show_all();
719 output_button.show();
721 _comment_button.show();
723 gpm.gain_automation_state_button.show();
725 parameter_changed ("mixer-element-visibility");
732 MixerStrip::set_stuff_from_route ()
734 /* if width is not set, it will be set by the MixerUI or editor */
736 string str = gui_property ("strip-width");
738 set_width_enum (Width (string_2_enum (str, _width)), this);
743 MixerStrip::set_width_enum (Width w, void* owner)
745 /* always set the gpm width again, things may be hidden */
748 panners.set_width (w);
750 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
752 _width_owner = owner;
756 if (_width_owner == this) {
757 set_gui_property ("strip-width", enum_2_string (_width));
762 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
767 if (show_sends_button) {
768 show_sends_button->set_text (_("Aux"));
771 gpm.gain_automation_style_button.set_text (
772 gpm.astyle_string(gain_automation->automation_style()));
773 gpm.gain_automation_state_button.set_text (
774 gpm.astate_string(gain_automation->automation_state()));
776 if (_route->panner()) {
777 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
778 panners.astyle_string(_route->panner()->automation_style()));
779 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
780 panners.astate_string(_route->panner()->automation_state()));
784 // panners expect an even number of horiz. pixels
785 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
787 set_size_request (width, -1);
793 if (show_sends_button) {
794 show_sends_button->set_text (_("Snd"));
797 gpm.gain_automation_style_button.set_text (
798 gpm.short_astyle_string(gain_automation->automation_style()));
799 gpm.gain_automation_state_button.set_text (
800 gpm.short_astate_string(gain_automation->automation_state()));
801 gain_meter().setup_meters (); // recalc meter width
803 if (_route->panner()) {
804 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
805 panners.short_astyle_string(_route->panner()->automation_style()));
806 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
807 panners.short_astate_string(_route->panner()->automation_state()));
811 // panners expect an even number of horiz. pixels
812 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
814 set_size_request (width, -1);
819 processor_box.set_width (w);
821 update_input_display ();
822 update_output_display ();
823 setup_comment_button ();
824 route_group_changed ();
830 MixerStrip::set_packed (bool yn)
835 set_gui_property ("visible", true);
837 set_gui_property ("visible", false);
842 struct RouteCompareByName {
843 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
844 return a->name().compare (b->name()) < 0;
849 MixerStrip::output_release (GdkEventButton *ev)
851 switch (ev->button) {
853 edit_output_configuration ();
861 MixerStrip::output_press (GdkEventButton *ev)
863 using namespace Menu_Helpers;
864 if (!_session->engine().connected()) {
865 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
870 MenuList& citems = output_menu.items();
871 switch (ev->button) {
874 return false; //wait for the mouse-up to pop the dialog
878 output_menu.set_name ("ArdourContextMenu");
880 output_menu_bundles.clear ();
882 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
884 citems.push_back (SeparatorElem());
885 uint32_t const n_with_separator = citems.size ();
887 ARDOUR::BundleList current = _route->output()->bundles_connected ();
889 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
891 /* give user bundles first chance at being in the menu */
893 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
894 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
895 maybe_add_bundle_to_output_menu (*i, current);
899 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
900 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
901 maybe_add_bundle_to_output_menu (*i, current);
905 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
906 RouteList copy = *routes;
907 copy.sort (RouteCompareByName ());
908 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
909 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
912 if (citems.size() == n_with_separator) {
913 /* no routes added; remove the separator */
917 if (!ARDOUR::Profile->get_mixbus()) {
918 citems.push_back (SeparatorElem());
920 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
923 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
924 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
930 citems.push_back (SeparatorElem());
931 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
933 Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
946 MixerStrip::input_release (GdkEventButton *ev)
948 switch (ev->button) {
951 edit_input_configuration ();
963 MixerStrip::input_press (GdkEventButton *ev)
965 using namespace Menu_Helpers;
967 MenuList& citems = input_menu.items();
968 input_menu.set_name ("ArdourContextMenu");
971 if (!_session->engine().connected()) {
972 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
977 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
980 switch (ev->button) {
983 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
987 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
989 citems.push_back (SeparatorElem());
990 uint32_t const n_with_separator = citems.size ();
992 input_menu_bundles.clear ();
994 ARDOUR::BundleList current = _route->input()->bundles_connected ();
996 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
998 /* give user bundles first chance at being in the menu */
1000 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1001 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1002 maybe_add_bundle_to_input_menu (*i, current);
1006 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1007 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1008 maybe_add_bundle_to_input_menu (*i, current);
1012 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1013 RouteList copy = *routes;
1014 copy.sort (RouteCompareByName ());
1015 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1016 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1019 if (citems.size() == n_with_separator) {
1020 /* no routes added; remove the separator */
1024 citems.push_back (SeparatorElem());
1025 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1028 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1029 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1034 citems.push_back (SeparatorElem());
1035 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1037 Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
1049 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1051 if (ignore_toggle) {
1055 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1057 if (std::find (current.begin(), current.end(), c) == current.end()) {
1058 _route->input()->connect_ports_to_bundle (c, true, this);
1060 _route->input()->disconnect_ports_from_bundle (c, this);
1065 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1067 if (ignore_toggle) {
1071 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1073 if (std::find (current.begin(), current.end(), c) == current.end()) {
1074 _route->output()->connect_ports_to_bundle (c, true, this);
1076 _route->output()->disconnect_ports_from_bundle (c, this);
1081 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1083 using namespace Menu_Helpers;
1085 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1089 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1090 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1094 if (i != input_menu_bundles.end()) {
1098 input_menu_bundles.push_back (b);
1100 MenuList& citems = input_menu.items();
1102 std::string n = b->name ();
1103 replace_all (n, "_", " ");
1105 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1109 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1111 using namespace Menu_Helpers;
1113 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1117 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1118 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1122 if (i != output_menu_bundles.end()) {
1126 output_menu_bundles.push_back (b);
1128 MenuList& citems = output_menu.items();
1130 std::string n = b->name ();
1131 replace_all (n, "_", " ");
1133 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1137 MixerStrip::update_diskstream_display ()
1139 if (is_track() && input_selector) {
1140 input_selector->hide_all ();
1143 route_color_changed ();
1147 MixerStrip::connect_to_pan ()
1149 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1151 panstate_connection.disconnect ();
1152 panstyle_connection.disconnect ();
1154 if (!_route->panner()) {
1158 boost::shared_ptr<Pannable> p = _route->pannable ();
1160 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1161 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1163 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1164 * However, that only works a panner was previously set.
1166 * PannerUI must remain subscribed to _panshell->Changed() in case
1167 * we switch the panner eg. AUX-Send and back
1168 * _route->panner_shell()->Changed() vs _panshell->Changed
1170 if (panners._panner == 0) {
1171 panners.panshell_changed ();
1173 update_panner_choices();
1177 MixerStrip::update_panner_choices ()
1179 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1180 if (!_route->panner_shell()) { return; }
1182 uint32_t in = _route->output()->n_ports().n_audio();
1184 if (_route->panner()) {
1185 in = _route->panner()->in().n_audio();
1188 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1192 * Output port labelling
1193 * =====================
1195 * Case 1: Each output has one connection, all connections are to system:playback_%i
1196 * out 1 -> system:playback_1
1197 * out 2 -> system:playback_2
1198 * out 3 -> system:playback_3
1201 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1202 * out 1 -> ardour:track_x/in 1
1203 * out 2 -> ardour:track_x/in 2
1204 * Display as: track_x
1206 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1207 * out 1 -> program x:foo
1208 * out 2 -> program x:foo
1209 * Display as: program x
1211 * Case 4: No connections (Disconnected)
1214 * Default case (unusual routing):
1215 * Display as: *number of connections*
1219 * .-----------------------------------------------.
1221 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1222 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1223 * '-----------------------------------------------'
1224 * .-----------------------------------------------.
1227 * '-----------------------------------------------'
1231 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1235 boost::shared_ptr<IO> io;
1236 boost::shared_ptr<Port> port;
1237 vector<string> port_connections;
1239 uint32_t total_connection_count = 0;
1240 uint32_t io_connection_count = 0;
1241 uint32_t ardour_connection_count = 0;
1242 uint32_t system_connection_count = 0;
1243 uint32_t other_connection_count = 0;
1244 uint32_t typed_connection_count = 0;
1246 ostringstream label;
1248 bool have_label = false;
1249 bool each_io_has_one_connection = true;
1251 string connection_name;
1252 string ardour_track_name;
1253 string other_connection_type;
1254 string system_ports;
1257 ostringstream tooltip;
1258 char * tooltip_cstr;
1260 /* To avoid confusion, the button caption only shows connections that match the expected datatype
1262 * First of all, if the user made only connections to a given type, we should use that one since
1263 * it is very probably what the user expects. If there are several connections types, then show
1264 * audio ones as primary, which matches expectations for both audio tracks with midi control and
1265 * synthesisers. This first heuristic can be expressed with these two rules:
1266 * A) If there are connected audio ports, consider audio as primary type.
1267 * B) Else, if there are connected midi ports, consider midi as primary type.
1269 * If there are no connected ports, then we choose the primary type based on the type of existing
1270 * but unconnected ports. Again:
1271 * C) If there are audio ports, consider audio as primary type.
1272 * D) Else, if there are midi ports, consider midi as primary type. */
1274 DataType dt = DataType::AUDIO;
1278 io = route->input();
1280 io = route->output();
1283 io_count = io->n_ports().n_total();
1284 for (io_index = 0; io_index < io_count; ++io_index) {
1285 port = io->nth (io_index);
1286 if (port->connected()) {
1288 if (port->type() == DataType::AUDIO) {
1289 /* Rule A) applies no matter the remaining ports */
1290 dt = DataType::AUDIO;
1293 if (port->type() == DataType::MIDI) {
1294 /* Rule B) is a good candidate... */
1295 dt = DataType::MIDI;
1296 /* ...but continue the loop to check remaining ports for rule A) */
1302 /* Neither rule A) nor rule B) matched */
1303 if ( io->n_ports().n_audio() > 0 ) {
1305 dt = DataType::AUDIO;
1306 } else if ( io->n_ports().n_midi() > 0 ) {
1308 dt = DataType::MIDI;
1312 if ( dt == DataType::MIDI ) {
1313 tooltip << _("MIDI ");
1317 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1319 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1322 for (io_index = 0; io_index < io_count; ++io_index) {
1323 port = io->nth (io_index);
1325 port_connections.clear ();
1326 port->get_connections(port_connections);
1328 //ignore any port connections that don't match our DataType
1329 if (port->type() != dt) {
1330 if (!port_connections.empty()) {
1331 ++typed_connection_count;
1336 io_connection_count = 0;
1338 if (!port_connections.empty()) {
1339 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1341 string& connection_name (*i);
1343 if (connection_name.find("system:") == 0) {
1344 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1347 if (io_connection_count == 0) {
1348 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1350 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1353 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1356 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1357 if (ardour_track_name.empty()) {
1358 // "ardour:Master/in 1" -> "ardour:Master/"
1359 string::size_type slash = connection_name.find("/");
1360 if (slash != string::npos) {
1361 ardour_track_name = connection_name.substr(0, slash + 1);
1365 if (connection_name.find(ardour_track_name) == 0) {
1366 ++ardour_connection_count;
1368 } else if (!pn.empty()) {
1369 if (system_ports.empty()) {
1372 system_ports += "/" + pn;
1374 if (connection_name.find("system:") == 0) {
1375 ++system_connection_count;
1377 } else if (connection_name.find("system:midi_") == 0) {
1379 // "system:midi_capture_123" -> "123"
1380 system_port = "M " + connection_name.substr(20);
1382 // "system:midi_playback_123" -> "123"
1383 system_port = "M " + connection_name.substr(21);
1386 if (system_ports.empty()) {
1387 system_ports += system_port;
1389 system_ports += "/" + system_port;
1392 ++system_connection_count;
1394 } else if (connection_name.find("system:") == 0) {
1396 // "system:capture_123" -> "123"
1397 system_port = connection_name.substr(15);
1399 // "system:playback_123" -> "123"
1400 system_port = connection_name.substr(16);
1403 if (system_ports.empty()) {
1404 system_ports += system_port;
1406 system_ports += "/" + system_port;
1409 ++system_connection_count;
1411 if (other_connection_type.empty()) {
1412 // "jamin:in 1" -> "jamin:"
1413 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1416 if (connection_name.find(other_connection_type) == 0) {
1417 ++other_connection_count;
1421 ++total_connection_count;
1422 ++io_connection_count;
1426 if (io_connection_count != 1) {
1427 each_io_has_one_connection = false;
1431 if (total_connection_count == 0) {
1432 tooltip << endl << _("Disconnected");
1435 tooltip_cstr = new char[tooltip.str().size() + 1];
1436 strcpy(tooltip_cstr, tooltip.str().c_str());
1439 set_tooltip (&input_button, tooltip_cstr);
1441 set_tooltip (&output_button, tooltip_cstr);
1444 delete [] tooltip_cstr;
1446 if (each_io_has_one_connection) {
1447 if (total_connection_count == ardour_connection_count) {
1448 // all connections are to the same track in ardour
1449 // "ardour:Master/" -> "Master"
1450 string::size_type slash = ardour_track_name.find("/");
1451 if (slash != string::npos) {
1452 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1453 label << ardour_track_name.substr (ppps, slash - ppps);
1457 else if (total_connection_count == system_connection_count) {
1458 // all connections are to system ports
1459 label << system_ports;
1462 else if (total_connection_count == other_connection_count) {
1463 // all connections are to the same external program eg jamin
1464 // "jamin:" -> "jamin"
1465 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1471 if (total_connection_count == 0) {
1475 // Odd configuration
1476 label << "*" << total_connection_count << "*";
1478 if (typed_connection_count > 0) {
1479 label << "\u2295"; // circled plus
1484 input_button.set_text (label.str());
1486 output_button.set_text (label.str());
1491 MixerStrip::update_input_display ()
1493 update_io_button (_route, _width, true);
1494 panners.setup_pan ();
1496 if (has_audio_outputs ()) {
1497 panners.show_all ();
1499 panners.hide_all ();
1505 MixerStrip::update_output_display ()
1507 update_io_button (_route, _width, false);
1508 gpm.setup_meters ();
1509 panners.setup_pan ();
1511 if (has_audio_outputs ()) {
1512 panners.show_all ();
1514 panners.hide_all ();
1519 MixerStrip::fast_update ()
1521 gpm.update_meters ();
1525 MixerStrip::diskstream_changed ()
1527 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1531 MixerStrip::io_changed_proxy ()
1533 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1534 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_trim_control));
1538 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1540 boost::shared_ptr<Port> a = wa.lock ();
1541 boost::shared_ptr<Port> b = wb.lock ();
1543 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1544 update_input_display ();
1545 set_width_enum (_width, this);
1548 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1549 update_output_display ();
1550 set_width_enum (_width, this);
1555 MixerStrip::setup_comment_button ()
1557 std::string comment = _route->comment();
1559 set_tooltip (_comment_button, comment.empty() ? _("Click to add/edit comments") : _route->comment());
1561 if (comment.empty ()) {
1562 _comment_button.set_name ("generic button");
1563 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1567 _comment_button.set_name ("comment button");
1569 string::size_type pos = comment.find_first_of (" \t\n");
1570 if (pos != string::npos) {
1571 comment = comment.substr (0, pos);
1573 if (comment.empty()) {
1574 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1576 _comment_button.set_text (comment);
1581 MixerStrip::select_route_group (GdkEventButton *ev)
1583 using namespace Menu_Helpers;
1585 if (ev->button == 1) {
1587 if (group_menu == 0) {
1589 PropertyList* plist = new PropertyList();
1591 plist->add (Properties::group_gain, true);
1592 plist->add (Properties::group_mute, true);
1593 plist->add (Properties::group_solo, true);
1595 group_menu = new RouteGroupMenu (_session, plist);
1599 r.push_back (route ());
1600 group_menu->build (r);
1602 RouteGroup *rg = _route->route_group();
1604 Gtkmm2ext::anchored_menu_popup(group_menu->menu(), &group_button,
1605 rg ? rg->name() : _("No Group"),
1613 MixerStrip::route_group_changed ()
1615 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1617 RouteGroup *rg = _route->route_group();
1620 group_button.set_text (PBD::short_version (rg->name(), 5));
1624 group_button.set_text (_("Grp"));
1627 group_button.set_text (_("~G"));
1634 MixerStrip::route_color_changed ()
1636 name_button.modify_bg (STATE_NORMAL, color());
1637 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1638 reset_strip_style ();
1642 MixerStrip::show_passthru_color ()
1644 reset_strip_style ();
1649 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1651 boost::shared_ptr<Processor> processor (p.lock ());
1652 if (!processor || !processor->display_to_user()) {
1655 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
1657 if (pi && pi->is_channelstrip ()) {
1662 ++_plugin_insert_cnt;
1666 MixerStrip::build_route_ops_menu ()
1668 using namespace Menu_Helpers;
1669 route_ops_menu = new Menu;
1670 route_ops_menu->set_name ("ArdourContextMenu");
1672 MenuList& items = route_ops_menu->items();
1674 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1676 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1678 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1680 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1682 items.push_back (SeparatorElem());
1684 if (!_route->is_master()) {
1685 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1687 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1688 rename_menu_item = &items.back();
1690 items.push_back (SeparatorElem());
1691 items.push_back (CheckMenuElem (_("Active")));
1692 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1693 i->set_active (_route->active());
1694 i->set_sensitive(! _session->transport_rolling());
1695 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1697 if (!Profile->get_mixbus ()) {
1698 items.push_back (SeparatorElem());
1699 items.push_back (CheckMenuElem (_("Strict I/O")));
1700 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1701 i->set_active (_route->strict_io());
1702 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1705 _plugin_insert_cnt = 0;
1706 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1707 if (_plugin_insert_cnt > 0) {
1708 items.push_back (SeparatorElem());
1709 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1712 if (_route->the_instrument () && _route->the_instrument ()->output_streams().n_audio() > 2) {
1713 // TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
1714 items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
1715 items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
1718 items.push_back (SeparatorElem());
1719 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1721 items.push_back (SeparatorElem());
1722 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1723 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1724 denormal_menu_item->set_active (_route->denormal_protection());
1727 /* note that this relies on selection being shared across editor and
1728 mixer (or global to the backend, in the future), which is the only
1729 sane thing for users anyway.
1732 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1734 Selection& selection (PublicEditor::instance().get_selection());
1735 if (!selection.selected (rtav)) {
1736 selection.set (rtav);
1739 if (!_route->is_master()) {
1740 items.push_back (SeparatorElem());
1741 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1744 items.push_back (SeparatorElem());
1745 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1751 MixerStrip::name_button_button_press (GdkEventButton* ev)
1753 if (ev->button == 1 || ev->button == 3) {
1754 list_route_operations ();
1756 /* do not allow rename if the track is record-enabled */
1757 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1758 if (ev->button == 1) {
1759 Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
1762 route_ops_menu->popup (3, ev->time);
1772 MixerStrip::number_button_button_press (GdkEventButton* ev)
1774 if ( ev->button == 3 ) {
1775 list_route_operations ();
1777 /* do not allow rename if the track is record-enabled */
1778 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1779 route_ops_menu->popup (1, ev->time);
1788 MixerStrip::list_route_operations ()
1790 delete route_ops_menu;
1791 build_route_ops_menu ();
1795 MixerStrip::set_selected (bool yn)
1797 AxisView::set_selected (yn);
1800 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1801 global_frame.set_name ("MixerStripSelectedFrame");
1803 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1804 global_frame.set_name ("MixerStripFrame");
1807 global_frame.queue_draw ();
1810 // processor_box.deselect_all_processors();
1814 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1816 if (what_changed.contains (ARDOUR::Properties::name)) {
1822 MixerStrip::name_changed ()
1826 name_button.set_text (_route->name());
1829 name_button.set_text (PBD::short_version (_route->name(), 5));
1833 set_tooltip (name_button, _route->name());
1835 if (_session->config.get_track_name_number()) {
1836 const int64_t track_number = _route->track_number ();
1837 if (track_number == 0) {
1838 number_label.set_text ("-");
1840 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1843 number_label.set_text ("");
1848 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1850 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1854 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1856 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1860 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1862 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1866 MixerStrip::comment_button_resized (Gtk::Allocation& alloc)
1868 _comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1872 MixerStrip::width_button_pressed (GdkEventButton* ev)
1874 if (ev->button != 1) {
1878 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1881 _mixer.set_strip_width (Narrow, true);
1885 _mixer.set_strip_width (Wide, true);
1891 set_width_enum (Narrow, this);
1894 set_width_enum (Wide, this);
1903 MixerStrip::hide_clicked ()
1905 // LAME fix to reset the button status for when it is redisplayed (part 1)
1906 hide_button.set_sensitive(false);
1909 Hiding(); /* EMIT_SIGNAL */
1911 _mixer.hide_strip (this);
1915 hide_button.set_sensitive(true);
1919 MixerStrip::set_embedded (bool yn)
1925 MixerStrip::map_frozen ()
1927 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1929 boost::shared_ptr<AudioTrack> at = audio_track();
1932 switch (at->freeze_state()) {
1933 case AudioTrack::Frozen:
1934 processor_box.set_sensitive (false);
1935 hide_redirect_editors ();
1938 processor_box.set_sensitive (true);
1939 // XXX need some way, maybe, to retoggle redirect editors
1943 processor_box.set_sensitive (true);
1945 RouteUI::map_frozen ();
1949 MixerStrip::hide_redirect_editors ()
1951 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1955 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1957 boost::shared_ptr<Processor> processor (p.lock ());
1962 Gtk::Window* w = processor_box.get_processor_ui (processor);
1970 MixerStrip::reset_strip_style ()
1972 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1974 gpm.set_fader_name ("SendStripBase");
1978 if (is_midi_track()) {
1979 if (_route->active()) {
1980 set_name ("MidiTrackStripBase");
1982 set_name ("MidiTrackStripBaseInactive");
1984 gpm.set_fader_name ("MidiTrackFader");
1985 } else if (is_audio_track()) {
1986 if (_route->active()) {
1987 set_name ("AudioTrackStripBase");
1989 set_name ("AudioTrackStripBaseInactive");
1991 gpm.set_fader_name ("AudioTrackFader");
1993 if (_route->active()) {
1994 set_name ("AudioBusStripBase");
1996 set_name ("AudioBusStripBaseInactive");
1998 gpm.set_fader_name ("AudioBusFader");
2000 /* (no MIDI busses yet) */
2007 MixerStrip::engine_stopped ()
2012 MixerStrip::engine_running ()
2017 MixerStrip::meter_point_string (MeterPoint mp)
2030 case MeterPostFader:
2047 return S_("Meter|In");
2051 return S_("Meter|Pr");
2054 case MeterPostFader:
2055 return S_("Meter|Po");
2059 return S_("Meter|O");
2064 return S_("Meter|C");
2073 /** Called when the monitor-section state */
2075 MixerStrip::monitor_changed ()
2077 assert (monitor_section_button);
2078 if (_session->monitor_active()) {
2079 monitor_section_button->set_name ("master monitor section button active");
2081 monitor_section_button->set_name ("master monitor section button normal");
2085 /** Called when the metering point has changed */
2087 MixerStrip::meter_changed ()
2089 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2090 gpm.setup_meters ();
2091 // reset peak when meter point changes
2092 gpm.reset_peak_display();
2095 /** The bus that we are displaying sends to has changed, or been turned off.
2096 * @param send_to New bus that we are displaying sends to, or 0.
2099 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2101 RouteUI::bus_send_display_changed (send_to);
2104 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2109 revert_to_default_display ();
2112 revert_to_default_display ();
2117 MixerStrip::drop_send ()
2119 boost::shared_ptr<Send> current_send;
2121 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2122 current_send->set_metering (false);
2125 send_gone_connection.disconnect ();
2126 input_button.set_sensitive (true);
2127 output_button.set_sensitive (true);
2128 group_button.set_sensitive (true);
2129 set_invert_sensitive (true);
2130 meter_point_button.set_sensitive (true);
2131 mute_button->set_sensitive (true);
2132 solo_button->set_sensitive (true);
2133 solo_isolated_led->set_sensitive (true);
2134 solo_safe_led->set_sensitive (true);
2135 monitor_input_button->set_sensitive (true);
2136 monitor_disk_button->set_sensitive (true);
2137 _comment_button.set_sensitive (true);
2138 RouteUI::check_rec_enable_sensitivity ();
2139 set_button_names (); // update solo button visual state
2143 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2145 _current_delivery = d;
2146 DeliveryChanged (_current_delivery);
2150 MixerStrip::show_send (boost::shared_ptr<Send> send)
2156 set_current_delivery (send);
2158 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2159 send->set_metering (true);
2160 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2162 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2163 gain_meter().setup_meters ();
2165 uint32_t const in = _current_delivery->pans_required();
2166 uint32_t const out = _current_delivery->pan_outs();
2168 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2169 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2170 panner_ui().setup_pan ();
2171 panner_ui().set_send_drawing_mode (true);
2172 panner_ui().show_all ();
2174 input_button.set_sensitive (false);
2175 group_button.set_sensitive (false);
2176 set_invert_sensitive (false);
2177 meter_point_button.set_sensitive (false);
2178 mute_button->set_sensitive (false);
2179 solo_button->set_sensitive (false);
2180 rec_enable_button->set_sensitive (false);
2181 solo_isolated_led->set_sensitive (false);
2182 solo_safe_led->set_sensitive (false);
2183 monitor_input_button->set_sensitive (false);
2184 monitor_disk_button->set_sensitive (false);
2185 _comment_button.set_sensitive (false);
2187 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2188 output_button.set_sensitive (false);
2191 reset_strip_style ();
2195 MixerStrip::revert_to_default_display ()
2199 set_current_delivery (_route->main_outs ());
2201 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2202 gain_meter().setup_meters ();
2204 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2205 update_panner_choices();
2206 panner_ui().setup_pan ();
2207 panner_ui().set_send_drawing_mode (false);
2209 if (has_audio_outputs ()) {
2210 panners.show_all ();
2212 panners.hide_all ();
2215 reset_strip_style ();
2219 MixerStrip::set_button_names ()
2223 mute_button->set_text (_("Mute"));
2224 monitor_input_button->set_text (_("In"));
2225 monitor_disk_button->set_text (_("Disk"));
2226 if (monitor_section_button) {
2227 monitor_section_button->set_text (_("Mon"));
2230 if (_route && _route->solo_safe_control()->solo_safe()) {
2231 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2233 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2235 if (!Config->get_solo_control_is_listen_control()) {
2236 solo_button->set_text (_("Solo"));
2238 switch (Config->get_listen_position()) {
2239 case AfterFaderListen:
2240 solo_button->set_text (_("AFL"));
2242 case PreFaderListen:
2243 solo_button->set_text (_("PFL"));
2247 solo_isolated_led->set_text (_("Iso"));
2248 solo_safe_led->set_text (S_("SoloLock|Lock"));
2252 mute_button->set_text (S_("Mute|M"));
2253 monitor_input_button->set_text (S_("MonitorInput|I"));
2254 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2255 if (monitor_section_button) {
2256 monitor_section_button->set_text (S_("Mon|O"));
2259 if (_route && _route->solo_safe_control()->solo_safe()) {
2260 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2262 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2264 if (!Config->get_solo_control_is_listen_control()) {
2265 solo_button->set_text (S_("Solo|S"));
2267 switch (Config->get_listen_position()) {
2268 case AfterFaderListen:
2269 solo_button->set_text (S_("AfterFader|A"));
2271 case PreFaderListen:
2272 solo_button->set_text (S_("Prefader|P"));
2277 solo_isolated_led->set_text (S_("SoloIso|I"));
2278 solo_safe_led->set_text (S_("SoloLock|L"));
2283 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2285 meter_point_button.set_text ("");
2290 MixerStrip::plugin_selector()
2292 return _mixer.plugin_selector();
2296 MixerStrip::hide_things ()
2298 processor_box.hide_things ();
2302 MixerStrip::input_active_button_press (GdkEventButton*)
2304 /* nothing happens on press */
2309 MixerStrip::input_active_button_release (GdkEventButton* ev)
2311 boost::shared_ptr<MidiTrack> mt = midi_track ();
2317 boost::shared_ptr<RouteList> rl (new RouteList);
2319 rl->push_back (route());
2321 _session->set_exclusive_input_active (rl, !mt->input_active(),
2322 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2328 MixerStrip::midi_input_status_changed ()
2330 if (midi_input_enable_button) {
2331 boost::shared_ptr<MidiTrack> mt = midi_track ();
2333 midi_input_enable_button->set_active (mt->input_active ());
2338 MixerStrip::state_id () const
2340 return string_compose ("strip %1", _route->id().to_s());
2344 MixerStrip::parameter_changed (string p)
2346 if (p == _visibility.get_state_name()) {
2347 /* The user has made changes to the mixer strip visibility, so get
2348 our VisibilityGroup to reflect these changes in our widgets.
2350 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2351 } else if (p == "track-name-number") {
2353 } else if (p == "use-monitor-bus") {
2354 if (monitor_section_button) {
2355 if (mute_button->get_parent()) {
2356 mute_button->get_parent()->remove(*mute_button);
2358 if (monitor_section_button->get_parent()) {
2359 monitor_section_button->get_parent()->remove(*monitor_section_button);
2361 if (Config->get_use_monitor_bus ()) {
2362 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2363 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2364 mute_button->show();
2365 monitor_section_button->show();
2367 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2368 mute_button->show();
2371 } else if (p == "track-name-number") {
2372 update_track_number_visibility();
2376 /** Called to decide whether the solo isolate / solo lock button visibility should
2377 * be overridden from that configured by the user. We do this for the master bus.
2379 * @return optional value that is present if visibility state should be overridden.
2381 boost::optional<bool>
2382 MixerStrip::override_solo_visibility () const
2384 if (_route && _route->is_master ()) {
2385 return boost::optional<bool> (false);
2388 return boost::optional<bool> ();
2392 MixerStrip::add_input_port (DataType t)
2394 _route->input()->add_port ("", this, t);
2398 MixerStrip::add_output_port (DataType t)
2400 _route->output()->add_port ("", this, t);
2404 MixerStrip::route_active_changed ()
2406 reset_strip_style ();
2410 MixerStrip::copy_processors ()
2412 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2416 MixerStrip::cut_processors ()
2418 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2422 MixerStrip::paste_processors ()
2424 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2428 MixerStrip::select_all_processors ()
2430 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2434 MixerStrip::deselect_all_processors ()
2436 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2440 MixerStrip::delete_processors ()
2442 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2446 MixerStrip::toggle_processors ()
2448 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2452 MixerStrip::ab_plugins ()
2454 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2458 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2460 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2463 if (ev->button == 3) {
2464 popup_level_meter_menu (ev);
2472 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2474 using namespace Gtk::Menu_Helpers;
2476 Gtk::Menu* m = manage (new Menu);
2477 MenuList& items = m->items ();
2479 RadioMenuItem::Group group;
2481 _suspend_menu_callbacks = true;
2482 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2483 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2484 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2485 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2486 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2488 if (gpm.meter_channels().n_audio() == 0) {
2489 m->popup (ev->button, ev->time);
2490 _suspend_menu_callbacks = false;
2494 RadioMenuItem::Group tgroup;
2495 items.push_back (SeparatorElem());
2497 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2498 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2499 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2500 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2501 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2502 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2503 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2504 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2505 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2506 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2507 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2510 if (_route->is_master()) {
2513 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2514 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2515 /* non-master bus */
2518 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2525 MeterType cmt = _route->meter_type();
2526 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2528 items.push_back (SeparatorElem());
2529 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2530 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2531 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2532 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2533 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2534 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2536 m->popup (ev->button, ev->time);
2537 _suspend_menu_callbacks = false;
2541 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2542 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2544 using namespace Menu_Helpers;
2546 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2547 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2548 i->set_active (_route->meter_point() == point);
2552 MixerStrip::set_meter_point (MeterPoint p)
2554 if (_suspend_menu_callbacks) return;
2555 _route->set_meter_point (p);
2559 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2560 RadioMenuItem::Group& group, string const & name, MeterType type)
2562 using namespace Menu_Helpers;
2564 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2565 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2566 i->set_active (_route->meter_type() == type);
2570 MixerStrip::set_meter_type (MeterType t)
2572 if (_suspend_menu_callbacks) return;
2577 MixerStrip::update_track_number_visibility ()
2579 DisplaySuspender ds;
2580 bool show_label = _session->config.get_track_name_number();
2582 if (_route && _route->is_master()) {
2587 number_label.show ();
2588 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2589 // except the width of the number label is subtracted from the name-hbox, so we
2590 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2591 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2593 number_label.set_size_request(tnw, -1);
2594 number_label.show ();
2596 number_label.hide ();
2601 MixerStrip::color () const
2603 return route_color ();
2607 MixerStrip::marked_for_display () const
2609 return !_route->presentation_info().hidden();
2613 MixerStrip::set_marked_for_display (bool yn)
2615 return RouteUI::mark_hidden (!yn);