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;
82 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
84 static const uint32_t n_vca_buttons = 4;
86 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
90 , _mixer_owned (in_mixer)
91 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
94 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
95 , rec_mon_table (2, 2)
96 , solo_iso_table (1, 2)
97 , mute_solo_table (1, 2)
98 , bottom_button_table (1, 3)
100 , meter_point_button (_("pre"))
101 , monitor_section_button (0)
102 , midi_input_enable_button (0)
103 , _comment_button (_("Comments"))
104 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
105 , _visibility (X_("mixer-element-visibility"))
110 /* the editor mixer strip: don't destroy it every time
111 the underlying route goes away.
114 self_destruct = false;
118 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
122 , _mixer_owned (in_mixer)
123 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
126 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
127 , rec_mon_table (2, 2)
128 , solo_iso_table (1, 2)
129 , mute_solo_table (1, 2)
130 , bottom_button_table (1, 3)
132 , meter_point_button (_("pre"))
133 , monitor_section_button (0)
134 , midi_input_enable_button (0)
135 , _comment_button (_("Comments"))
136 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
137 , _visibility (X_("mixer-element-visibility"))
146 _entered_mixer_strip= 0;
149 ignore_comment_edit = false;
150 ignore_toggle = false;
155 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
156 longest_label = "longest label";
158 string t = _("Click to toggle the width of this mixer strip.");
160 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
163 width_button.set_icon (ArdourIcon::StripWidth);
164 set_tooltip (width_button, t);
166 hide_button.set_icon (ArdourIcon::CloseCross);
167 set_tooltip (&hide_button, _("Hide this mixer strip"));
169 input_button_box.set_spacing(2);
171 input_button.set_text (_("Input"));
172 input_button.set_name ("mixer strip button");
173 input_button_box.pack_start (input_button, true, true);
175 output_button.set_text (_("Output"));
176 output_button.set_name ("mixer strip button");
178 set_tooltip (&meter_point_button, _("Click to select metering point"));
179 meter_point_button.set_name ("mixer strip button");
181 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
183 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
184 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
186 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
188 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
189 solo_isolated_led->show ();
190 solo_isolated_led->set_no_show_all (true);
191 solo_isolated_led->set_name (X_("solo isolate"));
192 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
193 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
194 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
196 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
197 solo_safe_led->show ();
198 solo_safe_led->set_no_show_all (true);
199 solo_safe_led->set_name (X_("solo safe"));
200 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
201 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
202 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
204 solo_safe_led->set_text (S_("SoloLock|Lock"));
205 solo_isolated_led->set_text (_("Iso"));
207 solo_iso_table.set_homogeneous (true);
208 solo_iso_table.set_spacings (2);
209 if (!ARDOUR::Profile->get_trx()) {
210 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
211 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
213 solo_iso_table.show ();
215 vca_table.set_homogeneous (true);
216 vca_table.set_spacings (1);
217 for (uint32_t n = 0; n < n_vca_buttons; ++n) {
218 ArdourButton* v = manage (new ArdourButton (ArdourButton::default_elements));
219 vca_buttons.push_back (v); /* no ownership transfer, button is managed by its container */
220 vca_table.attach (*v, n, n+1, 0, 1);
226 rec_mon_table.set_homogeneous (true);
227 rec_mon_table.set_row_spacings (2);
228 rec_mon_table.set_col_spacings (2);
229 if (ARDOUR::Profile->get_mixbus()) {
230 rec_mon_table.resize (1, 3);
231 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
232 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
233 } else if (!ARDOUR::Profile->get_trx()) {
234 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
235 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
237 rec_mon_table.show ();
239 if (solo_isolated_led) {
240 button_size_group->add_widget (*solo_isolated_led);
243 button_size_group->add_widget (*solo_safe_led);
246 if (!ARDOUR::Profile->get_mixbus()) {
247 if (rec_enable_button) {
248 button_size_group->add_widget (*rec_enable_button);
250 if (monitor_disk_button) {
251 button_size_group->add_widget (*monitor_disk_button);
253 if (monitor_input_button) {
254 button_size_group->add_widget (*monitor_input_button);
258 mute_solo_table.set_homogeneous (true);
259 mute_solo_table.set_spacings (2);
261 bottom_button_table.set_spacings (2);
262 bottom_button_table.set_homogeneous (true);
263 bottom_button_table.attach (group_button, 1, 2, 0, 1);
264 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
266 name_button.set_name ("mixer strip button");
267 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
268 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
270 set_tooltip (&group_button, _("Mix group"));
271 group_button.set_name ("mixer strip button");
273 _comment_button.set_name (X_("mixer strip button"));
274 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
276 // TODO implement ArdourKnob::on_size_request properly
277 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
278 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
280 trim_control.set_tooltip_prefix (_("Trim: "));
281 trim_control.set_name ("trim knob");
282 trim_control.set_no_show_all (true);
283 input_button_box.pack_start (trim_control, false, false);
285 global_vpacker.set_border_width (1);
286 global_vpacker.set_spacing (0);
288 width_button.set_name ("mixer strip button");
289 hide_button.set_name ("mixer strip button");
291 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
292 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
294 // width_hide_box.set_border_width (1);
295 width_hide_box.set_spacing (2);
296 width_hide_box.pack_start (width_button, false, true);
297 width_hide_box.pack_start (number_label, true, true);
298 width_hide_box.pack_end (hide_button, false, true);
300 number_label.set_text ("-");
301 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
302 number_label.set_no_show_all ();
303 number_label.set_name ("tracknumber label");
304 number_label.set_fixed_colors (0x80808080, 0x80808080);
305 number_label.set_alignment (.5, .5);
306 number_label.set_fallthrough_to_parent (true);
308 global_vpacker.set_spacing (2);
309 if (!ARDOUR::Profile->get_trx()) {
310 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
311 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
313 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
314 global_vpacker.pack_start (processor_box, true, true);
316 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
317 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
318 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
319 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
320 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
321 global_vpacker.pack_start (vca_table, Gtk::PACK_SHRINK);
322 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
323 if (!ARDOUR::Profile->get_trx()) {
324 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
325 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
327 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
330 global_frame.add (global_vpacker);
331 global_frame.set_shadow_type (Gtk::SHADOW_IN);
332 global_frame.set_name ("BaseFrame");
336 /* force setting of visible selected status */
339 set_selected (false);
344 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
345 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
347 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
348 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
349 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
351 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
352 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
354 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
355 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
356 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
358 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
360 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
361 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
363 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
367 /* start off as a passthru strip. we'll correct this, if necessary,
368 in update_diskstream_display().
371 /* start off as a passthru strip. we'll correct this, if necessary,
372 in update_diskstream_display().
375 if (is_midi_track()) {
376 set_name ("MidiTrackStripBase");
378 set_name ("AudioTrackStripBase");
381 add_events (Gdk::BUTTON_RELEASE_MASK|
382 Gdk::ENTER_NOTIFY_MASK|
383 Gdk::LEAVE_NOTIFY_MASK|
385 Gdk::KEY_RELEASE_MASK);
387 set_flags (get_flags() | Gtk::CAN_FOCUS);
389 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
390 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
393 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
394 must be the same as those used in RCOptionEditor so that the configuration changes
395 are recognised when they occur.
397 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
398 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
399 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
400 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
401 _visibility.add (&output_button, X_("Output"), _("Output"), false);
402 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
403 _visibility.add (&vca_table, X_("VCA"), _("VCA Assigns"), false);
405 parameter_changed (X_("mixer-element-visibility"));
406 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
407 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
408 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
410 //watch for mouse enter/exit so we can do some stuff
411 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
412 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
414 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
417 MixerStrip::~MixerStrip ()
419 CatchDeletion (this);
421 if (this ==_entered_mixer_strip)
422 _entered_mixer_strip = NULL;
426 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
428 _entered_mixer_strip = this;
430 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
431 //because the mixerstrip control is a parent that encompasses the strip
432 deselect_all_processors();
438 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
440 //if we have moved outside our strip, but not into a child view, then deselect ourselves
441 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
442 _entered_mixer_strip= 0;
444 //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
445 gpm.gain_display.set_sensitive(false);
447 gpm.gain_display.set_sensitive(true);
449 //if we leave this mixer strip we need to clear out any selections
450 //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
457 MixerStrip::set_route (boost::shared_ptr<Route> rt)
459 //the rec/monitor stuff only shows up for tracks.
460 //the show_sends only shows up for buses.
461 //remove them all here, and we may add them back later
462 if (show_sends_button->get_parent()) {
463 rec_mon_table.remove (*show_sends_button);
465 if (rec_enable_button->get_parent()) {
466 rec_mon_table.remove (*rec_enable_button);
468 if (monitor_input_button->get_parent()) {
469 rec_mon_table.remove (*monitor_input_button);
471 if (monitor_disk_button->get_parent()) {
472 rec_mon_table.remove (*monitor_disk_button);
474 if (group_button.get_parent()) {
475 bottom_button_table.remove (group_button);
478 RouteUI::set_route (rt);
480 /* ProcessorBox needs access to _route so that it can read
483 processor_box.set_route (rt);
485 revert_to_default_display ();
487 /* unpack these from the parent and stuff them into our own
491 if (gpm.peak_display.get_parent()) {
492 gpm.peak_display.get_parent()->remove (gpm.peak_display);
494 if (gpm.gain_display.get_parent()) {
495 gpm.gain_display.get_parent()->remove (gpm.gain_display);
498 gpm.set_type (rt->meter_type());
500 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
501 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
503 if (solo_button->get_parent()) {
504 mute_solo_table.remove (*solo_button);
507 if (mute_button->get_parent()) {
508 mute_solo_table.remove (*mute_button);
511 if (route()->is_master()) {
512 solo_button->hide ();
513 mute_button->show ();
514 rec_mon_table.hide ();
515 if (solo_iso_table.get_parent()) {
516 solo_iso_table.get_parent()->remove(solo_iso_table);
518 if (monitor_section_button == 0) {
519 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
520 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
522 monitor_section_button = manage (new ArdourButton);
524 monitor_section_button->set_related_action (act);
525 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
526 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
527 monitor_section_button->show();
528 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
530 parameter_changed ("use-monitor-bus");
532 bottom_button_table.attach (group_button, 1, 2, 0, 1);
533 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
534 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
535 mute_button->show ();
536 solo_button->show ();
537 rec_mon_table.show ();
540 if (_mixer_owned && route()->is_master() ) {
542 HScrollbar scrollbar;
543 Gtk::Requisition requisition(scrollbar.size_request ());
544 int scrollbar_height = requisition.height;
546 spacer = manage (new EventBox);
547 spacer->set_size_request (-1, scrollbar_height+2);
548 global_vpacker.pack_start (*spacer, false, false);
553 monitor_input_button->show ();
554 monitor_disk_button->show ();
556 monitor_input_button->hide();
557 monitor_disk_button->hide ();
560 if (route()->trim() && route()->trim()->active()) {
561 trim_control.show ();
562 trim_control.set_controllable (route()->trim()->gain_control());
564 trim_control.hide ();
565 boost::shared_ptr<Controllable> none;
566 trim_control.set_controllable (none);
569 if (is_midi_track()) {
570 if (midi_input_enable_button == 0) {
571 midi_input_enable_button = manage (new ArdourButton);
572 midi_input_enable_button->set_name ("midi input button");
573 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
574 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
575 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
576 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
577 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
579 input_button_box.remove (*midi_input_enable_button);
581 /* get current state */
582 midi_input_status_changed ();
583 input_button_box.pack_start (*midi_input_enable_button, false, false);
585 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
587 if (midi_input_enable_button) {
588 /* removal from the container will delete it */
589 input_button_box.remove (*midi_input_enable_button);
590 midi_input_enable_button = 0;
594 if (is_audio_track()) {
595 boost::shared_ptr<AudioTrack> at = audio_track();
596 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
601 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
602 rec_enable_button->show();
604 if (ARDOUR::Profile->get_mixbus()) {
605 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
606 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
607 } else if (ARDOUR::Profile->get_trx()) {
608 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
610 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
611 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
618 if (!_route->is_master()) {
619 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
620 show_sends_button->show();
624 meter_point_button.set_text (meter_point_string (_route->meter_point()));
626 delete route_ops_menu;
629 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
630 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
631 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
632 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
634 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
636 if (_route->panner_shell()) {
637 update_panner_choices();
638 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
641 if (is_audio_track()) {
642 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
645 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
646 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
648 set_stuff_from_route ();
650 /* now force an update of all the various elements */
652 update_mute_display ();
653 update_solo_display ();
656 route_group_changed ();
659 panners.setup_pan ();
661 if (has_audio_outputs ()) {
667 update_diskstream_display ();
668 update_input_display ();
669 update_output_display ();
671 add_events (Gdk::BUTTON_RELEASE_MASK);
673 processor_box.show ();
675 if (!route()->is_master() && !route()->is_monitor()) {
676 /* we don't allow master or control routes to be hidden */
681 gpm.reset_peak_display ();
682 gpm.gain_display.show ();
683 gpm.peak_display.show ();
686 width_hide_box.show();
688 global_vpacker.show();
689 mute_solo_table.show();
690 bottom_button_table.show();
692 meter_point_button.show();
693 input_button_box.show_all();
694 output_button.show();
696 _comment_button.show();
698 gpm.gain_automation_state_button.show();
700 parameter_changed ("mixer-element-visibility");
707 MixerStrip::set_stuff_from_route ()
709 /* if width is not set, it will be set by the MixerUI or editor */
711 string str = gui_property ("strip-width");
713 set_width_enum (Width (string_2_enum (str, _width)), this);
718 MixerStrip::set_width_enum (Width w, void* owner)
720 /* always set the gpm width again, things may be hidden */
723 panners.set_width (w);
725 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
727 _width_owner = owner;
731 if (_width_owner == this) {
732 set_gui_property ("strip-width", enum_2_string (_width));
737 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
742 if (show_sends_button) {
743 show_sends_button->set_text (_("Aux"));
746 gpm.gain_automation_style_button.set_text (
747 gpm.astyle_string(gain_automation->automation_style()));
748 gpm.gain_automation_state_button.set_text (
749 gpm.astate_string(gain_automation->automation_state()));
751 if (_route->panner()) {
752 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
753 panners.astyle_string(_route->panner()->automation_style()));
754 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
755 panners.astate_string(_route->panner()->automation_state()));
759 // panners expect an even number of horiz. pixels
760 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
762 set_size_request (width, -1);
768 if (show_sends_button) {
769 show_sends_button->set_text (_("Snd"));
772 gpm.gain_automation_style_button.set_text (
773 gpm.short_astyle_string(gain_automation->automation_style()));
774 gpm.gain_automation_state_button.set_text (
775 gpm.short_astate_string(gain_automation->automation_state()));
776 gain_meter().setup_meters (); // recalc meter width
778 if (_route->panner()) {
779 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
780 panners.short_astyle_string(_route->panner()->automation_style()));
781 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
782 panners.short_astate_string(_route->panner()->automation_state()));
786 // panners expect an even number of horiz. pixels
787 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
789 set_size_request (width, -1);
794 processor_box.set_width (w);
796 update_input_display ();
797 update_output_display ();
798 setup_comment_button ();
799 route_group_changed ();
805 MixerStrip::set_packed (bool yn)
810 set_gui_property ("visible", true);
812 set_gui_property ("visible", false);
817 struct RouteCompareByName {
818 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
819 return a->name().compare (b->name()) < 0;
824 MixerStrip::output_release (GdkEventButton *ev)
826 switch (ev->button) {
828 edit_output_configuration ();
836 MixerStrip::output_press (GdkEventButton *ev)
838 using namespace Menu_Helpers;
839 if (!_session->engine().connected()) {
840 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
845 MenuList& citems = output_menu.items();
846 switch (ev->button) {
849 return false; //wait for the mouse-up to pop the dialog
853 output_menu.set_name ("ArdourContextMenu");
855 output_menu_bundles.clear ();
857 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
859 citems.push_back (SeparatorElem());
860 uint32_t const n_with_separator = citems.size ();
862 ARDOUR::BundleList current = _route->output()->bundles_connected ();
864 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
866 /* give user bundles first chance at being in the menu */
868 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
869 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
870 maybe_add_bundle_to_output_menu (*i, current);
874 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
875 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
876 maybe_add_bundle_to_output_menu (*i, current);
880 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
881 RouteList copy = *routes;
882 copy.sort (RouteCompareByName ());
883 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
884 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
887 if (citems.size() == n_with_separator) {
888 /* no routes added; remove the separator */
892 if (!ARDOUR::Profile->get_mixbus()) {
893 citems.push_back (SeparatorElem());
895 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
898 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
899 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
905 citems.push_back (SeparatorElem());
906 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
908 output_menu.popup (1, ev->time);
919 MixerStrip::input_release (GdkEventButton *ev)
921 switch (ev->button) {
924 edit_input_configuration ();
936 MixerStrip::input_press (GdkEventButton *ev)
938 using namespace Menu_Helpers;
940 MenuList& citems = input_menu.items();
941 input_menu.set_name ("ArdourContextMenu");
944 if (!_session->engine().connected()) {
945 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
950 if (_session->actively_recording() && _route->record_enabled())
953 switch (ev->button) {
956 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
960 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
962 citems.push_back (SeparatorElem());
963 uint32_t const n_with_separator = citems.size ();
965 input_menu_bundles.clear ();
967 ARDOUR::BundleList current = _route->input()->bundles_connected ();
969 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
971 /* give user bundles first chance at being in the menu */
973 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
974 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
975 maybe_add_bundle_to_input_menu (*i, current);
979 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
980 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
981 maybe_add_bundle_to_input_menu (*i, current);
985 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
986 RouteList copy = *routes;
987 copy.sort (RouteCompareByName ());
988 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
989 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
992 if (citems.size() == n_with_separator) {
993 /* no routes added; remove the separator */
997 citems.push_back (SeparatorElem());
998 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1001 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1002 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1007 citems.push_back (SeparatorElem());
1008 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1010 input_menu.popup (1, ev->time);
1021 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1023 if (ignore_toggle) {
1027 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1029 if (std::find (current.begin(), current.end(), c) == current.end()) {
1030 _route->input()->connect_ports_to_bundle (c, true, this);
1032 _route->input()->disconnect_ports_from_bundle (c, this);
1037 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1039 if (ignore_toggle) {
1043 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1045 if (std::find (current.begin(), current.end(), c) == current.end()) {
1046 _route->output()->connect_ports_to_bundle (c, true, this);
1048 _route->output()->disconnect_ports_from_bundle (c, this);
1053 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1055 using namespace Menu_Helpers;
1057 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1061 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1062 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1066 if (i != input_menu_bundles.end()) {
1070 input_menu_bundles.push_back (b);
1072 MenuList& citems = input_menu.items();
1074 std::string n = b->name ();
1075 replace_all (n, "_", " ");
1077 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1081 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1083 using namespace Menu_Helpers;
1085 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1089 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1090 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1094 if (i != output_menu_bundles.end()) {
1098 output_menu_bundles.push_back (b);
1100 MenuList& citems = output_menu.items();
1102 std::string n = b->name ();
1103 replace_all (n, "_", " ");
1105 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1109 MixerStrip::update_diskstream_display ()
1111 if (is_track() && input_selector) {
1112 input_selector->hide_all ();
1115 route_color_changed ();
1119 MixerStrip::connect_to_pan ()
1121 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1123 panstate_connection.disconnect ();
1124 panstyle_connection.disconnect ();
1126 if (!_route->panner()) {
1130 boost::shared_ptr<Pannable> p = _route->pannable ();
1132 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1133 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1135 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1136 * However, that only works a panner was previously set.
1138 * PannerUI must remain subscribed to _panshell->Changed() in case
1139 * we switch the panner eg. AUX-Send and back
1140 * _route->panner_shell()->Changed() vs _panshell->Changed
1142 if (panners._panner == 0) {
1143 panners.panshell_changed ();
1145 update_panner_choices();
1149 MixerStrip::update_panner_choices ()
1151 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1152 if (!_route->panner_shell()) { return; }
1154 uint32_t in = _route->output()->n_ports().n_audio();
1156 if (_route->panner()) {
1157 in = _route->panner()->in().n_audio();
1160 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1164 * Output port labelling
1165 * =====================
1167 * Case 1: Each output has one connection, all connections are to system:playback_%i
1168 * out 1 -> system:playback_1
1169 * out 2 -> system:playback_2
1170 * out 3 -> system:playback_3
1173 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1174 * out 1 -> ardour:track_x/in 1
1175 * out 2 -> ardour:track_x/in 2
1176 * Display as: track_x
1178 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1179 * out 1 -> program x:foo
1180 * out 2 -> program x:foo
1181 * Display as: program x
1183 * Case 4: No connections (Disconnected)
1186 * Default case (unusual routing):
1187 * Display as: *number of connections*
1191 * .-----------------------------------------------.
1193 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1194 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1195 * '-----------------------------------------------'
1196 * .-----------------------------------------------.
1199 * '-----------------------------------------------'
1203 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1207 boost::shared_ptr<Port> port;
1208 vector<string> port_connections;
1210 uint32_t total_connection_count = 0;
1211 uint32_t io_connection_count = 0;
1212 uint32_t ardour_connection_count = 0;
1213 uint32_t system_connection_count = 0;
1214 uint32_t other_connection_count = 0;
1215 uint32_t typed_connection_count = 0;
1217 ostringstream label;
1219 bool have_label = false;
1220 bool each_io_has_one_connection = true;
1222 string connection_name;
1223 string ardour_track_name;
1224 string other_connection_type;
1225 string system_ports;
1228 ostringstream tooltip;
1229 char * tooltip_cstr;
1231 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1232 DataType dt = DataType::AUDIO;
1233 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1234 dt = DataType::MIDI;
1235 // avoid further confusion with Midi-tracks that have a synth.
1236 // Audio-ports may be connected, but button says "Disconnected"
1237 tooltip << _("MIDI ");
1241 io_count = route->n_inputs().n_total();
1242 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1244 io_count = route->n_outputs().n_total();
1245 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1249 for (io_index = 0; io_index < io_count; ++io_index) {
1251 port = route->input()->nth (io_index);
1253 port = route->output()->nth (io_index);
1256 port_connections.clear ();
1257 port->get_connections(port_connections);
1259 //ignore any port connections that don't match our DataType
1260 if (port->type() != dt) {
1261 if (!port_connections.empty()) {
1262 ++typed_connection_count;
1267 io_connection_count = 0;
1269 if (!port_connections.empty()) {
1270 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1272 string& connection_name (*i);
1274 if (connection_name.find("system:") == 0) {
1275 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1278 if (io_connection_count == 0) {
1279 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1281 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1284 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1287 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1288 if (ardour_track_name.empty()) {
1289 // "ardour:Master/in 1" -> "ardour:Master/"
1290 string::size_type slash = connection_name.find("/");
1291 if (slash != string::npos) {
1292 ardour_track_name = connection_name.substr(0, slash + 1);
1296 if (connection_name.find(ardour_track_name) == 0) {
1297 ++ardour_connection_count;
1299 } else if (!pn.empty()) {
1300 if (system_ports.empty()) {
1303 system_ports += "/" + pn;
1305 if (connection_name.find("system:") == 0) {
1306 ++system_connection_count;
1308 } else if (connection_name.find("system:midi_") == 0) {
1310 // "system:midi_capture_123" -> "123"
1311 system_port = "M " + connection_name.substr(20);
1313 // "system:midi_playback_123" -> "123"
1314 system_port = "M " + connection_name.substr(21);
1317 if (system_ports.empty()) {
1318 system_ports += system_port;
1320 system_ports += "/" + system_port;
1323 ++system_connection_count;
1325 } else if (connection_name.find("system:") == 0) {
1327 // "system:capture_123" -> "123"
1328 system_port = connection_name.substr(15);
1330 // "system:playback_123" -> "123"
1331 system_port = connection_name.substr(16);
1334 if (system_ports.empty()) {
1335 system_ports += system_port;
1337 system_ports += "/" + system_port;
1340 ++system_connection_count;
1342 if (other_connection_type.empty()) {
1343 // "jamin:in 1" -> "jamin:"
1344 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1347 if (connection_name.find(other_connection_type) == 0) {
1348 ++other_connection_count;
1352 ++total_connection_count;
1353 ++io_connection_count;
1357 if (io_connection_count != 1) {
1358 each_io_has_one_connection = false;
1362 if (total_connection_count == 0) {
1363 tooltip << endl << _("Disconnected");
1366 tooltip_cstr = new char[tooltip.str().size() + 1];
1367 strcpy(tooltip_cstr, tooltip.str().c_str());
1370 set_tooltip (&input_button, tooltip_cstr);
1372 set_tooltip (&output_button, tooltip_cstr);
1375 delete [] tooltip_cstr;
1377 if (each_io_has_one_connection) {
1378 if (total_connection_count == ardour_connection_count) {
1379 // all connections are to the same track in ardour
1380 // "ardour:Master/" -> "Master"
1381 string::size_type slash = ardour_track_name.find("/");
1382 if (slash != string::npos) {
1383 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1384 label << ardour_track_name.substr (ppps, slash - ppps);
1388 else if (total_connection_count == system_connection_count) {
1389 // all connections are to system ports
1390 label << system_ports;
1393 else if (total_connection_count == other_connection_count) {
1394 // all connections are to the same external program eg jamin
1395 // "jamin:" -> "jamin"
1396 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1402 if (total_connection_count == 0) {
1406 // Odd configuration
1407 label << "*" << total_connection_count << "*";
1409 if (typed_connection_count > 0) {
1410 label << "\u2295"; // circled plus
1415 input_button.set_text (label.str());
1417 output_button.set_text (label.str());
1422 MixerStrip::update_input_display ()
1424 update_io_button (_route, _width, true);
1425 panners.setup_pan ();
1427 if (has_audio_outputs ()) {
1428 panners.show_all ();
1430 panners.hide_all ();
1436 MixerStrip::update_output_display ()
1438 update_io_button (_route, _width, false);
1439 gpm.setup_meters ();
1440 panners.setup_pan ();
1442 if (has_audio_outputs ()) {
1443 panners.show_all ();
1445 panners.hide_all ();
1450 MixerStrip::fast_update ()
1452 gpm.update_meters ();
1456 MixerStrip::diskstream_changed ()
1458 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1462 MixerStrip::io_changed_proxy ()
1464 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1468 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1470 boost::shared_ptr<Port> a = wa.lock ();
1471 boost::shared_ptr<Port> b = wb.lock ();
1473 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1474 update_input_display ();
1475 set_width_enum (_width, this);
1478 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1479 update_output_display ();
1480 set_width_enum (_width, this);
1485 MixerStrip::setup_comment_button ()
1490 if (_route->comment().empty ()) {
1491 _comment_button.unset_bg (STATE_NORMAL);
1492 _comment_button.set_text (_("Comments"));
1494 _comment_button.modify_bg (STATE_NORMAL, color ());
1495 _comment_button.set_text (_("*Comments*"));
1500 if (_route->comment().empty ()) {
1501 _comment_button.unset_bg (STATE_NORMAL);
1502 _comment_button.set_text (_("Cmt"));
1504 _comment_button.modify_bg (STATE_NORMAL, color ());
1505 _comment_button.set_text (_("*Cmt*"));
1511 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1517 MixerStrip::select_route_group (GdkEventButton *ev)
1519 using namespace Menu_Helpers;
1521 if (ev->button == 1) {
1523 if (group_menu == 0) {
1525 PropertyList* plist = new PropertyList();
1527 plist->add (Properties::gain, true);
1528 plist->add (Properties::mute, true);
1529 plist->add (Properties::solo, true);
1531 group_menu = new RouteGroupMenu (_session, plist);
1535 r.push_back (route ());
1536 group_menu->build (r);
1537 group_menu->menu()->popup (1, ev->time);
1544 MixerStrip::route_group_changed ()
1546 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1548 RouteGroup *rg = _route->route_group();
1551 group_button.set_text (PBD::short_version (rg->name(), 5));
1555 group_button.set_text (_("Grp"));
1558 group_button.set_text (_("~G"));
1565 MixerStrip::route_color_changed ()
1567 name_button.modify_bg (STATE_NORMAL, color());
1568 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1569 reset_strip_style ();
1573 MixerStrip::show_passthru_color ()
1575 reset_strip_style ();
1579 MixerStrip::build_route_ops_menu ()
1581 using namespace Menu_Helpers;
1582 route_ops_menu = new Menu;
1583 route_ops_menu->set_name ("ArdourContextMenu");
1585 MenuList& items = route_ops_menu->items();
1587 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1589 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1591 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1593 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1595 items.push_back (SeparatorElem());
1597 if (!_route->is_master()) {
1598 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1600 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1601 rename_menu_item = &items.back();
1603 items.push_back (SeparatorElem());
1604 items.push_back (CheckMenuElem (_("Active")));
1605 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1606 i->set_active (_route->active());
1607 i->set_sensitive(! _session->transport_rolling());
1608 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1610 if (!Profile->get_mixbus ()) {
1611 items.push_back (SeparatorElem());
1612 items.push_back (CheckMenuElem (_("Strict I/O")));
1613 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1614 i->set_active (_route->strict_io());
1615 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1618 if (1 /* TODO IFF >= 1 plugin-insert */) {
1619 items.push_back (SeparatorElem());
1620 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1623 items.push_back (SeparatorElem());
1624 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1626 items.push_back (SeparatorElem());
1627 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1628 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1629 denormal_menu_item->set_active (_route->denormal_protection());
1631 items.push_back (SeparatorElem());
1632 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1635 /* note that this relies on selection being shared across editor and
1636 mixer (or global to the backend, in the future), which is the only
1637 sane thing for users anyway.
1640 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1642 Selection& selection (PublicEditor::instance().get_selection());
1643 if (!selection.selected (rtav)) {
1644 selection.set (rtav);
1647 if (!_route->is_master()) {
1648 items.push_back (SeparatorElem());
1649 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1652 items.push_back (SeparatorElem());
1653 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1659 MixerStrip::name_button_button_press (GdkEventButton* ev)
1661 if (ev->button == 3) {
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);
1675 MixerStrip::name_button_button_release (GdkEventButton* ev)
1677 if (ev->button == 1) {
1678 list_route_operations ();
1680 /* do not allow rename if the track is record-enabled */
1681 rename_menu_item->set_sensitive (!_route->record_enabled());
1682 route_ops_menu->popup (1, ev->time);
1689 MixerStrip::number_button_button_press (GdkEventButton* ev)
1691 if ( ev->button == 3 ) {
1692 list_route_operations ();
1694 /* do not allow rename if the track is record-enabled */
1695 rename_menu_item->set_sensitive (!_route->record_enabled());
1696 route_ops_menu->popup (1, ev->time);
1705 MixerStrip::list_route_operations ()
1707 delete route_ops_menu;
1708 build_route_ops_menu ();
1712 MixerStrip::set_selected (bool yn)
1714 AxisView::set_selected (yn);
1716 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1717 global_frame.set_name ("MixerStripSelectedFrame");
1719 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1720 global_frame.set_name ("MixerStripFrame");
1722 global_frame.queue_draw ();
1725 // processor_box.deselect_all_processors();
1729 MixerStrip::property_changed (const PropertyChange& what_changed)
1731 RouteUI::property_changed (what_changed);
1733 if (what_changed.contains (ARDOUR::Properties::name)) {
1739 MixerStrip::name_changed ()
1743 name_button.set_text (_route->name());
1746 name_button.set_text (PBD::short_version (_route->name(), 5));
1750 set_tooltip (name_button, _route->name());
1752 if (_session->config.get_track_name_number()) {
1753 const int64_t track_number = _route->track_number ();
1754 if (track_number == 0) {
1755 number_label.set_text ("-");
1757 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1760 number_label.set_text ("");
1765 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1767 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1771 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1773 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1777 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1779 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1783 MixerStrip::width_button_pressed (GdkEventButton* ev)
1785 if (ev->button != 1) {
1789 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1792 _mixer.set_strip_width (Narrow, true);
1796 _mixer.set_strip_width (Wide, true);
1802 set_width_enum (Narrow, this);
1805 set_width_enum (Wide, this);
1814 MixerStrip::hide_clicked ()
1816 // LAME fix to reset the button status for when it is redisplayed (part 1)
1817 hide_button.set_sensitive(false);
1820 Hiding(); /* EMIT_SIGNAL */
1822 _mixer.hide_strip (this);
1826 hide_button.set_sensitive(true);
1830 MixerStrip::set_embedded (bool yn)
1836 MixerStrip::map_frozen ()
1838 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1840 boost::shared_ptr<AudioTrack> at = audio_track();
1843 switch (at->freeze_state()) {
1844 case AudioTrack::Frozen:
1845 processor_box.set_sensitive (false);
1846 hide_redirect_editors ();
1849 processor_box.set_sensitive (true);
1850 // XXX need some way, maybe, to retoggle redirect editors
1854 processor_box.set_sensitive (true);
1856 RouteUI::map_frozen ();
1860 MixerStrip::hide_redirect_editors ()
1862 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1866 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1868 boost::shared_ptr<Processor> processor (p.lock ());
1873 Gtk::Window* w = processor_box.get_processor_ui (processor);
1881 MixerStrip::reset_strip_style ()
1883 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1885 gpm.set_fader_name ("SendStripBase");
1889 if (is_midi_track()) {
1890 if (_route->active()) {
1891 set_name ("MidiTrackStripBase");
1893 set_name ("MidiTrackStripBaseInactive");
1895 gpm.set_fader_name ("MidiTrackFader");
1896 } else if (is_audio_track()) {
1897 if (_route->active()) {
1898 set_name ("AudioTrackStripBase");
1900 set_name ("AudioTrackStripBaseInactive");
1902 gpm.set_fader_name ("AudioTrackFader");
1904 if (_route->active()) {
1905 set_name ("AudioBusStripBase");
1907 set_name ("AudioBusStripBaseInactive");
1909 gpm.set_fader_name ("AudioBusFader");
1911 /* (no MIDI busses yet) */
1918 MixerStrip::engine_stopped ()
1923 MixerStrip::engine_running ()
1928 MixerStrip::meter_point_string (MeterPoint mp)
1941 case MeterPostFader:
1958 return S_("Meter|In");
1962 return S_("Meter|Pr");
1965 case MeterPostFader:
1966 return S_("Meter|Po");
1970 return S_("Meter|O");
1975 return S_("Meter|C");
1984 /** Called when the monitor-section state */
1986 MixerStrip::monitor_changed ()
1988 assert (monitor_section_button);
1989 if (_session->monitor_active()) {
1990 monitor_section_button->set_name ("master monitor section button active");
1992 monitor_section_button->set_name ("master monitor section button normal");
1996 /** Called when the metering point has changed */
1998 MixerStrip::meter_changed ()
2000 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2001 gpm.setup_meters ();
2002 // reset peak when meter point changes
2003 gpm.reset_peak_display();
2006 /** The bus that we are displaying sends to has changed, or been turned off.
2007 * @param send_to New bus that we are displaying sends to, or 0.
2010 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2012 RouteUI::bus_send_display_changed (send_to);
2015 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2020 revert_to_default_display ();
2023 revert_to_default_display ();
2028 MixerStrip::drop_send ()
2030 boost::shared_ptr<Send> current_send;
2032 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2033 current_send->set_metering (false);
2036 send_gone_connection.disconnect ();
2037 input_button.set_sensitive (true);
2038 output_button.set_sensitive (true);
2039 group_button.set_sensitive (true);
2040 set_invert_sensitive (true);
2041 meter_point_button.set_sensitive (true);
2042 mute_button->set_sensitive (true);
2043 solo_button->set_sensitive (true);
2044 solo_isolated_led->set_sensitive (true);
2045 solo_safe_led->set_sensitive (true);
2046 monitor_input_button->set_sensitive (true);
2047 monitor_disk_button->set_sensitive (true);
2048 _comment_button.set_sensitive (true);
2049 RouteUI::check_rec_enable_sensitivity ();
2050 set_button_names (); // update solo button visual state
2054 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2056 _current_delivery = d;
2057 DeliveryChanged (_current_delivery);
2061 MixerStrip::show_send (boost::shared_ptr<Send> send)
2067 set_current_delivery (send);
2069 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2070 send->set_metering (true);
2071 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2073 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2074 gain_meter().setup_meters ();
2076 uint32_t const in = _current_delivery->pans_required();
2077 uint32_t const out = _current_delivery->pan_outs();
2079 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2080 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2081 panner_ui().setup_pan ();
2082 panner_ui().set_send_drawing_mode (true);
2083 panner_ui().show_all ();
2085 input_button.set_sensitive (false);
2086 group_button.set_sensitive (false);
2087 set_invert_sensitive (false);
2088 meter_point_button.set_sensitive (false);
2089 mute_button->set_sensitive (false);
2090 solo_button->set_sensitive (false);
2091 rec_enable_button->set_sensitive (false);
2092 solo_isolated_led->set_sensitive (false);
2093 solo_safe_led->set_sensitive (false);
2094 monitor_input_button->set_sensitive (false);
2095 monitor_disk_button->set_sensitive (false);
2096 _comment_button.set_sensitive (false);
2098 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2099 output_button.set_sensitive (false);
2102 reset_strip_style ();
2106 MixerStrip::revert_to_default_display ()
2110 set_current_delivery (_route->main_outs ());
2112 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2113 gain_meter().setup_meters ();
2115 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2116 update_panner_choices();
2117 panner_ui().setup_pan ();
2118 panner_ui().set_send_drawing_mode (false);
2120 if (has_audio_outputs ()) {
2121 panners.show_all ();
2123 panners.hide_all ();
2126 reset_strip_style ();
2130 MixerStrip::set_button_names ()
2134 mute_button->set_text (_("Mute"));
2135 monitor_input_button->set_text (_("In"));
2136 monitor_disk_button->set_text (_("Disk"));
2137 if (monitor_section_button) {
2138 monitor_section_button->set_text (_("Mon"));
2141 if (_route && _route->solo_safe()) {
2142 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2144 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2146 if (!Config->get_solo_control_is_listen_control()) {
2147 solo_button->set_text (_("Solo"));
2149 switch (Config->get_listen_position()) {
2150 case AfterFaderListen:
2151 solo_button->set_text (_("AFL"));
2153 case PreFaderListen:
2154 solo_button->set_text (_("PFL"));
2158 solo_isolated_led->set_text (_("Iso"));
2159 solo_safe_led->set_text (S_("SoloLock|Lock"));
2163 mute_button->set_text (S_("Mute|M"));
2164 monitor_input_button->set_text (S_("MonitorInput|I"));
2165 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2166 if (monitor_section_button) {
2167 monitor_section_button->set_text (S_("Mon|O"));
2170 if (_route && _route->solo_safe()) {
2171 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2173 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2175 if (!Config->get_solo_control_is_listen_control()) {
2176 solo_button->set_text (S_("Solo|S"));
2178 switch (Config->get_listen_position()) {
2179 case AfterFaderListen:
2180 solo_button->set_text (S_("AfterFader|A"));
2182 case PreFaderListen:
2183 solo_button->set_text (S_("Prefader|P"));
2188 solo_isolated_led->set_text (S_("SoloIso|I"));
2189 solo_safe_led->set_text (S_("SoloLock|L"));
2194 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2196 meter_point_button.set_text ("");
2201 MixerStrip::plugin_selector()
2203 return _mixer.plugin_selector();
2207 MixerStrip::hide_things ()
2209 processor_box.hide_things ();
2213 MixerStrip::input_active_button_press (GdkEventButton*)
2215 /* nothing happens on press */
2220 MixerStrip::input_active_button_release (GdkEventButton* ev)
2222 boost::shared_ptr<MidiTrack> mt = midi_track ();
2228 boost::shared_ptr<RouteList> rl (new RouteList);
2230 rl->push_back (route());
2232 _session->set_exclusive_input_active (rl, !mt->input_active(),
2233 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2239 MixerStrip::midi_input_status_changed ()
2241 if (midi_input_enable_button) {
2242 boost::shared_ptr<MidiTrack> mt = midi_track ();
2244 midi_input_enable_button->set_active (mt->input_active ());
2249 MixerStrip::state_id () const
2251 return string_compose ("strip %1", _route->id().to_s());
2255 MixerStrip::parameter_changed (string p)
2257 if (p == _visibility.get_state_name()) {
2258 /* The user has made changes to the mixer strip visibility, so get
2259 our VisibilityGroup to reflect these changes in our widgets.
2261 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2263 else if (p == "track-name-number") {
2266 else if (p == "use-monitor-bus") {
2267 if (monitor_section_button) {
2268 if (mute_button->get_parent()) {
2269 mute_button->get_parent()->remove(*mute_button);
2271 if (monitor_section_button->get_parent()) {
2272 monitor_section_button->get_parent()->remove(*monitor_section_button);
2274 if (Config->get_use_monitor_bus ()) {
2275 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2276 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2277 mute_button->show();
2278 monitor_section_button->show();
2280 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2281 mute_button->show();
2287 /** Called to decide whether the solo isolate / solo lock button visibility should
2288 * be overridden from that configured by the user. We do this for the master bus.
2290 * @return optional value that is present if visibility state should be overridden.
2292 boost::optional<bool>
2293 MixerStrip::override_solo_visibility () const
2295 if (_route && _route->is_master ()) {
2296 return boost::optional<bool> (false);
2299 return boost::optional<bool> ();
2303 MixerStrip::add_input_port (DataType t)
2305 _route->input()->add_port ("", this, t);
2309 MixerStrip::add_output_port (DataType t)
2311 _route->output()->add_port ("", this, t);
2315 MixerStrip::route_active_changed ()
2317 reset_strip_style ();
2321 MixerStrip::copy_processors ()
2323 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2327 MixerStrip::cut_processors ()
2329 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2333 MixerStrip::paste_processors ()
2335 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2339 MixerStrip::select_all_processors ()
2341 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2345 MixerStrip::deselect_all_processors ()
2347 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2351 MixerStrip::delete_processors ()
2353 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2357 MixerStrip::toggle_processors ()
2359 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2363 MixerStrip::ab_plugins ()
2365 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2369 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2371 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2374 if (ev->button == 3) {
2375 popup_level_meter_menu (ev);
2383 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2385 using namespace Gtk::Menu_Helpers;
2387 Gtk::Menu* m = manage (new Menu);
2388 MenuList& items = m->items ();
2390 RadioMenuItem::Group group;
2392 _suspend_menu_callbacks = true;
2393 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2394 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2395 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2396 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2397 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2399 if (gpm.meter_channels().n_audio() == 0) {
2400 m->popup (ev->button, ev->time);
2401 _suspend_menu_callbacks = false;
2405 RadioMenuItem::Group tgroup;
2406 items.push_back (SeparatorElem());
2408 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2409 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2410 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2411 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2412 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2413 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2414 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2415 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2416 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2417 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2418 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2421 if (_route->is_master()) {
2424 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2425 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2426 /* non-master bus */
2429 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2436 MeterType cmt = _route->meter_type();
2437 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2439 items.push_back (SeparatorElem());
2440 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2441 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2442 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2443 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2444 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2445 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2447 m->popup (ev->button, ev->time);
2448 _suspend_menu_callbacks = false;
2452 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2453 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2455 using namespace Menu_Helpers;
2457 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2458 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2459 i->set_active (_route->meter_point() == point);
2463 MixerStrip::set_meter_point (MeterPoint p)
2465 if (_suspend_menu_callbacks) return;
2466 _route->set_meter_point (p);
2470 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2471 RadioMenuItem::Group& group, string const & name, MeterType type)
2473 using namespace Menu_Helpers;
2475 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2476 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2477 i->set_active (_route->meter_type() == type);
2481 MixerStrip::set_meter_type (MeterType t)
2483 if (_suspend_menu_callbacks) return;