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 if (solo_iso_table.get_parent()) {
553 solo_iso_table.get_parent()->remove(solo_iso_table);
555 if (monitor_section_button == 0) {
556 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
557 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
559 monitor_section_button = manage (new ArdourButton);
561 monitor_section_button->set_related_action (act);
562 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
563 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
564 monitor_section_button->show();
565 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
567 parameter_changed ("use-monitor-bus");
569 bottom_button_table.attach (group_button, 1, 2, 0, 1);
570 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
571 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
572 mute_button->show ();
573 solo_button->show ();
574 rec_mon_table.show ();
577 if (_mixer_owned && route()->is_master() ) {
584 monitor_input_button->show ();
585 monitor_disk_button->show ();
587 monitor_input_button->hide();
588 monitor_disk_button->hide ();
591 update_trim_control();
593 if (is_midi_track()) {
594 if (midi_input_enable_button == 0) {
595 midi_input_enable_button = manage (new ArdourButton);
596 midi_input_enable_button->set_name ("midi input button");
597 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
598 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
599 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
600 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
601 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
603 input_button_box.remove (*midi_input_enable_button);
605 /* get current state */
606 midi_input_status_changed ();
607 input_button_box.pack_start (*midi_input_enable_button, false, false);
609 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
611 if (midi_input_enable_button) {
612 /* removal from the container will delete it */
613 input_button_box.remove (*midi_input_enable_button);
614 midi_input_enable_button = 0;
618 if (is_audio_track()) {
619 boost::shared_ptr<AudioTrack> at = audio_track();
620 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
625 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
626 rec_enable_button->show();
628 if (ARDOUR::Profile->get_mixbus()) {
629 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
630 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
631 } else if (ARDOUR::Profile->get_trx()) {
632 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
634 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
635 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
642 if (!_route->is_master()) {
643 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
644 show_sends_button->show();
648 meter_point_button.set_text (meter_point_string (_route->meter_point()));
650 delete route_ops_menu;
653 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
654 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
655 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
656 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
658 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
660 if (_route->panner_shell()) {
661 update_panner_choices();
662 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
665 if (is_audio_track()) {
666 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
669 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
671 set_stuff_from_route ();
673 /* now force an update of all the various elements */
675 update_mute_display ();
676 update_solo_display ();
679 route_group_changed ();
680 update_track_number_visibility ();
683 panners.setup_pan ();
685 if (has_audio_outputs ()) {
691 update_diskstream_display ();
692 update_input_display ();
693 update_output_display ();
695 add_events (Gdk::BUTTON_RELEASE_MASK);
697 processor_box.show ();
699 if (!route()->is_master() && !route()->is_monitor()) {
700 /* we don't allow master or control routes to be hidden */
705 gpm.reset_peak_display ();
706 gpm.gain_display.show ();
707 gpm.peak_display.show ();
710 width_hide_box.show();
712 global_vpacker.show();
713 mute_solo_table.show();
714 bottom_button_table.show();
716 meter_point_button.show();
717 input_button_box.show_all();
718 output_button.show();
720 _comment_button.show();
722 gpm.gain_automation_state_button.show();
724 parameter_changed ("mixer-element-visibility");
731 MixerStrip::set_stuff_from_route ()
733 /* if width is not set, it will be set by the MixerUI or editor */
735 string str = gui_property ("strip-width");
737 set_width_enum (Width (string_2_enum (str, _width)), this);
742 MixerStrip::set_width_enum (Width w, void* owner)
744 /* always set the gpm width again, things may be hidden */
747 panners.set_width (w);
749 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
751 _width_owner = owner;
755 if (_width_owner == this) {
756 set_gui_property ("strip-width", enum_2_string (_width));
761 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
766 if (show_sends_button) {
767 show_sends_button->set_text (_("Aux"));
770 gpm.gain_automation_style_button.set_text (
771 gpm.astyle_string(gain_automation->automation_style()));
772 gpm.gain_automation_state_button.set_text (
773 gpm.astate_string(gain_automation->automation_state()));
775 if (_route->panner()) {
776 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
777 panners.astyle_string(_route->panner()->automation_style()));
778 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
779 panners.astate_string(_route->panner()->automation_state()));
783 // panners expect an even number of horiz. pixels
784 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
786 set_size_request (width, -1);
792 if (show_sends_button) {
793 show_sends_button->set_text (_("Snd"));
796 gpm.gain_automation_style_button.set_text (
797 gpm.short_astyle_string(gain_automation->automation_style()));
798 gpm.gain_automation_state_button.set_text (
799 gpm.short_astate_string(gain_automation->automation_state()));
800 gain_meter().setup_meters (); // recalc meter width
802 if (_route->panner()) {
803 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
804 panners.short_astyle_string(_route->panner()->automation_style()));
805 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
806 panners.short_astate_string(_route->panner()->automation_state()));
810 // panners expect an even number of horiz. pixels
811 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
813 set_size_request (width, -1);
818 processor_box.set_width (w);
820 update_input_display ();
821 update_output_display ();
822 setup_comment_button ();
823 route_group_changed ();
829 MixerStrip::set_packed (bool yn)
834 set_gui_property ("visible", true);
836 set_gui_property ("visible", false);
841 struct RouteCompareByName {
842 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
843 return a->name().compare (b->name()) < 0;
848 MixerStrip::output_release (GdkEventButton *ev)
850 switch (ev->button) {
852 edit_output_configuration ();
860 MixerStrip::output_press (GdkEventButton *ev)
862 using namespace Menu_Helpers;
863 if (!_session->engine().connected()) {
864 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
869 MenuList& citems = output_menu.items();
870 switch (ev->button) {
873 return false; //wait for the mouse-up to pop the dialog
877 output_menu.set_name ("ArdourContextMenu");
879 output_menu_bundles.clear ();
881 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
883 citems.push_back (SeparatorElem());
884 uint32_t const n_with_separator = citems.size ();
886 ARDOUR::BundleList current = _route->output()->bundles_connected ();
888 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
890 /* give user bundles first chance at being in the menu */
892 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
893 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
894 maybe_add_bundle_to_output_menu (*i, current);
898 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
899 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
900 maybe_add_bundle_to_output_menu (*i, current);
904 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
905 RouteList copy = *routes;
906 copy.sort (RouteCompareByName ());
907 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
908 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
911 if (citems.size() == n_with_separator) {
912 /* no routes added; remove the separator */
916 if (!ARDOUR::Profile->get_mixbus()) {
917 citems.push_back (SeparatorElem());
919 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
922 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
923 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
929 citems.push_back (SeparatorElem());
930 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
932 Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
945 MixerStrip::input_release (GdkEventButton *ev)
947 switch (ev->button) {
950 edit_input_configuration ();
962 MixerStrip::input_press (GdkEventButton *ev)
964 using namespace Menu_Helpers;
966 MenuList& citems = input_menu.items();
967 input_menu.set_name ("ArdourContextMenu");
970 if (!_session->engine().connected()) {
971 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
976 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
979 switch (ev->button) {
982 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
986 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
988 citems.push_back (SeparatorElem());
989 uint32_t const n_with_separator = citems.size ();
991 input_menu_bundles.clear ();
993 ARDOUR::BundleList current = _route->input()->bundles_connected ();
995 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
997 /* give user bundles first chance at being in the menu */
999 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1000 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1001 maybe_add_bundle_to_input_menu (*i, current);
1005 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1006 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1007 maybe_add_bundle_to_input_menu (*i, current);
1011 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1012 RouteList copy = *routes;
1013 copy.sort (RouteCompareByName ());
1014 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1015 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1018 if (citems.size() == n_with_separator) {
1019 /* no routes added; remove the separator */
1023 citems.push_back (SeparatorElem());
1024 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1027 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1028 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1033 citems.push_back (SeparatorElem());
1034 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1036 Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
1048 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1050 if (ignore_toggle) {
1054 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1056 if (std::find (current.begin(), current.end(), c) == current.end()) {
1057 _route->input()->connect_ports_to_bundle (c, true, this);
1059 _route->input()->disconnect_ports_from_bundle (c, this);
1064 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1066 if (ignore_toggle) {
1070 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1072 if (std::find (current.begin(), current.end(), c) == current.end()) {
1073 _route->output()->connect_ports_to_bundle (c, true, this);
1075 _route->output()->disconnect_ports_from_bundle (c, this);
1080 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1082 using namespace Menu_Helpers;
1084 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1088 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1089 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1093 if (i != input_menu_bundles.end()) {
1097 input_menu_bundles.push_back (b);
1099 MenuList& citems = input_menu.items();
1101 std::string n = b->name ();
1102 replace_all (n, "_", " ");
1104 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1108 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1110 using namespace Menu_Helpers;
1112 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1116 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1117 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1121 if (i != output_menu_bundles.end()) {
1125 output_menu_bundles.push_back (b);
1127 MenuList& citems = output_menu.items();
1129 std::string n = b->name ();
1130 replace_all (n, "_", " ");
1132 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1136 MixerStrip::update_diskstream_display ()
1138 if (is_track() && input_selector) {
1139 input_selector->hide_all ();
1142 route_color_changed ();
1146 MixerStrip::connect_to_pan ()
1148 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1150 panstate_connection.disconnect ();
1151 panstyle_connection.disconnect ();
1153 if (!_route->panner()) {
1157 boost::shared_ptr<Pannable> p = _route->pannable ();
1159 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1160 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1162 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1163 * However, that only works a panner was previously set.
1165 * PannerUI must remain subscribed to _panshell->Changed() in case
1166 * we switch the panner eg. AUX-Send and back
1167 * _route->panner_shell()->Changed() vs _panshell->Changed
1169 if (panners._panner == 0) {
1170 panners.panshell_changed ();
1172 update_panner_choices();
1176 MixerStrip::update_panner_choices ()
1178 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1179 if (!_route->panner_shell()) { return; }
1181 uint32_t in = _route->output()->n_ports().n_audio();
1183 if (_route->panner()) {
1184 in = _route->panner()->in().n_audio();
1187 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1191 * Output port labelling
1192 * =====================
1194 * Case 1: Each output has one connection, all connections are to system:playback_%i
1195 * out 1 -> system:playback_1
1196 * out 2 -> system:playback_2
1197 * out 3 -> system:playback_3
1200 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1201 * out 1 -> ardour:track_x/in 1
1202 * out 2 -> ardour:track_x/in 2
1203 * Display as: track_x
1205 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1206 * out 1 -> program x:foo
1207 * out 2 -> program x:foo
1208 * Display as: program x
1210 * Case 4: No connections (Disconnected)
1213 * Default case (unusual routing):
1214 * Display as: *number of connections*
1218 * .-----------------------------------------------.
1220 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1221 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1222 * '-----------------------------------------------'
1223 * .-----------------------------------------------.
1226 * '-----------------------------------------------'
1230 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1234 boost::shared_ptr<IO> io;
1235 boost::shared_ptr<Port> port;
1236 vector<string> port_connections;
1238 uint32_t total_connection_count = 0;
1239 uint32_t io_connection_count = 0;
1240 uint32_t ardour_connection_count = 0;
1241 uint32_t system_connection_count = 0;
1242 uint32_t other_connection_count = 0;
1243 uint32_t typed_connection_count = 0;
1245 ostringstream label;
1247 bool have_label = false;
1248 bool each_io_has_one_connection = true;
1250 string connection_name;
1251 string ardour_track_name;
1252 string other_connection_type;
1253 string system_ports;
1256 ostringstream tooltip;
1257 char * tooltip_cstr;
1259 /* To avoid confusion, the button caption only shows connections that match the expected datatype
1261 * First of all, if the user made only connections to a given type, we should use that one since
1262 * it is very probably what the user expects. If there are several connections types, then show
1263 * audio ones as primary, which matches expectations for both audio tracks with midi control and
1264 * synthesisers. This first heuristic can be expressed with these two rules:
1265 * A) If there are connected audio ports, consider audio as primary type.
1266 * B) Else, if there are connected midi ports, consider midi as primary type.
1268 * If there are no connected ports, then we choose the primary type based on the type of existing
1269 * but unconnected ports. Again:
1270 * C) If there are audio ports, consider audio as primary type.
1271 * D) Else, if there are midi ports, consider midi as primary type. */
1273 DataType dt = DataType::AUDIO;
1277 io = route->input();
1279 io = route->output();
1282 io_count = io->n_ports().n_total();
1283 for (io_index = 0; io_index < io_count; ++io_index) {
1284 port = io->nth (io_index);
1285 if (port->connected()) {
1287 if (port->type() == DataType::AUDIO) {
1288 /* Rule A) applies no matter the remaining ports */
1289 dt = DataType::AUDIO;
1292 if (port->type() == DataType::MIDI) {
1293 /* Rule B) is a good candidate... */
1294 dt = DataType::MIDI;
1295 /* ...but continue the loop to check remaining ports for rule A) */
1301 /* Neither rule A) nor rule B) matched */
1302 if ( io->n_ports().n_audio() > 0 ) {
1304 dt = DataType::AUDIO;
1305 } else if ( io->n_ports().n_midi() > 0 ) {
1307 dt = DataType::MIDI;
1311 if ( dt == DataType::MIDI ) {
1312 tooltip << _("MIDI ");
1316 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1318 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1321 for (io_index = 0; io_index < io_count; ++io_index) {
1322 port = io->nth (io_index);
1324 port_connections.clear ();
1325 port->get_connections(port_connections);
1327 //ignore any port connections that don't match our DataType
1328 if (port->type() != dt) {
1329 if (!port_connections.empty()) {
1330 ++typed_connection_count;
1335 io_connection_count = 0;
1337 if (!port_connections.empty()) {
1338 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1340 string& connection_name (*i);
1342 if (connection_name.find("system:") == 0) {
1343 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1346 if (io_connection_count == 0) {
1347 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1349 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1352 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1355 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1356 if (ardour_track_name.empty()) {
1357 // "ardour:Master/in 1" -> "ardour:Master/"
1358 string::size_type slash = connection_name.find("/");
1359 if (slash != string::npos) {
1360 ardour_track_name = connection_name.substr(0, slash + 1);
1364 if (connection_name.find(ardour_track_name) == 0) {
1365 ++ardour_connection_count;
1367 } else if (!pn.empty()) {
1368 if (system_ports.empty()) {
1371 system_ports += "/" + pn;
1373 if (connection_name.find("system:") == 0) {
1374 ++system_connection_count;
1376 } else if (connection_name.find("system:midi_") == 0) {
1378 // "system:midi_capture_123" -> "123"
1379 system_port = "M " + connection_name.substr(20);
1381 // "system:midi_playback_123" -> "123"
1382 system_port = "M " + connection_name.substr(21);
1385 if (system_ports.empty()) {
1386 system_ports += system_port;
1388 system_ports += "/" + system_port;
1391 ++system_connection_count;
1393 } else if (connection_name.find("system:") == 0) {
1395 // "system:capture_123" -> "123"
1396 system_port = connection_name.substr(15);
1398 // "system:playback_123" -> "123"
1399 system_port = connection_name.substr(16);
1402 if (system_ports.empty()) {
1403 system_ports += system_port;
1405 system_ports += "/" + system_port;
1408 ++system_connection_count;
1410 if (other_connection_type.empty()) {
1411 // "jamin:in 1" -> "jamin:"
1412 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1415 if (connection_name.find(other_connection_type) == 0) {
1416 ++other_connection_count;
1420 ++total_connection_count;
1421 ++io_connection_count;
1425 if (io_connection_count != 1) {
1426 each_io_has_one_connection = false;
1430 if (total_connection_count == 0) {
1431 tooltip << endl << _("Disconnected");
1434 tooltip_cstr = new char[tooltip.str().size() + 1];
1435 strcpy(tooltip_cstr, tooltip.str().c_str());
1438 set_tooltip (&input_button, tooltip_cstr);
1440 set_tooltip (&output_button, tooltip_cstr);
1443 delete [] tooltip_cstr;
1445 if (each_io_has_one_connection) {
1446 if (total_connection_count == ardour_connection_count) {
1447 // all connections are to the same track in ardour
1448 // "ardour:Master/" -> "Master"
1449 string::size_type slash = ardour_track_name.find("/");
1450 if (slash != string::npos) {
1451 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1452 label << ardour_track_name.substr (ppps, slash - ppps);
1456 else if (total_connection_count == system_connection_count) {
1457 // all connections are to system ports
1458 label << system_ports;
1461 else if (total_connection_count == other_connection_count) {
1462 // all connections are to the same external program eg jamin
1463 // "jamin:" -> "jamin"
1464 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1470 if (total_connection_count == 0) {
1474 // Odd configuration
1475 label << "*" << total_connection_count << "*";
1477 if (typed_connection_count > 0) {
1478 label << "\u2295"; // circled plus
1483 input_button.set_text (label.str());
1485 output_button.set_text (label.str());
1490 MixerStrip::update_input_display ()
1492 update_io_button (_route, _width, true);
1493 panners.setup_pan ();
1495 if (has_audio_outputs ()) {
1496 panners.show_all ();
1498 panners.hide_all ();
1504 MixerStrip::update_output_display ()
1506 update_io_button (_route, _width, false);
1507 gpm.setup_meters ();
1508 panners.setup_pan ();
1510 if (has_audio_outputs ()) {
1511 panners.show_all ();
1513 panners.hide_all ();
1518 MixerStrip::fast_update ()
1520 gpm.update_meters ();
1524 MixerStrip::diskstream_changed ()
1526 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1530 MixerStrip::io_changed_proxy ()
1532 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1533 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_trim_control));
1537 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1539 boost::shared_ptr<Port> a = wa.lock ();
1540 boost::shared_ptr<Port> b = wb.lock ();
1542 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1543 update_input_display ();
1544 set_width_enum (_width, this);
1547 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1548 update_output_display ();
1549 set_width_enum (_width, this);
1554 MixerStrip::setup_comment_button ()
1556 std::string comment = _route->comment();
1558 set_tooltip (_comment_button, comment.empty() ? _("Click to add/edit comments") : _route->comment());
1560 if (comment.empty ()) {
1561 _comment_button.set_name ("generic button");
1562 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1566 _comment_button.set_name ("comment button");
1568 string::size_type pos = comment.find_first_of (" \t\n");
1569 if (pos != string::npos) {
1570 comment = comment.substr (0, pos);
1572 if (comment.empty()) {
1573 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1575 _comment_button.set_text (comment);
1580 MixerStrip::select_route_group (GdkEventButton *ev)
1582 using namespace Menu_Helpers;
1584 if (ev->button == 1) {
1586 if (group_menu == 0) {
1588 PropertyList* plist = new PropertyList();
1590 plist->add (Properties::group_gain, true);
1591 plist->add (Properties::group_mute, true);
1592 plist->add (Properties::group_solo, true);
1594 group_menu = new RouteGroupMenu (_session, plist);
1598 r.push_back (route ());
1599 group_menu->build (r);
1601 RouteGroup *rg = _route->route_group();
1603 Gtkmm2ext::anchored_menu_popup(group_menu->menu(), &group_button,
1604 rg ? rg->name() : _("No Group"),
1612 MixerStrip::route_group_changed ()
1614 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1616 RouteGroup *rg = _route->route_group();
1619 group_button.set_text (PBD::short_version (rg->name(), 5));
1623 group_button.set_text (_("Grp"));
1626 group_button.set_text (_("~G"));
1633 MixerStrip::route_color_changed ()
1635 name_button.modify_bg (STATE_NORMAL, color());
1636 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1637 reset_strip_style ();
1641 MixerStrip::show_passthru_color ()
1643 reset_strip_style ();
1648 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1650 boost::shared_ptr<Processor> processor (p.lock ());
1651 if (!processor || !processor->display_to_user()) {
1654 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
1656 if (pi && pi->is_channelstrip ()) {
1661 ++_plugin_insert_cnt;
1665 MixerStrip::build_route_ops_menu ()
1667 using namespace Menu_Helpers;
1668 route_ops_menu = new Menu;
1669 route_ops_menu->set_name ("ArdourContextMenu");
1671 MenuList& items = route_ops_menu->items();
1673 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1675 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1677 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1679 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1681 items.push_back (SeparatorElem());
1683 if (!_route->is_master()) {
1684 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1686 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1687 rename_menu_item = &items.back();
1689 items.push_back (SeparatorElem());
1690 items.push_back (CheckMenuElem (_("Active")));
1691 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1692 i->set_active (_route->active());
1693 i->set_sensitive(! _session->transport_rolling());
1694 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1696 if (!Profile->get_mixbus ()) {
1697 items.push_back (SeparatorElem());
1698 items.push_back (CheckMenuElem (_("Strict I/O")));
1699 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1700 i->set_active (_route->strict_io());
1701 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1704 _plugin_insert_cnt = 0;
1705 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1706 if (_plugin_insert_cnt > 0) {
1707 items.push_back (SeparatorElem());
1708 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1711 if (_route->the_instrument () && _route->the_instrument ()->output_streams().n_audio() > 2) {
1712 // TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
1713 items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
1714 items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
1717 items.push_back (SeparatorElem());
1718 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1720 items.push_back (SeparatorElem());
1721 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1722 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1723 denormal_menu_item->set_active (_route->denormal_protection());
1726 /* note that this relies on selection being shared across editor and
1727 mixer (or global to the backend, in the future), which is the only
1728 sane thing for users anyway.
1731 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1733 Selection& selection (PublicEditor::instance().get_selection());
1734 if (!selection.selected (rtav)) {
1735 selection.set (rtav);
1738 if (!_route->is_master()) {
1739 items.push_back (SeparatorElem());
1740 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1743 items.push_back (SeparatorElem());
1744 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1750 MixerStrip::name_button_button_press (GdkEventButton* ev)
1752 if (ev->button == 1 || ev->button == 3) {
1753 list_route_operations ();
1755 /* do not allow rename if the track is record-enabled */
1756 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1757 if (ev->button == 1) {
1758 Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
1761 route_ops_menu->popup (3, ev->time);
1771 MixerStrip::number_button_button_press (GdkEventButton* ev)
1773 if ( ev->button == 3 ) {
1774 list_route_operations ();
1776 /* do not allow rename if the track is record-enabled */
1777 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1778 route_ops_menu->popup (1, ev->time);
1787 MixerStrip::list_route_operations ()
1789 delete route_ops_menu;
1790 build_route_ops_menu ();
1794 MixerStrip::set_selected (bool yn)
1796 AxisView::set_selected (yn);
1799 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1800 global_frame.set_name ("MixerStripSelectedFrame");
1802 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1803 global_frame.set_name ("MixerStripFrame");
1806 global_frame.queue_draw ();
1809 // processor_box.deselect_all_processors();
1813 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1815 if (what_changed.contains (ARDOUR::Properties::name)) {
1821 MixerStrip::name_changed ()
1825 name_button.set_text (_route->name());
1828 name_button.set_text (PBD::short_version (_route->name(), 5));
1832 set_tooltip (name_button, _route->name());
1834 if (_session->config.get_track_name_number()) {
1835 const int64_t track_number = _route->track_number ();
1836 if (track_number == 0) {
1837 number_label.set_text ("-");
1839 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1842 number_label.set_text ("");
1847 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1849 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1853 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1855 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1859 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1861 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1865 MixerStrip::comment_button_resized (Gtk::Allocation& alloc)
1867 _comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1871 MixerStrip::width_button_pressed (GdkEventButton* ev)
1873 if (ev->button != 1) {
1877 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1880 _mixer.set_strip_width (Narrow, true);
1884 _mixer.set_strip_width (Wide, true);
1890 set_width_enum (Narrow, this);
1893 set_width_enum (Wide, this);
1902 MixerStrip::hide_clicked ()
1904 // LAME fix to reset the button status for when it is redisplayed (part 1)
1905 hide_button.set_sensitive(false);
1908 Hiding(); /* EMIT_SIGNAL */
1910 _mixer.hide_strip (this);
1914 hide_button.set_sensitive(true);
1918 MixerStrip::set_embedded (bool yn)
1924 MixerStrip::map_frozen ()
1926 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1928 boost::shared_ptr<AudioTrack> at = audio_track();
1931 switch (at->freeze_state()) {
1932 case AudioTrack::Frozen:
1933 processor_box.set_sensitive (false);
1934 hide_redirect_editors ();
1937 processor_box.set_sensitive (true);
1938 // XXX need some way, maybe, to retoggle redirect editors
1942 processor_box.set_sensitive (true);
1944 RouteUI::map_frozen ();
1948 MixerStrip::hide_redirect_editors ()
1950 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1954 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1956 boost::shared_ptr<Processor> processor (p.lock ());
1961 Gtk::Window* w = processor_box.get_processor_ui (processor);
1969 MixerStrip::reset_strip_style ()
1971 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1973 gpm.set_fader_name ("SendStripBase");
1977 if (is_midi_track()) {
1978 if (_route->active()) {
1979 set_name ("MidiTrackStripBase");
1981 set_name ("MidiTrackStripBaseInactive");
1983 gpm.set_fader_name ("MidiTrackFader");
1984 } else if (is_audio_track()) {
1985 if (_route->active()) {
1986 set_name ("AudioTrackStripBase");
1988 set_name ("AudioTrackStripBaseInactive");
1990 gpm.set_fader_name ("AudioTrackFader");
1992 if (_route->active()) {
1993 set_name ("AudioBusStripBase");
1995 set_name ("AudioBusStripBaseInactive");
1997 gpm.set_fader_name ("AudioBusFader");
1999 /* (no MIDI busses yet) */
2006 MixerStrip::engine_stopped ()
2011 MixerStrip::engine_running ()
2016 MixerStrip::meter_point_string (MeterPoint mp)
2029 case MeterPostFader:
2046 return S_("Meter|In");
2050 return S_("Meter|Pr");
2053 case MeterPostFader:
2054 return S_("Meter|Po");
2058 return S_("Meter|O");
2063 return S_("Meter|C");
2072 /** Called when the monitor-section state */
2074 MixerStrip::monitor_changed ()
2076 assert (monitor_section_button);
2077 if (_session->monitor_active()) {
2078 monitor_section_button->set_name ("master monitor section button active");
2080 monitor_section_button->set_name ("master monitor section button normal");
2084 /** Called when the metering point has changed */
2086 MixerStrip::meter_changed ()
2088 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2089 gpm.setup_meters ();
2090 // reset peak when meter point changes
2091 gpm.reset_peak_display();
2094 /** The bus that we are displaying sends to has changed, or been turned off.
2095 * @param send_to New bus that we are displaying sends to, or 0.
2098 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2100 RouteUI::bus_send_display_changed (send_to);
2103 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2108 revert_to_default_display ();
2111 revert_to_default_display ();
2116 MixerStrip::drop_send ()
2118 boost::shared_ptr<Send> current_send;
2120 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2121 current_send->set_metering (false);
2124 send_gone_connection.disconnect ();
2125 input_button.set_sensitive (true);
2126 output_button.set_sensitive (true);
2127 group_button.set_sensitive (true);
2128 set_invert_sensitive (true);
2129 meter_point_button.set_sensitive (true);
2130 mute_button->set_sensitive (true);
2131 solo_button->set_sensitive (true);
2132 solo_isolated_led->set_sensitive (true);
2133 solo_safe_led->set_sensitive (true);
2134 monitor_input_button->set_sensitive (true);
2135 monitor_disk_button->set_sensitive (true);
2136 _comment_button.set_sensitive (true);
2137 RouteUI::check_rec_enable_sensitivity ();
2138 set_button_names (); // update solo button visual state
2142 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2144 _current_delivery = d;
2145 DeliveryChanged (_current_delivery);
2149 MixerStrip::show_send (boost::shared_ptr<Send> send)
2155 set_current_delivery (send);
2157 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2158 send->set_metering (true);
2159 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2161 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2162 gain_meter().setup_meters ();
2164 uint32_t const in = _current_delivery->pans_required();
2165 uint32_t const out = _current_delivery->pan_outs();
2167 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2168 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2169 panner_ui().setup_pan ();
2170 panner_ui().set_send_drawing_mode (true);
2171 panner_ui().show_all ();
2173 input_button.set_sensitive (false);
2174 group_button.set_sensitive (false);
2175 set_invert_sensitive (false);
2176 meter_point_button.set_sensitive (false);
2177 mute_button->set_sensitive (false);
2178 solo_button->set_sensitive (false);
2179 rec_enable_button->set_sensitive (false);
2180 solo_isolated_led->set_sensitive (false);
2181 solo_safe_led->set_sensitive (false);
2182 monitor_input_button->set_sensitive (false);
2183 monitor_disk_button->set_sensitive (false);
2184 _comment_button.set_sensitive (false);
2186 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2187 output_button.set_sensitive (false);
2190 reset_strip_style ();
2194 MixerStrip::revert_to_default_display ()
2198 set_current_delivery (_route->main_outs ());
2200 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2201 gain_meter().setup_meters ();
2203 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2204 update_panner_choices();
2205 panner_ui().setup_pan ();
2206 panner_ui().set_send_drawing_mode (false);
2208 if (has_audio_outputs ()) {
2209 panners.show_all ();
2211 panners.hide_all ();
2214 reset_strip_style ();
2218 MixerStrip::set_button_names ()
2222 mute_button->set_text (_("Mute"));
2223 monitor_input_button->set_text (_("In"));
2224 monitor_disk_button->set_text (_("Disk"));
2225 if (monitor_section_button) {
2226 monitor_section_button->set_text (_("Mon"));
2229 if (_route && _route->solo_safe_control()->solo_safe()) {
2230 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2232 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2234 if (!Config->get_solo_control_is_listen_control()) {
2235 solo_button->set_text (_("Solo"));
2237 switch (Config->get_listen_position()) {
2238 case AfterFaderListen:
2239 solo_button->set_text (_("AFL"));
2241 case PreFaderListen:
2242 solo_button->set_text (_("PFL"));
2246 solo_isolated_led->set_text (_("Iso"));
2247 solo_safe_led->set_text (S_("SoloLock|Lock"));
2251 mute_button->set_text (S_("Mute|M"));
2252 monitor_input_button->set_text (S_("MonitorInput|I"));
2253 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2254 if (monitor_section_button) {
2255 monitor_section_button->set_text (S_("Mon|O"));
2258 if (_route && _route->solo_safe_control()->solo_safe()) {
2259 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2261 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2263 if (!Config->get_solo_control_is_listen_control()) {
2264 solo_button->set_text (S_("Solo|S"));
2266 switch (Config->get_listen_position()) {
2267 case AfterFaderListen:
2268 solo_button->set_text (S_("AfterFader|A"));
2270 case PreFaderListen:
2271 solo_button->set_text (S_("Prefader|P"));
2276 solo_isolated_led->set_text (S_("SoloIso|I"));
2277 solo_safe_led->set_text (S_("SoloLock|L"));
2282 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2284 meter_point_button.set_text ("");
2289 MixerStrip::plugin_selector()
2291 return _mixer.plugin_selector();
2295 MixerStrip::hide_things ()
2297 processor_box.hide_things ();
2301 MixerStrip::input_active_button_press (GdkEventButton*)
2303 /* nothing happens on press */
2308 MixerStrip::input_active_button_release (GdkEventButton* ev)
2310 boost::shared_ptr<MidiTrack> mt = midi_track ();
2316 boost::shared_ptr<RouteList> rl (new RouteList);
2318 rl->push_back (route());
2320 _session->set_exclusive_input_active (rl, !mt->input_active(),
2321 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2327 MixerStrip::midi_input_status_changed ()
2329 if (midi_input_enable_button) {
2330 boost::shared_ptr<MidiTrack> mt = midi_track ();
2332 midi_input_enable_button->set_active (mt->input_active ());
2337 MixerStrip::state_id () const
2339 return string_compose ("strip %1", _route->id().to_s());
2343 MixerStrip::parameter_changed (string p)
2345 if (p == _visibility.get_state_name()) {
2346 /* The user has made changes to the mixer strip visibility, so get
2347 our VisibilityGroup to reflect these changes in our widgets.
2349 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2350 } else if (p == "track-name-number") {
2352 } else if (p == "use-monitor-bus") {
2353 if (monitor_section_button) {
2354 if (mute_button->get_parent()) {
2355 mute_button->get_parent()->remove(*mute_button);
2357 if (monitor_section_button->get_parent()) {
2358 monitor_section_button->get_parent()->remove(*monitor_section_button);
2360 if (Config->get_use_monitor_bus ()) {
2361 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2362 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2363 mute_button->show();
2364 monitor_section_button->show();
2366 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2367 mute_button->show();
2370 } else if (p == "track-name-number") {
2371 update_track_number_visibility();
2375 /** Called to decide whether the solo isolate / solo lock button visibility should
2376 * be overridden from that configured by the user. We do this for the master bus.
2378 * @return optional value that is present if visibility state should be overridden.
2380 boost::optional<bool>
2381 MixerStrip::override_solo_visibility () const
2383 if (_route && _route->is_master ()) {
2384 return boost::optional<bool> (false);
2387 return boost::optional<bool> ();
2391 MixerStrip::add_input_port (DataType t)
2393 _route->input()->add_port ("", this, t);
2397 MixerStrip::add_output_port (DataType t)
2399 _route->output()->add_port ("", this, t);
2403 MixerStrip::route_active_changed ()
2405 reset_strip_style ();
2409 MixerStrip::copy_processors ()
2411 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2415 MixerStrip::cut_processors ()
2417 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2421 MixerStrip::paste_processors ()
2423 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2427 MixerStrip::select_all_processors ()
2429 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2433 MixerStrip::deselect_all_processors ()
2435 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2439 MixerStrip::delete_processors ()
2441 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2445 MixerStrip::toggle_processors ()
2447 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2451 MixerStrip::ab_plugins ()
2453 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2457 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2459 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2462 if (ev->button == 3) {
2463 popup_level_meter_menu (ev);
2471 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2473 using namespace Gtk::Menu_Helpers;
2475 Gtk::Menu* m = manage (new Menu);
2476 MenuList& items = m->items ();
2478 RadioMenuItem::Group group;
2480 _suspend_menu_callbacks = true;
2481 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2482 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2483 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2484 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2485 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2487 if (gpm.meter_channels().n_audio() == 0) {
2488 m->popup (ev->button, ev->time);
2489 _suspend_menu_callbacks = false;
2493 RadioMenuItem::Group tgroup;
2494 items.push_back (SeparatorElem());
2496 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2497 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2498 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2499 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2500 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2501 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2502 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2503 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2504 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2505 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2506 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2509 if (_route->is_master()) {
2512 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2513 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2514 /* non-master bus */
2517 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2524 MeterType cmt = _route->meter_type();
2525 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2527 items.push_back (SeparatorElem());
2528 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2529 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2530 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2531 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2532 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2533 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2535 m->popup (ev->button, ev->time);
2536 _suspend_menu_callbacks = false;
2540 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2541 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2543 using namespace Menu_Helpers;
2545 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2546 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2547 i->set_active (_route->meter_point() == point);
2551 MixerStrip::set_meter_point (MeterPoint p)
2553 if (_suspend_menu_callbacks) return;
2554 _route->set_meter_point (p);
2558 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2559 RadioMenuItem::Group& group, string const & name, MeterType type)
2561 using namespace Menu_Helpers;
2563 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2564 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2565 i->set_active (_route->meter_type() == type);
2569 MixerStrip::set_meter_type (MeterType t)
2571 if (_suspend_menu_callbacks) return;
2576 MixerStrip::update_track_number_visibility ()
2578 DisplaySuspender ds;
2579 bool show_label = _session->config.get_track_name_number();
2581 if (_route && _route->is_master()) {
2586 number_label.show ();
2587 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2588 // except the width of the number label is subtracted from the name-hbox, so we
2589 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2590 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2592 number_label.set_size_request(tnw, -1);
2593 number_label.show ();
2595 number_label.hide ();
2600 MixerStrip::color () const
2602 return route_color ();
2606 MixerStrip::marked_for_display () const
2608 return !_route->presentation_info().hidden();
2612 MixerStrip::set_marked_for_display (bool yn)
2614 return RouteUI::mark_hidden (!yn);