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/meter.h"
42 #include "ardour/midi_track.h"
43 #include "ardour/pannable.h"
44 #include "ardour/panner.h"
45 #include "ardour/panner_shell.h"
46 #include "ardour/panner_manager.h"
47 #include "ardour/port.h"
48 #include "ardour/profile.h"
49 #include "ardour/route.h"
50 #include "ardour/route_group.h"
51 #include "ardour/send.h"
52 #include "ardour/session.h"
53 #include "ardour/types.h"
54 #include "ardour/user_bundle.h"
56 #include "ardour_window.h"
57 #include "mixer_strip.h"
60 #include "ardour_button.h"
61 #include "public_editor.h"
63 #include "io_selector.h"
65 #include "gui_thread.h"
66 #include "route_group_menu.h"
67 #include "meter_patterns.h"
69 #include "ui_config.h"
73 using namespace ARDOUR;
74 using namespace ARDOUR_UI_UTILS;
77 using namespace Gtkmm2ext;
79 using namespace ArdourMeter;
81 MixerStrip* MixerStrip::_entered_mixer_strip;
83 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
85 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
89 , _mixer_owned (in_mixer)
90 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
93 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
94 , rec_mon_table (2, 2)
95 , solo_iso_table (1, 2)
96 , mute_solo_table (1, 2)
97 , bottom_button_table (1, 3)
98 , meter_point_button (_("pre"))
99 , monitor_section_button (0)
100 , midi_input_enable_button (0)
101 , _comment_button (_("Comments"))
102 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
103 , _visibility (X_("mixer-element-visibility"))
108 /* the editor mixer strip: don't destroy it every time
109 the underlying route goes away.
112 self_destruct = false;
116 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
120 , _mixer_owned (in_mixer)
121 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
124 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
125 , rec_mon_table (2, 2)
126 , solo_iso_table (1, 2)
127 , mute_solo_table (1, 2)
128 , bottom_button_table (1, 3)
129 , meter_point_button (_("pre"))
130 , monitor_section_button (0)
131 , midi_input_enable_button (0)
132 , _comment_button (_("Comments"))
133 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
134 , _visibility (X_("mixer-element-visibility"))
143 _entered_mixer_strip= 0;
146 ignore_comment_edit = false;
147 ignore_toggle = false;
152 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
153 longest_label = "longest label";
155 string t = _("Click to toggle the width of this mixer strip.");
157 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
160 width_button.set_icon (ArdourIcon::StripWidth);
161 set_tooltip (width_button, t);
163 hide_button.set_icon (ArdourIcon::CloseCross);
164 set_tooltip (&hide_button, _("Hide this mixer strip"));
166 input_button_box.set_spacing(2);
168 input_button.set_text (_("Input"));
169 input_button.set_name ("mixer strip button");
170 input_button_box.pack_start (input_button, true, true);
172 output_button.set_text (_("Output"));
173 output_button.set_name ("mixer strip button");
175 set_tooltip (&meter_point_button, _("Click to select metering point"));
176 meter_point_button.set_name ("mixer strip button");
178 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
180 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
181 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
183 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
185 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
186 solo_isolated_led->show ();
187 solo_isolated_led->set_no_show_all (true);
188 solo_isolated_led->set_name (X_("solo isolate"));
189 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
190 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
191 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
193 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
194 solo_safe_led->show ();
195 solo_safe_led->set_no_show_all (true);
196 solo_safe_led->set_name (X_("solo safe"));
197 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
198 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
199 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
201 solo_safe_led->set_text (S_("SoloLock|Lock"));
202 solo_isolated_led->set_text (_("Iso"));
204 solo_iso_table.set_homogeneous (true);
205 solo_iso_table.set_spacings (2);
206 if (!ARDOUR::Profile->get_trx()) {
207 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
208 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
210 solo_iso_table.show ();
212 rec_mon_table.set_homogeneous (true);
213 rec_mon_table.set_row_spacings (2);
214 rec_mon_table.set_col_spacings (2);
215 if (ARDOUR::Profile->get_mixbus()) {
216 rec_mon_table.resize (1, 3);
217 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
218 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
219 } else if (!ARDOUR::Profile->get_trx()) {
220 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
221 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
223 rec_mon_table.show ();
225 if (solo_isolated_led) {
226 button_size_group->add_widget (*solo_isolated_led);
229 button_size_group->add_widget (*solo_safe_led);
232 if (!ARDOUR::Profile->get_mixbus()) {
233 if (rec_enable_button) {
234 button_size_group->add_widget (*rec_enable_button);
236 if (monitor_disk_button) {
237 button_size_group->add_widget (*monitor_disk_button);
239 if (monitor_input_button) {
240 button_size_group->add_widget (*monitor_input_button);
244 mute_solo_table.set_homogeneous (true);
245 mute_solo_table.set_spacings (2);
247 bottom_button_table.set_spacings (2);
248 bottom_button_table.set_homogeneous (true);
249 bottom_button_table.attach (group_button, 1, 2, 0, 1);
250 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
252 name_button.set_name ("mixer strip button");
253 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
254 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
256 set_tooltip (&group_button, _("Mix group"));
257 group_button.set_name ("mixer strip button");
259 _comment_button.set_name (X_("mixer strip button"));
260 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
262 // TODO implement ArdourKnob::on_size_request properly
263 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
264 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
266 trim_control.set_tooltip_prefix (_("Trim: "));
267 trim_control.set_name ("trim knob");
268 trim_control.set_no_show_all (true);
269 input_button_box.pack_start (trim_control, false, false);
271 global_vpacker.set_border_width (1);
272 global_vpacker.set_spacing (0);
274 width_button.set_name ("mixer strip button");
275 hide_button.set_name ("mixer strip button");
277 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
278 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
280 // width_hide_box.set_border_width (1);
281 width_hide_box.set_spacing (2);
282 width_hide_box.pack_start (width_button, false, true);
283 width_hide_box.pack_start (number_label, true, true);
284 width_hide_box.pack_end (hide_button, false, true);
286 number_label.set_text ("-");
287 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
288 number_label.set_no_show_all ();
289 number_label.set_name ("tracknumber label");
290 number_label.set_fixed_colors (0x80808080, 0x80808080);
291 number_label.set_alignment (.5, .5);
292 number_label.set_fallthrough_to_parent (true);
294 global_vpacker.set_spacing (2);
295 if (!ARDOUR::Profile->get_trx()) {
296 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
298 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
299 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
300 global_vpacker.pack_start (processor_box, true, true);
302 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
305 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
306 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
308 if (!ARDOUR::Profile->get_trx()) {
309 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
315 global_frame.add (global_vpacker);
316 global_frame.set_shadow_type (Gtk::SHADOW_IN);
317 global_frame.set_name ("BaseFrame");
321 /* force setting of visible selected status */
324 set_selected (false);
329 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
330 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
332 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
333 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
334 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
336 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
337 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
339 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
340 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
341 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
343 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
345 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
346 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
348 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
352 /* start off as a passthru strip. we'll correct this, if necessary,
353 in update_diskstream_display().
356 /* start off as a passthru strip. we'll correct this, if necessary,
357 in update_diskstream_display().
360 if (is_midi_track()) {
361 set_name ("MidiTrackStripBase");
363 set_name ("AudioTrackStripBase");
366 add_events (Gdk::BUTTON_RELEASE_MASK|
367 Gdk::ENTER_NOTIFY_MASK|
368 Gdk::LEAVE_NOTIFY_MASK|
370 Gdk::KEY_RELEASE_MASK);
372 set_flags (get_flags() | Gtk::CAN_FOCUS);
374 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
375 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
378 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
379 must be the same as those used in RCOptionEditor so that the configuration changes
380 are recognised when they occur.
382 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
383 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
384 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
385 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
386 _visibility.add (&output_button, X_("Output"), _("Output"), false);
387 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
389 parameter_changed (X_("mixer-element-visibility"));
390 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
391 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
392 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
394 //watch for mouse enter/exit so we can do some stuff
395 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
396 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
398 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
401 MixerStrip::~MixerStrip ()
403 CatchDeletion (this);
405 if (this ==_entered_mixer_strip)
406 _entered_mixer_strip = NULL;
410 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
412 _entered_mixer_strip = this;
414 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
415 //because the mixerstrip control is a parent that encompasses the strip
416 deselect_all_processors();
422 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
424 //if we have moved outside our strip, but not into a child view, then deselect ourselves
425 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
426 _entered_mixer_strip= 0;
428 //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
429 gpm.gain_display.set_sensitive(false);
431 gpm.gain_display.set_sensitive(true);
433 //if we leave this mixer strip we need to clear out any selections
434 //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
441 MixerStrip::set_route (boost::shared_ptr<Route> rt)
443 //the rec/monitor stuff only shows up for tracks.
444 //the show_sends only shows up for buses.
445 //remove them all here, and we may add them back later
446 if (show_sends_button->get_parent()) {
447 rec_mon_table.remove (*show_sends_button);
449 if (rec_enable_button->get_parent()) {
450 rec_mon_table.remove (*rec_enable_button);
452 if (monitor_input_button->get_parent()) {
453 rec_mon_table.remove (*monitor_input_button);
455 if (monitor_disk_button->get_parent()) {
456 rec_mon_table.remove (*monitor_disk_button);
458 if (group_button.get_parent()) {
459 bottom_button_table.remove (group_button);
462 RouteUI::set_route (rt);
464 /* ProcessorBox needs access to _route so that it can read
467 processor_box.set_route (rt);
469 revert_to_default_display ();
471 /* unpack these from the parent and stuff them into our own
475 if (gpm.peak_display.get_parent()) {
476 gpm.peak_display.get_parent()->remove (gpm.peak_display);
478 if (gpm.gain_display.get_parent()) {
479 gpm.gain_display.get_parent()->remove (gpm.gain_display);
482 gpm.set_type (rt->meter_type());
484 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
485 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
487 if (solo_button->get_parent()) {
488 mute_solo_table.remove (*solo_button);
491 if (mute_button->get_parent()) {
492 mute_solo_table.remove (*mute_button);
495 if (route()->is_master()) {
496 solo_button->hide ();
497 mute_button->show ();
498 rec_mon_table.hide ();
499 if (solo_iso_table.get_parent()) {
500 solo_iso_table.get_parent()->remove(solo_iso_table);
502 if (monitor_section_button == 0) {
503 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
504 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
506 monitor_section_button = manage (new ArdourButton);
508 monitor_section_button->set_related_action (act);
509 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
510 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
511 monitor_section_button->show();
512 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
514 parameter_changed ("use-monitor-bus");
516 bottom_button_table.attach (group_button, 1, 2, 0, 1);
517 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
518 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
519 mute_button->show ();
520 solo_button->show ();
521 rec_mon_table.show ();
524 if (_mixer_owned && route()->is_master() ) {
526 HScrollbar scrollbar;
527 Gtk::Requisition requisition(scrollbar.size_request ());
528 int scrollbar_height = requisition.height;
530 spacer = manage (new EventBox);
531 spacer->set_size_request (-1, scrollbar_height+2);
532 global_vpacker.pack_start (*spacer, false, false);
537 monitor_input_button->show ();
538 monitor_disk_button->show ();
540 monitor_input_button->hide();
541 monitor_disk_button->hide ();
544 if (route()->trim() && route()->trim()->active()) {
545 trim_control.show ();
546 trim_control.set_controllable (route()->trim()->gain_control());
548 trim_control.hide ();
549 boost::shared_ptr<Controllable> none;
550 trim_control.set_controllable (none);
553 if (is_midi_track()) {
554 if (midi_input_enable_button == 0) {
555 midi_input_enable_button = manage (new ArdourButton);
556 midi_input_enable_button->set_name ("midi input button");
557 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
558 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
559 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
560 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
561 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
563 input_button_box.remove (*midi_input_enable_button);
565 /* get current state */
566 midi_input_status_changed ();
567 input_button_box.pack_start (*midi_input_enable_button, false, false);
569 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
571 if (midi_input_enable_button) {
572 /* removal from the container will delete it */
573 input_button_box.remove (*midi_input_enable_button);
574 midi_input_enable_button = 0;
578 if (is_audio_track()) {
579 boost::shared_ptr<AudioTrack> at = audio_track();
580 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
585 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
586 rec_enable_button->show();
588 if (ARDOUR::Profile->get_mixbus()) {
589 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
590 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
591 } else if (ARDOUR::Profile->get_trx()) {
592 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
594 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
595 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
602 if (!_route->is_master()) {
603 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
604 show_sends_button->show();
608 meter_point_button.set_text (meter_point_string (_route->meter_point()));
610 delete route_ops_menu;
613 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
614 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
615 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
616 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
618 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
620 if (_route->panner_shell()) {
621 update_panner_choices();
622 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
625 if (is_audio_track()) {
626 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
629 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
630 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
632 set_stuff_from_route ();
634 /* now force an update of all the various elements */
636 update_mute_display ();
637 update_solo_display ();
640 route_group_changed ();
643 panners.setup_pan ();
645 if (has_audio_outputs ()) {
651 update_diskstream_display ();
652 update_input_display ();
653 update_output_display ();
655 add_events (Gdk::BUTTON_RELEASE_MASK);
657 processor_box.show ();
659 if (!route()->is_master() && !route()->is_monitor()) {
660 /* we don't allow master or control routes to be hidden */
665 gpm.reset_peak_display ();
666 gpm.gain_display.show ();
667 gpm.peak_display.show ();
670 width_hide_box.show();
672 global_vpacker.show();
673 mute_solo_table.show();
674 bottom_button_table.show();
676 meter_point_button.show();
677 input_button_box.show_all();
678 output_button.show();
680 _comment_button.show();
682 gpm.gain_automation_state_button.show();
684 parameter_changed ("mixer-element-visibility");
691 MixerStrip::set_stuff_from_route ()
693 /* if width is not set, it will be set by the MixerUI or editor */
695 string str = gui_property ("strip-width");
697 set_width_enum (Width (string_2_enum (str, _width)), this);
702 MixerStrip::set_width_enum (Width w, void* owner)
704 /* always set the gpm width again, things may be hidden */
707 panners.set_width (w);
709 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
711 _width_owner = owner;
715 if (_width_owner == this) {
716 set_gui_property ("strip-width", enum_2_string (_width));
721 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
726 if (show_sends_button) {
727 show_sends_button->set_text (_("Aux"));
730 gpm.gain_automation_style_button.set_text (
731 gpm.astyle_string(gain_automation->automation_style()));
732 gpm.gain_automation_state_button.set_text (
733 gpm.astate_string(gain_automation->automation_state()));
735 if (_route->panner()) {
736 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
737 panners.astyle_string(_route->panner()->automation_style()));
738 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
739 panners.astate_string(_route->panner()->automation_state()));
743 // panners expect an even number of horiz. pixels
744 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
746 set_size_request (width, -1);
752 if (show_sends_button) {
753 show_sends_button->set_text (_("Snd"));
756 gpm.gain_automation_style_button.set_text (
757 gpm.short_astyle_string(gain_automation->automation_style()));
758 gpm.gain_automation_state_button.set_text (
759 gpm.short_astate_string(gain_automation->automation_state()));
760 gain_meter().setup_meters (); // recalc meter width
762 if (_route->panner()) {
763 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
764 panners.short_astyle_string(_route->panner()->automation_style()));
765 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
766 panners.short_astate_string(_route->panner()->automation_state()));
770 // panners expect an even number of horiz. pixels
771 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
773 set_size_request (width, -1);
778 processor_box.set_width (w);
780 update_input_display ();
781 update_output_display ();
782 setup_comment_button ();
783 route_group_changed ();
789 MixerStrip::set_packed (bool yn)
794 set_gui_property ("visible", true);
796 set_gui_property ("visible", false);
801 struct RouteCompareByName {
802 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
803 return a->name().compare (b->name()) < 0;
808 MixerStrip::output_release (GdkEventButton *ev)
810 switch (ev->button) {
812 edit_output_configuration ();
820 MixerStrip::output_press (GdkEventButton *ev)
822 using namespace Menu_Helpers;
823 if (!_session->engine().connected()) {
824 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
829 MenuList& citems = output_menu.items();
830 switch (ev->button) {
833 return false; //wait for the mouse-up to pop the dialog
837 output_menu.set_name ("ArdourContextMenu");
839 output_menu_bundles.clear ();
841 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
843 citems.push_back (SeparatorElem());
844 uint32_t const n_with_separator = citems.size ();
846 ARDOUR::BundleList current = _route->output()->bundles_connected ();
848 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
850 /* give user bundles first chance at being in the menu */
852 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
853 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
854 maybe_add_bundle_to_output_menu (*i, current);
858 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
859 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
860 maybe_add_bundle_to_output_menu (*i, current);
864 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
865 RouteList copy = *routes;
866 copy.sort (RouteCompareByName ());
867 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
868 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
871 if (citems.size() == n_with_separator) {
872 /* no routes added; remove the separator */
876 if (!ARDOUR::Profile->get_mixbus()) {
877 citems.push_back (SeparatorElem());
879 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
882 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
883 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
889 citems.push_back (SeparatorElem());
890 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
892 output_menu.popup (1, ev->time);
903 MixerStrip::input_release (GdkEventButton *ev)
905 switch (ev->button) {
908 edit_input_configuration ();
920 MixerStrip::input_press (GdkEventButton *ev)
922 using namespace Menu_Helpers;
924 MenuList& citems = input_menu.items();
925 input_menu.set_name ("ArdourContextMenu");
928 if (!_session->engine().connected()) {
929 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
934 if (_session->actively_recording() && _route->record_enabled())
937 switch (ev->button) {
940 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
944 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
946 citems.push_back (SeparatorElem());
947 uint32_t const n_with_separator = citems.size ();
949 input_menu_bundles.clear ();
951 ARDOUR::BundleList current = _route->input()->bundles_connected ();
953 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
955 /* give user bundles first chance at being in the menu */
957 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
958 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
959 maybe_add_bundle_to_input_menu (*i, current);
963 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
964 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
965 maybe_add_bundle_to_input_menu (*i, current);
969 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
970 RouteList copy = *routes;
971 copy.sort (RouteCompareByName ());
972 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
973 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
976 if (citems.size() == n_with_separator) {
977 /* no routes added; remove the separator */
981 citems.push_back (SeparatorElem());
982 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
985 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
986 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
991 citems.push_back (SeparatorElem());
992 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
994 input_menu.popup (1, ev->time);
1005 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1007 if (ignore_toggle) {
1011 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1013 if (std::find (current.begin(), current.end(), c) == current.end()) {
1014 _route->input()->connect_ports_to_bundle (c, true, this);
1016 _route->input()->disconnect_ports_from_bundle (c, this);
1021 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1023 if (ignore_toggle) {
1027 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1029 if (std::find (current.begin(), current.end(), c) == current.end()) {
1030 _route->output()->connect_ports_to_bundle (c, true, this);
1032 _route->output()->disconnect_ports_from_bundle (c, this);
1037 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1039 using namespace Menu_Helpers;
1041 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1045 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1046 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1050 if (i != input_menu_bundles.end()) {
1054 input_menu_bundles.push_back (b);
1056 MenuList& citems = input_menu.items();
1058 std::string n = b->name ();
1059 replace_all (n, "_", " ");
1061 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1065 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1067 using namespace Menu_Helpers;
1069 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1073 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1074 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1078 if (i != output_menu_bundles.end()) {
1082 output_menu_bundles.push_back (b);
1084 MenuList& citems = output_menu.items();
1086 std::string n = b->name ();
1087 replace_all (n, "_", " ");
1089 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1093 MixerStrip::update_diskstream_display ()
1095 if (is_track() && input_selector) {
1096 input_selector->hide_all ();
1099 route_color_changed ();
1103 MixerStrip::connect_to_pan ()
1105 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1107 panstate_connection.disconnect ();
1108 panstyle_connection.disconnect ();
1110 if (!_route->panner()) {
1114 boost::shared_ptr<Pannable> p = _route->pannable ();
1116 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1117 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1119 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1120 * However, that only works a panner was previously set.
1122 * PannerUI must remain subscribed to _panshell->Changed() in case
1123 * we switch the panner eg. AUX-Send and back
1124 * _route->panner_shell()->Changed() vs _panshell->Changed
1126 if (panners._panner == 0) {
1127 panners.panshell_changed ();
1129 update_panner_choices();
1133 MixerStrip::update_panner_choices ()
1135 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1136 if (!_route->panner_shell()) { return; }
1138 uint32_t in = _route->output()->n_ports().n_audio();
1140 if (_route->panner()) {
1141 in = _route->panner()->in().n_audio();
1144 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1148 * Output port labelling
1149 * =====================
1151 * Case 1: Each output has one connection, all connections are to system:playback_%i
1152 * out 1 -> system:playback_1
1153 * out 2 -> system:playback_2
1154 * out 3 -> system:playback_3
1157 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1158 * out 1 -> ardour:track_x/in 1
1159 * out 2 -> ardour:track_x/in 2
1160 * Display as: track_x
1162 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1163 * out 1 -> program x:foo
1164 * out 2 -> program x:foo
1165 * Display as: program x
1167 * Case 4: No connections (Disconnected)
1170 * Default case (unusual routing):
1171 * Display as: *number of connections*
1175 * .-----------------------------------------------.
1177 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1178 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1179 * '-----------------------------------------------'
1180 * .-----------------------------------------------.
1183 * '-----------------------------------------------'
1187 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1191 boost::shared_ptr<Port> port;
1192 vector<string> port_connections;
1194 uint32_t total_connection_count = 0;
1195 uint32_t io_connection_count = 0;
1196 uint32_t ardour_connection_count = 0;
1197 uint32_t system_connection_count = 0;
1198 uint32_t other_connection_count = 0;
1199 uint32_t typed_connection_count = 0;
1201 ostringstream label;
1203 bool have_label = false;
1204 bool each_io_has_one_connection = true;
1206 string connection_name;
1207 string ardour_track_name;
1208 string other_connection_type;
1209 string system_ports;
1212 ostringstream tooltip;
1213 char * tooltip_cstr;
1215 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1216 DataType dt = DataType::AUDIO;
1217 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1218 dt = DataType::MIDI;
1219 // avoid further confusion with Midi-tracks that have a synth.
1220 // Audio-ports may be connected, but button says "Disconnected"
1221 tooltip << _("MIDI ");
1225 io_count = route->n_inputs().n_total();
1226 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1228 io_count = route->n_outputs().n_total();
1229 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1233 for (io_index = 0; io_index < io_count; ++io_index) {
1235 port = route->input()->nth (io_index);
1237 port = route->output()->nth (io_index);
1240 port_connections.clear ();
1241 port->get_connections(port_connections);
1243 //ignore any port connections that don't match our DataType
1244 if (port->type() != dt) {
1245 if (!port_connections.empty()) {
1246 ++typed_connection_count;
1251 io_connection_count = 0;
1253 if (!port_connections.empty()) {
1254 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1256 string& connection_name (*i);
1258 if (connection_name.find("system:") == 0) {
1259 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1262 if (io_connection_count == 0) {
1263 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1265 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1268 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1271 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1272 if (ardour_track_name.empty()) {
1273 // "ardour:Master/in 1" -> "ardour:Master/"
1274 string::size_type slash = connection_name.find("/");
1275 if (slash != string::npos) {
1276 ardour_track_name = connection_name.substr(0, slash + 1);
1280 if (connection_name.find(ardour_track_name) == 0) {
1281 ++ardour_connection_count;
1283 } else if (!pn.empty()) {
1284 if (system_ports.empty()) {
1287 system_ports += "/" + pn;
1289 if (connection_name.find("system:") == 0) {
1290 ++system_connection_count;
1292 } else if (connection_name.find("system:midi_") == 0) {
1294 // "system:midi_capture_123" -> "123"
1295 system_port = "M " + connection_name.substr(20);
1297 // "system:midi_playback_123" -> "123"
1298 system_port = "M " + connection_name.substr(21);
1301 if (system_ports.empty()) {
1302 system_ports += system_port;
1304 system_ports += "/" + system_port;
1307 ++system_connection_count;
1309 } else if (connection_name.find("system:") == 0) {
1311 // "system:capture_123" -> "123"
1312 system_port = connection_name.substr(15);
1314 // "system:playback_123" -> "123"
1315 system_port = connection_name.substr(16);
1318 if (system_ports.empty()) {
1319 system_ports += system_port;
1321 system_ports += "/" + system_port;
1324 ++system_connection_count;
1326 if (other_connection_type.empty()) {
1327 // "jamin:in 1" -> "jamin:"
1328 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1331 if (connection_name.find(other_connection_type) == 0) {
1332 ++other_connection_count;
1336 ++total_connection_count;
1337 ++io_connection_count;
1341 if (io_connection_count != 1) {
1342 each_io_has_one_connection = false;
1346 if (total_connection_count == 0) {
1347 tooltip << endl << _("Disconnected");
1350 tooltip_cstr = new char[tooltip.str().size() + 1];
1351 strcpy(tooltip_cstr, tooltip.str().c_str());
1354 set_tooltip (&input_button, tooltip_cstr);
1356 set_tooltip (&output_button, tooltip_cstr);
1359 delete [] tooltip_cstr;
1361 if (each_io_has_one_connection) {
1362 if (total_connection_count == ardour_connection_count) {
1363 // all connections are to the same track in ardour
1364 // "ardour:Master/" -> "Master"
1365 string::size_type slash = ardour_track_name.find("/");
1366 if (slash != string::npos) {
1367 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1368 label << ardour_track_name.substr (ppps, slash - ppps);
1372 else if (total_connection_count == system_connection_count) {
1373 // all connections are to system ports
1374 label << system_ports;
1377 else if (total_connection_count == other_connection_count) {
1378 // all connections are to the same external program eg jamin
1379 // "jamin:" -> "jamin"
1380 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1386 if (total_connection_count == 0) {
1390 // Odd configuration
1391 label << "*" << total_connection_count << "*";
1393 if (typed_connection_count > 0) {
1394 label << "\u2295"; // circled plus
1399 input_button.set_text (label.str());
1401 output_button.set_text (label.str());
1406 MixerStrip::update_input_display ()
1408 update_io_button (_route, _width, true);
1409 panners.setup_pan ();
1411 if (has_audio_outputs ()) {
1412 panners.show_all ();
1414 panners.hide_all ();
1420 MixerStrip::update_output_display ()
1422 update_io_button (_route, _width, false);
1423 gpm.setup_meters ();
1424 panners.setup_pan ();
1426 if (has_audio_outputs ()) {
1427 panners.show_all ();
1429 panners.hide_all ();
1434 MixerStrip::fast_update ()
1436 gpm.update_meters ();
1440 MixerStrip::diskstream_changed ()
1442 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1446 MixerStrip::io_changed_proxy ()
1448 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1452 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1454 boost::shared_ptr<Port> a = wa.lock ();
1455 boost::shared_ptr<Port> b = wb.lock ();
1457 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1458 update_input_display ();
1459 set_width_enum (_width, this);
1462 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1463 update_output_display ();
1464 set_width_enum (_width, this);
1469 MixerStrip::setup_comment_button ()
1474 if (_route->comment().empty ()) {
1475 _comment_button.unset_bg (STATE_NORMAL);
1476 _comment_button.set_text (_("Comments"));
1478 _comment_button.modify_bg (STATE_NORMAL, color ());
1479 _comment_button.set_text (_("*Comments*"));
1484 if (_route->comment().empty ()) {
1485 _comment_button.unset_bg (STATE_NORMAL);
1486 _comment_button.set_text (_("Cmt"));
1488 _comment_button.modify_bg (STATE_NORMAL, color ());
1489 _comment_button.set_text (_("*Cmt*"));
1495 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1501 MixerStrip::select_route_group (GdkEventButton *ev)
1503 using namespace Menu_Helpers;
1505 if (ev->button == 1) {
1507 if (group_menu == 0) {
1509 PropertyList* plist = new PropertyList();
1511 plist->add (Properties::gain, true);
1512 plist->add (Properties::mute, true);
1513 plist->add (Properties::solo, true);
1515 group_menu = new RouteGroupMenu (_session, plist);
1519 r.push_back (route ());
1520 group_menu->build (r);
1521 group_menu->menu()->popup (1, ev->time);
1528 MixerStrip::route_group_changed ()
1530 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1532 RouteGroup *rg = _route->route_group();
1535 group_button.set_text (PBD::short_version (rg->name(), 5));
1539 group_button.set_text (_("Grp"));
1542 group_button.set_text (_("~G"));
1549 MixerStrip::route_color_changed ()
1551 name_button.modify_bg (STATE_NORMAL, color());
1552 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1553 reset_strip_style ();
1557 MixerStrip::show_passthru_color ()
1559 reset_strip_style ();
1563 MixerStrip::build_route_ops_menu ()
1565 using namespace Menu_Helpers;
1566 route_ops_menu = new Menu;
1567 route_ops_menu->set_name ("ArdourContextMenu");
1569 MenuList& items = route_ops_menu->items();
1571 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1573 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1575 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1577 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1579 items.push_back (SeparatorElem());
1581 if (!_route->is_master()) {
1582 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1584 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1585 rename_menu_item = &items.back();
1587 items.push_back (SeparatorElem());
1588 items.push_back (CheckMenuElem (_("Active")));
1589 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1590 i->set_active (_route->active());
1591 i->set_sensitive(! _session->transport_rolling());
1592 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1594 if (!Profile->get_mixbus ()) {
1595 items.push_back (SeparatorElem());
1596 items.push_back (CheckMenuElem (_("Strict I/O")));
1597 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1598 i->set_active (_route->strict_io());
1599 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1602 if (1 /* TODO IFF >= 1 plugin-insert */) {
1603 items.push_back (SeparatorElem());
1604 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1607 items.push_back (SeparatorElem());
1608 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1610 items.push_back (SeparatorElem());
1611 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1612 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1613 denormal_menu_item->set_active (_route->denormal_protection());
1615 items.push_back (SeparatorElem());
1616 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1619 /* note that this relies on selection being shared across editor and
1620 mixer (or global to the backend, in the future), which is the only
1621 sane thing for users anyway.
1624 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1626 Selection& selection (PublicEditor::instance().get_selection());
1627 if (!selection.selected (rtav)) {
1628 selection.set (rtav);
1631 if (!_route->is_master()) {
1632 items.push_back (SeparatorElem());
1633 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1636 items.push_back (SeparatorElem());
1637 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1643 MixerStrip::name_button_button_press (GdkEventButton* ev)
1645 if (ev->button == 3) {
1646 list_route_operations ();
1648 /* do not allow rename if the track is record-enabled */
1649 rename_menu_item->set_sensitive (!_route->record_enabled());
1650 route_ops_menu->popup (1, ev->time);
1659 MixerStrip::name_button_button_release (GdkEventButton* ev)
1661 if (ev->button == 1) {
1662 list_route_operations ();
1664 /* do not allow rename if the track is record-enabled */
1665 rename_menu_item->set_sensitive (!_route->record_enabled());
1666 route_ops_menu->popup (1, ev->time);
1673 MixerStrip::number_button_button_press (GdkEventButton* ev)
1675 if ( ev->button == 3 ) {
1676 list_route_operations ();
1678 /* do not allow rename if the track is record-enabled */
1679 rename_menu_item->set_sensitive (!_route->record_enabled());
1680 route_ops_menu->popup (1, ev->time);
1689 MixerStrip::list_route_operations ()
1691 delete route_ops_menu;
1692 build_route_ops_menu ();
1696 MixerStrip::set_selected (bool yn)
1698 AxisView::set_selected (yn);
1700 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1701 global_frame.set_name ("MixerStripSelectedFrame");
1703 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1704 global_frame.set_name ("MixerStripFrame");
1706 global_frame.queue_draw ();
1709 // processor_box.deselect_all_processors();
1713 MixerStrip::property_changed (const PropertyChange& what_changed)
1715 RouteUI::property_changed (what_changed);
1717 if (what_changed.contains (ARDOUR::Properties::name)) {
1723 MixerStrip::name_changed ()
1727 name_button.set_text (_route->name());
1730 name_button.set_text (PBD::short_version (_route->name(), 5));
1734 set_tooltip (name_button, _route->name());
1736 if (_session->config.get_track_name_number()) {
1737 const int64_t track_number = _route->track_number ();
1738 if (track_number == 0) {
1739 number_label.set_text ("-");
1741 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1744 number_label.set_text ("");
1749 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1751 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1755 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1757 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1761 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1763 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1767 MixerStrip::width_button_pressed (GdkEventButton* ev)
1769 if (ev->button != 1) {
1773 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1776 _mixer.set_strip_width (Narrow, true);
1780 _mixer.set_strip_width (Wide, true);
1786 set_width_enum (Narrow, this);
1789 set_width_enum (Wide, this);
1798 MixerStrip::hide_clicked ()
1800 // LAME fix to reset the button status for when it is redisplayed (part 1)
1801 hide_button.set_sensitive(false);
1804 Hiding(); /* EMIT_SIGNAL */
1806 _mixer.hide_strip (this);
1810 hide_button.set_sensitive(true);
1814 MixerStrip::set_embedded (bool yn)
1820 MixerStrip::map_frozen ()
1822 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1824 boost::shared_ptr<AudioTrack> at = audio_track();
1827 switch (at->freeze_state()) {
1828 case AudioTrack::Frozen:
1829 processor_box.set_sensitive (false);
1830 hide_redirect_editors ();
1833 processor_box.set_sensitive (true);
1834 // XXX need some way, maybe, to retoggle redirect editors
1838 processor_box.set_sensitive (true);
1840 RouteUI::map_frozen ();
1844 MixerStrip::hide_redirect_editors ()
1846 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1850 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1852 boost::shared_ptr<Processor> processor (p.lock ());
1857 Gtk::Window* w = processor_box.get_processor_ui (processor);
1865 MixerStrip::reset_strip_style ()
1867 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1869 gpm.set_fader_name ("SendStripBase");
1873 if (is_midi_track()) {
1874 if (_route->active()) {
1875 set_name ("MidiTrackStripBase");
1877 set_name ("MidiTrackStripBaseInactive");
1879 gpm.set_fader_name ("MidiTrackFader");
1880 } else if (is_audio_track()) {
1881 if (_route->active()) {
1882 set_name ("AudioTrackStripBase");
1884 set_name ("AudioTrackStripBaseInactive");
1886 gpm.set_fader_name ("AudioTrackFader");
1888 if (_route->active()) {
1889 set_name ("AudioBusStripBase");
1891 set_name ("AudioBusStripBaseInactive");
1893 gpm.set_fader_name ("AudioBusFader");
1895 /* (no MIDI busses yet) */
1902 MixerStrip::engine_stopped ()
1907 MixerStrip::engine_running ()
1912 MixerStrip::meter_point_string (MeterPoint mp)
1925 case MeterPostFader:
1942 return S_("Meter|In");
1946 return S_("Meter|Pr");
1949 case MeterPostFader:
1950 return S_("Meter|Po");
1954 return S_("Meter|O");
1959 return S_("Meter|C");
1968 /** Called when the monitor-section state */
1970 MixerStrip::monitor_changed ()
1972 assert (monitor_section_button);
1973 if (_session->monitor_active()) {
1974 monitor_section_button->set_name ("master monitor section button active");
1976 monitor_section_button->set_name ("master monitor section button normal");
1980 /** Called when the metering point has changed */
1982 MixerStrip::meter_changed ()
1984 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1985 gpm.setup_meters ();
1986 // reset peak when meter point changes
1987 gpm.reset_peak_display();
1990 /** The bus that we are displaying sends to has changed, or been turned off.
1991 * @param send_to New bus that we are displaying sends to, or 0.
1994 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1996 RouteUI::bus_send_display_changed (send_to);
1999 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2004 revert_to_default_display ();
2007 revert_to_default_display ();
2012 MixerStrip::drop_send ()
2014 boost::shared_ptr<Send> current_send;
2016 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2017 current_send->set_metering (false);
2020 send_gone_connection.disconnect ();
2021 input_button.set_sensitive (true);
2022 output_button.set_sensitive (true);
2023 group_button.set_sensitive (true);
2024 set_invert_sensitive (true);
2025 meter_point_button.set_sensitive (true);
2026 mute_button->set_sensitive (true);
2027 solo_button->set_sensitive (true);
2028 solo_isolated_led->set_sensitive (true);
2029 solo_safe_led->set_sensitive (true);
2030 monitor_input_button->set_sensitive (true);
2031 monitor_disk_button->set_sensitive (true);
2032 _comment_button.set_sensitive (true);
2033 RouteUI::check_rec_enable_sensitivity ();
2034 set_button_names (); // update solo button visual state
2038 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2040 _current_delivery = d;
2041 DeliveryChanged (_current_delivery);
2045 MixerStrip::show_send (boost::shared_ptr<Send> send)
2051 set_current_delivery (send);
2053 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2054 send->set_metering (true);
2055 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2057 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2058 gain_meter().setup_meters ();
2060 uint32_t const in = _current_delivery->pans_required();
2061 uint32_t const out = _current_delivery->pan_outs();
2063 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2064 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2065 panner_ui().setup_pan ();
2066 panner_ui().set_send_drawing_mode (true);
2067 panner_ui().show_all ();
2069 input_button.set_sensitive (false);
2070 group_button.set_sensitive (false);
2071 set_invert_sensitive (false);
2072 meter_point_button.set_sensitive (false);
2073 mute_button->set_sensitive (false);
2074 solo_button->set_sensitive (false);
2075 rec_enable_button->set_sensitive (false);
2076 solo_isolated_led->set_sensitive (false);
2077 solo_safe_led->set_sensitive (false);
2078 monitor_input_button->set_sensitive (false);
2079 monitor_disk_button->set_sensitive (false);
2080 _comment_button.set_sensitive (false);
2082 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2083 output_button.set_sensitive (false);
2086 reset_strip_style ();
2090 MixerStrip::revert_to_default_display ()
2094 set_current_delivery (_route->main_outs ());
2096 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2097 gain_meter().setup_meters ();
2099 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2100 update_panner_choices();
2101 panner_ui().setup_pan ();
2102 panner_ui().set_send_drawing_mode (false);
2104 if (has_audio_outputs ()) {
2105 panners.show_all ();
2107 panners.hide_all ();
2110 reset_strip_style ();
2114 MixerStrip::set_button_names ()
2118 mute_button->set_text (_("Mute"));
2119 monitor_input_button->set_text (_("In"));
2120 monitor_disk_button->set_text (_("Disk"));
2121 if (monitor_section_button) {
2122 monitor_section_button->set_text (_("Mon"));
2125 if (_route && _route->solo_safe()) {
2126 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2128 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2130 if (!Config->get_solo_control_is_listen_control()) {
2131 solo_button->set_text (_("Solo"));
2133 switch (Config->get_listen_position()) {
2134 case AfterFaderListen:
2135 solo_button->set_text (_("AFL"));
2137 case PreFaderListen:
2138 solo_button->set_text (_("PFL"));
2142 solo_isolated_led->set_text (_("Iso"));
2143 solo_safe_led->set_text (S_("SoloLock|Lock"));
2147 mute_button->set_text (S_("Mute|M"));
2148 monitor_input_button->set_text (S_("MonitorInput|I"));
2149 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2150 if (monitor_section_button) {
2151 monitor_section_button->set_text (S_("Mon|O"));
2154 if (_route && _route->solo_safe()) {
2155 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2157 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2159 if (!Config->get_solo_control_is_listen_control()) {
2160 solo_button->set_text (S_("Solo|S"));
2162 switch (Config->get_listen_position()) {
2163 case AfterFaderListen:
2164 solo_button->set_text (S_("AfterFader|A"));
2166 case PreFaderListen:
2167 solo_button->set_text (S_("Prefader|P"));
2172 solo_isolated_led->set_text (S_("SoloIso|I"));
2173 solo_safe_led->set_text (S_("SoloLock|L"));
2178 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2180 meter_point_button.set_text ("");
2185 MixerStrip::plugin_selector()
2187 return _mixer.plugin_selector();
2191 MixerStrip::hide_things ()
2193 processor_box.hide_things ();
2197 MixerStrip::input_active_button_press (GdkEventButton*)
2199 /* nothing happens on press */
2204 MixerStrip::input_active_button_release (GdkEventButton* ev)
2206 boost::shared_ptr<MidiTrack> mt = midi_track ();
2212 boost::shared_ptr<RouteList> rl (new RouteList);
2214 rl->push_back (route());
2216 _session->set_exclusive_input_active (rl, !mt->input_active(),
2217 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2223 MixerStrip::midi_input_status_changed ()
2225 if (midi_input_enable_button) {
2226 boost::shared_ptr<MidiTrack> mt = midi_track ();
2228 midi_input_enable_button->set_active (mt->input_active ());
2233 MixerStrip::state_id () const
2235 return string_compose ("strip %1", _route->id().to_s());
2239 MixerStrip::parameter_changed (string p)
2241 if (p == _visibility.get_state_name()) {
2242 /* The user has made changes to the mixer strip visibility, so get
2243 our VisibilityGroup to reflect these changes in our widgets.
2245 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2247 else if (p == "track-name-number") {
2250 else if (p == "use-monitor-bus") {
2251 if (monitor_section_button) {
2252 if (mute_button->get_parent()) {
2253 mute_button->get_parent()->remove(*mute_button);
2255 if (monitor_section_button->get_parent()) {
2256 monitor_section_button->get_parent()->remove(*monitor_section_button);
2258 if (Config->get_use_monitor_bus ()) {
2259 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2260 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2261 mute_button->show();
2262 monitor_section_button->show();
2264 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2265 mute_button->show();
2271 /** Called to decide whether the solo isolate / solo lock button visibility should
2272 * be overridden from that configured by the user. We do this for the master bus.
2274 * @return optional value that is present if visibility state should be overridden.
2276 boost::optional<bool>
2277 MixerStrip::override_solo_visibility () const
2279 if (_route && _route->is_master ()) {
2280 return boost::optional<bool> (false);
2283 return boost::optional<bool> ();
2287 MixerStrip::add_input_port (DataType t)
2289 _route->input()->add_port ("", this, t);
2293 MixerStrip::add_output_port (DataType t)
2295 _route->output()->add_port ("", this, t);
2299 MixerStrip::route_active_changed ()
2301 reset_strip_style ();
2305 MixerStrip::copy_processors ()
2307 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2311 MixerStrip::cut_processors ()
2313 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2317 MixerStrip::paste_processors ()
2319 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2323 MixerStrip::select_all_processors ()
2325 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2329 MixerStrip::deselect_all_processors ()
2331 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2335 MixerStrip::delete_processors ()
2337 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2341 MixerStrip::toggle_processors ()
2343 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2347 MixerStrip::ab_plugins ()
2349 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2353 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2355 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2358 if (ev->button == 3) {
2359 popup_level_meter_menu (ev);
2367 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2369 using namespace Gtk::Menu_Helpers;
2371 Gtk::Menu* m = manage (new Menu);
2372 MenuList& items = m->items ();
2374 RadioMenuItem::Group group;
2376 _suspend_menu_callbacks = true;
2377 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2378 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2379 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2380 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2381 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2383 if (gpm.meter_channels().n_audio() == 0) {
2384 m->popup (ev->button, ev->time);
2385 _suspend_menu_callbacks = false;
2389 RadioMenuItem::Group tgroup;
2390 items.push_back (SeparatorElem());
2392 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2393 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2394 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2395 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2396 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2397 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2398 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2399 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2400 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2401 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2402 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2405 if (_route->is_master()) {
2408 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2409 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2410 /* non-master bus */
2413 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2420 MeterType cmt = _route->meter_type();
2421 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2423 items.push_back (SeparatorElem());
2424 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2425 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2426 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2427 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2428 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2429 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2431 m->popup (ev->button, ev->time);
2432 _suspend_menu_callbacks = false;
2436 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2437 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2439 using namespace Menu_Helpers;
2441 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2442 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2443 i->set_active (_route->meter_point() == point);
2447 MixerStrip::set_meter_point (MeterPoint p)
2449 if (_suspend_menu_callbacks) return;
2450 _route->set_meter_point (p);
2454 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2455 RadioMenuItem::Group& group, string const & name, MeterType type)
2457 using namespace Menu_Helpers;
2459 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2460 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2461 i->set_active (_route->meter_type() == type);
2465 MixerStrip::set_meter_type (MeterType t)
2467 if (_suspend_menu_callbacks) return;