2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <sigc++/bind.h>
25 #include "pbd/convert.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/replace_all.h"
28 #include "pbd/stacktrace.h"
30 #include <gtkmm2ext/gtk_ui.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/choice.h>
33 #include <gtkmm2ext/doi.h>
34 #include <gtkmm2ext/slider_controller.h>
35 #include <gtkmm2ext/bindable_button.h>
37 #include "ardour/amp.h"
38 #include "ardour/audio_track.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/internal_send.h"
41 #include "ardour/io.h"
42 #include "ardour/meter.h"
43 #include "ardour/midi_track.h"
44 #include "ardour/pannable.h"
45 #include "ardour/panner.h"
46 #include "ardour/panner_shell.h"
47 #include "ardour/panner_manager.h"
48 #include "ardour/port.h"
49 #include "ardour/profile.h"
50 #include "ardour/route.h"
51 #include "ardour/route_group.h"
52 #include "ardour/send.h"
53 #include "ardour/session.h"
54 #include "ardour/types.h"
55 #include "ardour/user_bundle.h"
56 #include "ardour/vca.h"
57 #include "ardour/vca_manager.h"
59 #include "ardour_window.h"
60 #include "mixer_strip.h"
63 #include "ardour_button.h"
64 #include "public_editor.h"
66 #include "io_selector.h"
68 #include "gui_thread.h"
69 #include "route_group_menu.h"
70 #include "meter_patterns.h"
72 #include "ui_config.h"
76 using namespace ARDOUR;
77 using namespace ARDOUR_UI_UTILS;
80 using namespace Gtkmm2ext;
82 using namespace ArdourMeter;
84 MixerStrip* MixerStrip::_entered_mixer_strip;
85 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
87 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
88 : SessionHandlePtr (sess)
91 , _mixer_owned (in_mixer)
92 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
95 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
96 , rec_mon_table (2, 2)
97 , solo_iso_table (1, 2)
98 , mute_solo_table (1, 2)
99 , bottom_button_table (1, 3)
100 , monitor_section_button (0)
101 , midi_input_enable_button (0)
102 , _plugin_insert_cnt (0)
103 , _comment_button (_("Comments"))
104 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
105 , _visibility (X_("mixer-element-visibility"))
106 , control_slave_ui (sess)
111 /* the editor mixer strip: don't destroy it every time
112 the underlying route goes away.
115 self_destruct = false;
119 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
120 : SessionHandlePtr (sess)
123 , _mixer_owned (in_mixer)
124 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
127 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
128 , rec_mon_table (2, 2)
129 , solo_iso_table (1, 2)
130 , mute_solo_table (1, 2)
131 , bottom_button_table (1, 3)
132 , monitor_section_button (0)
133 , midi_input_enable_button (0)
134 , _plugin_insert_cnt (0)
135 , _comment_button (_("Comments"))
136 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
137 , _visibility (X_("mixer-element-visibility"))
138 , control_slave_ui (sess)
147 _entered_mixer_strip= 0;
150 ignore_comment_edit = false;
151 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 hide_button.set_tweaks (ArdourButton::Square);
165 set_tooltip (width_button, t);
167 hide_button.set_icon (ArdourIcon::CloseCross);
168 hide_button.set_tweaks (ArdourButton::Square);
169 set_tooltip (&hide_button, _("Hide this mixer strip"));
171 input_button_box.set_spacing(2);
173 input_button.set_text (_("Input"));
174 input_button.set_name ("mixer strip button");
175 input_button_box.pack_start (input_button, true, true);
177 output_button.set_text (_("Output"));
178 output_button.set_name ("mixer strip button");
180 bottom_button_table.attach (gpm.meter_point_button, 2, 3, 0, 1);
182 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
184 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
185 solo_isolated_led->show ();
186 solo_isolated_led->set_no_show_all (true);
187 solo_isolated_led->set_name (X_("solo isolate"));
188 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
189 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
190 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
192 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
193 solo_safe_led->show ();
194 solo_safe_led->set_no_show_all (true);
195 solo_safe_led->set_name (X_("solo safe"));
196 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
197 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
198 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
200 solo_safe_led->set_text (S_("SoloLock|Lock"));
201 solo_isolated_led->set_text (_("Iso"));
203 solo_iso_table.set_homogeneous (true);
204 solo_iso_table.set_spacings (2);
205 if (!ARDOUR::Profile->get_trx()) {
206 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
207 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
209 solo_iso_table.show ();
211 rec_mon_table.set_homogeneous (true);
212 rec_mon_table.set_row_spacings (2);
213 rec_mon_table.set_col_spacings (2);
214 if (ARDOUR::Profile->get_mixbus()) {
215 rec_mon_table.resize (1, 3);
216 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
217 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
218 } else if (!ARDOUR::Profile->get_trx()) {
219 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
220 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
222 rec_mon_table.show ();
224 if (solo_isolated_led) {
225 button_size_group->add_widget (*solo_isolated_led);
228 button_size_group->add_widget (*solo_safe_led);
231 if (!ARDOUR::Profile->get_mixbus()) {
232 if (rec_enable_button) {
233 button_size_group->add_widget (*rec_enable_button);
235 if (monitor_disk_button) {
236 button_size_group->add_widget (*monitor_disk_button);
238 if (monitor_input_button) {
239 button_size_group->add_widget (*monitor_input_button);
243 mute_solo_table.set_homogeneous (true);
244 mute_solo_table.set_spacings (2);
246 bottom_button_table.set_spacings (2);
247 bottom_button_table.set_homogeneous (true);
248 bottom_button_table.attach (group_button, 1, 2, 0, 1);
249 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
251 name_button.set_name ("mixer strip button");
252 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
253 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
255 set_tooltip (&group_button, _("Mix group"));
256 group_button.set_name ("mixer strip button");
258 _comment_button.set_name (X_("mixer strip button"));
259 _comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
260 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
261 _comment_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::comment_button_resized));
263 // TODO implement ArdourKnob::on_size_request properly
264 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
265 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
267 trim_control.set_tooltip_prefix (_("Trim: "));
268 trim_control.set_name ("trim knob");
269 trim_control.set_no_show_all (true);
270 input_button_box.pack_start (trim_control, false, false);
272 global_vpacker.set_border_width (1);
273 global_vpacker.set_spacing (0);
275 width_button.set_name ("mixer strip button");
276 hide_button.set_name ("mixer strip button");
278 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
279 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
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);
293 number_label.set_tweaks (ArdourButton::OccasionalText);
295 global_vpacker.set_spacing (2);
296 if (!ARDOUR::Profile->get_trx()) {
297 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
298 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
299 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
300 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
301 global_vpacker.pack_start (processor_box, true, true);
303 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
305 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
306 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
308 global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
310 if (!ARDOUR::Profile->get_trx()) {
311 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
314 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
318 //add a spacer underneath the master bus;
319 //this fills the area that is taken up by the scrollbar on the tracks;
320 //and therefore keeps the faders "even" across the bottom
321 int scrollbar_height = 0;
323 Gtk::Window window (WINDOW_TOPLEVEL);
324 HScrollbar scrollbar;
325 window.add (scrollbar);
326 scrollbar.set_name ("MixerWindow");
327 scrollbar.ensure_style();
328 Gtk::Requisition requisition(scrollbar.size_request ());
329 scrollbar_height = requisition.height;
331 spacer.set_size_request (-1, scrollbar_height);
332 global_vpacker.pack_end (spacer, false, false);
335 global_frame.add (global_vpacker);
336 global_frame.set_shadow_type (Gtk::SHADOW_IN);
337 global_frame.set_name ("BaseFrame");
341 /* force setting of visible selected status */
344 set_selected (false);
349 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
350 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
352 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
353 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
354 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
356 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
357 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
359 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
360 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
361 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
363 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
365 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
367 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
371 /* start off as a passthru strip. we'll correct this, if necessary,
372 in update_diskstream_display().
375 /* start off as a passthru strip. we'll correct this, if necessary,
376 in update_diskstream_display().
379 if (is_midi_track()) {
380 set_name ("MidiTrackStripBase");
382 set_name ("AudioTrackStripBase");
385 add_events (Gdk::BUTTON_RELEASE_MASK|
386 Gdk::ENTER_NOTIFY_MASK|
387 Gdk::LEAVE_NOTIFY_MASK|
389 Gdk::KEY_RELEASE_MASK);
391 set_flags (get_flags() | Gtk::CAN_FOCUS);
393 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
394 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
397 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
398 must be the same as those used in RCOptionEditor so that the configuration changes
399 are recognised when they occur.
401 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
402 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
403 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
404 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
405 _visibility.add (&output_button, X_("Output"), _("Output"), false);
406 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
407 _visibility.add (&control_slave_ui, X_("VCA"), _("VCA Assigns"), false);
409 parameter_changed (X_("mixer-element-visibility"));
410 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
411 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
412 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
414 //watch for mouse enter/exit so we can do some stuff
415 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
416 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
418 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
421 MixerStrip::~MixerStrip ()
423 CatchDeletion (this);
425 if (this ==_entered_mixer_strip)
426 _entered_mixer_strip = NULL;
430 MixerStrip::vca_assign (boost::shared_ptr<ARDOUR::VCA> vca)
432 boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
434 sl->assign(vca, false);
438 MixerStrip::vca_unassign (boost::shared_ptr<ARDOUR::VCA> vca)
440 boost::shared_ptr<Slavable> sl = boost::dynamic_pointer_cast<Slavable> ( route() );
446 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
448 _entered_mixer_strip = this;
450 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
451 //because the mixerstrip control is a parent that encompasses the strip
452 deselect_all_processors();
458 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
460 //if we have moved outside our strip, but not into a child view, then deselect ourselves
461 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
462 _entered_mixer_strip= 0;
464 //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
465 gpm.gain_display.set_sensitive(false);
467 gpm.gain_display.set_sensitive(true);
469 //if we leave this mixer strip we need to clear out any selections
470 //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
477 MixerStrip::name() const
480 return _route->name();
486 MixerStrip::update_trim_control ()
488 if (route()->trim() && route()->trim()->active() &&
489 route()->n_inputs().n_audio() > 0) {
490 trim_control.show ();
491 trim_control.set_controllable (route()->trim()->gain_control());
493 trim_control.hide ();
494 boost::shared_ptr<Controllable> none;
495 trim_control.set_controllable (none);
500 MixerStrip::set_route (boost::shared_ptr<Route> rt)
502 //the rec/monitor stuff only shows up for tracks.
503 //the show_sends only shows up for buses.
504 //remove them all here, and we may add them back later
505 if (show_sends_button->get_parent()) {
506 rec_mon_table.remove (*show_sends_button);
508 if (rec_enable_button->get_parent()) {
509 rec_mon_table.remove (*rec_enable_button);
511 if (monitor_input_button->get_parent()) {
512 rec_mon_table.remove (*monitor_input_button);
514 if (monitor_disk_button->get_parent()) {
515 rec_mon_table.remove (*monitor_disk_button);
517 if (group_button.get_parent()) {
518 bottom_button_table.remove (group_button);
521 RouteUI::set_route (rt);
523 control_slave_ui.set_stripable (boost::dynamic_pointer_cast<Stripable> (rt));
525 /* ProcessorBox needs access to _route so that it can read
528 processor_box.set_route (rt);
530 revert_to_default_display ();
532 /* unpack these from the parent and stuff them into our own
536 if (gpm.peak_display.get_parent()) {
537 gpm.peak_display.get_parent()->remove (gpm.peak_display);
539 if (gpm.gain_display.get_parent()) {
540 gpm.gain_display.get_parent()->remove (gpm.gain_display);
543 gpm.set_type (rt->meter_type());
545 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
546 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
548 if (solo_button->get_parent()) {
549 mute_solo_table.remove (*solo_button);
552 if (mute_button->get_parent()) {
553 mute_solo_table.remove (*mute_button);
556 if (route()->is_master()) {
557 solo_button->hide ();
558 mute_button->show ();
559 rec_mon_table.hide ();
560 solo_iso_table.set_sensitive(false);
561 control_slave_ui.set_sensitive(false);
562 if (monitor_section_button == 0) {
563 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
564 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
566 monitor_section_button = manage (new ArdourButton);
568 monitor_section_button->set_related_action (act);
569 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
570 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
571 monitor_section_button->show();
572 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
574 parameter_changed ("use-monitor-bus");
576 bottom_button_table.attach (group_button, 1, 2, 0, 1);
577 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
578 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
579 mute_button->show ();
580 solo_button->show ();
581 rec_mon_table.show ();
582 solo_iso_table.set_sensitive(true);
583 control_slave_ui.set_sensitive(true);
586 if (_mixer_owned && route()->is_master() ) {
593 monitor_input_button->show ();
594 monitor_disk_button->show ();
596 monitor_input_button->hide();
597 monitor_disk_button->hide ();
600 update_trim_control();
602 if (is_midi_track()) {
603 if (midi_input_enable_button == 0) {
604 midi_input_enable_button = manage (new ArdourButton);
605 midi_input_enable_button->set_name ("midi input button");
606 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
607 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
608 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
609 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
610 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
612 input_button_box.remove (*midi_input_enable_button);
614 /* get current state */
615 midi_input_status_changed ();
616 input_button_box.pack_start (*midi_input_enable_button, false, false);
618 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
620 if (midi_input_enable_button) {
621 /* removal from the container will delete it */
622 input_button_box.remove (*midi_input_enable_button);
623 midi_input_enable_button = 0;
627 if (is_audio_track()) {
628 boost::shared_ptr<AudioTrack> at = audio_track();
629 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
634 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
635 rec_enable_button->show();
637 if (ARDOUR::Profile->get_mixbus()) {
638 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
639 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
640 } else if (ARDOUR::Profile->get_trx()) {
641 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
643 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
644 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
651 if (!_route->is_master()) {
652 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
653 show_sends_button->show();
657 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
659 delete route_ops_menu;
662 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
663 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
664 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
665 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
667 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
669 if (_route->panner_shell()) {
670 update_panner_choices();
671 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
674 if (is_audio_track()) {
675 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
678 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
680 set_stuff_from_route ();
682 /* now force an update of all the various elements */
684 update_mute_display ();
685 update_solo_display ();
688 route_group_changed ();
689 update_track_number_visibility ();
692 panners.setup_pan ();
694 if (has_audio_outputs ()) {
700 update_diskstream_display ();
701 update_input_display ();
702 update_output_display ();
704 add_events (Gdk::BUTTON_RELEASE_MASK);
706 processor_box.show ();
708 if (!route()->is_master() && !route()->is_monitor()) {
709 /* we don't allow master or control routes to be hidden */
714 gpm.reset_peak_display ();
715 gpm.gain_display.show ();
716 gpm.peak_display.show ();
719 width_hide_box.show();
721 global_vpacker.show();
722 mute_solo_table.show();
723 bottom_button_table.show();
725 gpm.meter_point_button.show();
726 input_button_box.show_all();
727 output_button.show();
729 _comment_button.show();
731 gpm.gain_automation_state_button.show();
733 parameter_changed ("mixer-element-visibility");
740 MixerStrip::set_stuff_from_route ()
742 /* if width is not set, it will be set by the MixerUI or editor */
744 string str = gui_property ("strip-width");
746 set_width_enum (Width (string_2_enum (str, _width)), this);
751 MixerStrip::set_width_enum (Width w, void* owner)
753 /* always set the gpm width again, things may be hidden */
756 panners.set_width (w);
758 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
760 _width_owner = owner;
764 if (_width_owner == this) {
765 set_gui_property ("strip-width", enum_2_string (_width));
770 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
775 if (show_sends_button) {
776 show_sends_button->set_text (_("Aux"));
779 gpm.gain_automation_style_button.set_text (
780 gpm.astyle_string(gain_automation->automation_style()));
781 gpm.gain_automation_state_button.set_text (
782 gpm.astate_string(gain_automation->automation_state()));
784 if (_route->panner()) {
785 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
786 panners.astyle_string(_route->panner()->automation_style()));
787 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
788 panners.astate_string(_route->panner()->automation_state()));
792 // panners expect an even number of horiz. pixels
793 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
795 set_size_request (width, -1);
801 if (show_sends_button) {
802 show_sends_button->set_text (_("Snd"));
805 gpm.gain_automation_style_button.set_text (
806 gpm.short_astyle_string(gain_automation->automation_style()));
807 gpm.gain_automation_state_button.set_text (
808 gpm.short_astate_string(gain_automation->automation_state()));
809 gain_meter().setup_meters (); // recalc meter width
811 if (_route->panner()) {
812 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
813 panners.short_astyle_string(_route->panner()->automation_style()));
814 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
815 panners.short_astate_string(_route->panner()->automation_state()));
819 // panners expect an even number of horiz. pixels
820 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
822 set_size_request (width, -1);
827 processor_box.set_width (w);
829 update_input_display ();
830 update_output_display ();
831 setup_comment_button ();
832 route_group_changed ();
838 MixerStrip::set_packed (bool yn)
843 set_gui_property ("visible", true);
845 set_gui_property ("visible", false);
850 struct RouteCompareByName {
851 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
852 return a->name().compare (b->name()) < 0;
857 MixerStrip::output_release (GdkEventButton *ev)
859 switch (ev->button) {
861 edit_output_configuration ();
869 MixerStrip::output_press (GdkEventButton *ev)
871 using namespace Menu_Helpers;
872 if (!_session->engine().connected()) {
873 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
878 MenuList& citems = output_menu.items();
879 switch (ev->button) {
882 return false; //wait for the mouse-up to pop the dialog
886 output_menu.set_name ("ArdourContextMenu");
888 output_menu_bundles.clear ();
890 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
892 citems.push_back (SeparatorElem());
893 uint32_t const n_with_separator = citems.size ();
895 ARDOUR::BundleList current = _route->output()->bundles_connected ();
897 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
899 /* give user bundles first chance at being in the menu */
901 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
902 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
903 maybe_add_bundle_to_output_menu (*i, current);
907 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
908 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
909 maybe_add_bundle_to_output_menu (*i, current);
913 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
914 RouteList copy = *routes;
915 copy.sort (RouteCompareByName ());
916 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
917 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
920 if (citems.size() == n_with_separator) {
921 /* no routes added; remove the separator */
925 if (!ARDOUR::Profile->get_mixbus()) {
926 citems.push_back (SeparatorElem());
928 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
931 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
932 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
938 citems.push_back (SeparatorElem());
939 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
941 Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
954 MixerStrip::input_release (GdkEventButton *ev)
956 switch (ev->button) {
959 edit_input_configuration ();
971 MixerStrip::input_press (GdkEventButton *ev)
973 using namespace Menu_Helpers;
975 MenuList& citems = input_menu.items();
976 input_menu.set_name ("ArdourContextMenu");
979 if (!_session->engine().connected()) {
980 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
985 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
988 switch (ev->button) {
991 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
995 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
997 citems.push_back (SeparatorElem());
998 uint32_t const n_with_separator = citems.size ();
1000 input_menu_bundles.clear ();
1002 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1004 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
1006 /* give user bundles first chance at being in the menu */
1008 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1009 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
1010 maybe_add_bundle_to_input_menu (*i, current);
1014 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
1015 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
1016 maybe_add_bundle_to_input_menu (*i, current);
1020 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1021 RouteList copy = *routes;
1022 copy.sort (RouteCompareByName ());
1023 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1024 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1027 if (citems.size() == n_with_separator) {
1028 /* no routes added; remove the separator */
1032 citems.push_back (SeparatorElem());
1033 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1036 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1037 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1042 citems.push_back (SeparatorElem());
1043 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1045 Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
1057 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1059 if (ignore_toggle) {
1063 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1065 if (std::find (current.begin(), current.end(), c) == current.end()) {
1066 _route->input()->connect_ports_to_bundle (c, true, this);
1068 _route->input()->disconnect_ports_from_bundle (c, this);
1073 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1075 if (ignore_toggle) {
1079 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1081 if (std::find (current.begin(), current.end(), c) == current.end()) {
1082 _route->output()->connect_ports_to_bundle (c, true, this);
1084 _route->output()->disconnect_ports_from_bundle (c, this);
1089 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1091 using namespace Menu_Helpers;
1093 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1097 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1098 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1102 if (i != input_menu_bundles.end()) {
1106 input_menu_bundles.push_back (b);
1108 MenuList& citems = input_menu.items();
1110 std::string n = b->name ();
1111 replace_all (n, "_", " ");
1113 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1117 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1119 using namespace Menu_Helpers;
1121 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1125 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1126 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1130 if (i != output_menu_bundles.end()) {
1134 output_menu_bundles.push_back (b);
1136 MenuList& citems = output_menu.items();
1138 std::string n = b->name ();
1139 replace_all (n, "_", " ");
1141 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1145 MixerStrip::update_diskstream_display ()
1147 if (is_track() && input_selector) {
1148 input_selector->hide_all ();
1151 route_color_changed ();
1155 MixerStrip::connect_to_pan ()
1157 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1159 panstate_connection.disconnect ();
1160 panstyle_connection.disconnect ();
1162 if (!_route->panner()) {
1166 boost::shared_ptr<Pannable> p = _route->pannable ();
1168 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1169 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1171 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1172 * However, that only works a panner was previously set.
1174 * PannerUI must remain subscribed to _panshell->Changed() in case
1175 * we switch the panner eg. AUX-Send and back
1176 * _route->panner_shell()->Changed() vs _panshell->Changed
1178 if (panners._panner == 0) {
1179 panners.panshell_changed ();
1181 update_panner_choices();
1185 MixerStrip::update_panner_choices ()
1187 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1188 if (!_route->panner_shell()) { return; }
1190 uint32_t in = _route->output()->n_ports().n_audio();
1192 if (_route->panner()) {
1193 in = _route->panner()->in().n_audio();
1196 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1200 * Output port labelling
1201 * =====================
1203 * Case 1: Each output has one connection, all connections are to system:playback_%i
1204 * out 1 -> system:playback_1
1205 * out 2 -> system:playback_2
1206 * out 3 -> system:playback_3
1209 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1210 * out 1 -> ardour:track_x/in 1
1211 * out 2 -> ardour:track_x/in 2
1212 * Display as: track_x
1214 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1215 * out 1 -> program x:foo
1216 * out 2 -> program x:foo
1217 * Display as: program x
1219 * Case 4: No connections (Disconnected)
1222 * Default case (unusual routing):
1223 * Display as: *number of connections*
1227 * .-----------------------------------------------.
1229 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1230 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1231 * '-----------------------------------------------'
1232 * .-----------------------------------------------.
1235 * '-----------------------------------------------'
1239 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1243 boost::shared_ptr<IO> io;
1244 boost::shared_ptr<Port> port;
1245 vector<string> port_connections;
1247 uint32_t total_connection_count = 0;
1248 uint32_t io_connection_count = 0;
1249 uint32_t ardour_connection_count = 0;
1250 uint32_t system_connection_count = 0;
1251 uint32_t other_connection_count = 0;
1252 uint32_t typed_connection_count = 0;
1254 ostringstream label;
1256 bool have_label = false;
1257 bool each_io_has_one_connection = true;
1259 string connection_name;
1260 string ardour_track_name;
1261 string other_connection_type;
1262 string system_ports;
1265 ostringstream tooltip;
1266 char * tooltip_cstr;
1268 /* To avoid confusion, the button caption only shows connections that match the expected datatype
1270 * First of all, if the user made only connections to a given type, we should use that one since
1271 * it is very probably what the user expects. If there are several connections types, then show
1272 * audio ones as primary, which matches expectations for both audio tracks with midi control and
1273 * synthesisers. This first heuristic can be expressed with these two rules:
1274 * A) If there are connected audio ports, consider audio as primary type.
1275 * B) Else, if there are connected midi ports, consider midi as primary type.
1277 * If there are no connected ports, then we choose the primary type based on the type of existing
1278 * but unconnected ports. Again:
1279 * C) If there are audio ports, consider audio as primary type.
1280 * D) Else, if there are midi ports, consider midi as primary type. */
1282 DataType dt = DataType::AUDIO;
1286 io = route->input();
1288 io = route->output();
1291 io_count = io->n_ports().n_total();
1292 for (io_index = 0; io_index < io_count; ++io_index) {
1293 port = io->nth (io_index);
1294 if (port->connected()) {
1296 if (port->type() == DataType::AUDIO) {
1297 /* Rule A) applies no matter the remaining ports */
1298 dt = DataType::AUDIO;
1301 if (port->type() == DataType::MIDI) {
1302 /* Rule B) is a good candidate... */
1303 dt = DataType::MIDI;
1304 /* ...but continue the loop to check remaining ports for rule A) */
1310 /* Neither rule A) nor rule B) matched */
1311 if ( io->n_ports().n_audio() > 0 ) {
1313 dt = DataType::AUDIO;
1314 } else if ( io->n_ports().n_midi() > 0 ) {
1316 dt = DataType::MIDI;
1320 if ( dt == DataType::MIDI ) {
1321 tooltip << _("MIDI ");
1325 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1327 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1330 for (io_index = 0; io_index < io_count; ++io_index) {
1331 port = io->nth (io_index);
1333 port_connections.clear ();
1334 port->get_connections(port_connections);
1336 //ignore any port connections that don't match our DataType
1337 if (port->type() != dt) {
1338 if (!port_connections.empty()) {
1339 ++typed_connection_count;
1344 io_connection_count = 0;
1346 if (!port_connections.empty()) {
1347 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1349 string& connection_name (*i);
1351 if (connection_name.find("system:") == 0) {
1352 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1355 if (io_connection_count == 0) {
1356 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1358 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1361 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1364 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1365 if (ardour_track_name.empty()) {
1366 // "ardour:Master/in 1" -> "ardour:Master/"
1367 string::size_type slash = connection_name.find("/");
1368 if (slash != string::npos) {
1369 ardour_track_name = connection_name.substr(0, slash + 1);
1373 if (connection_name.find(ardour_track_name) == 0) {
1374 ++ardour_connection_count;
1376 } else if (!pn.empty()) {
1377 if (system_ports.empty()) {
1380 system_ports += "/" + pn;
1382 if (connection_name.find("system:") == 0) {
1383 ++system_connection_count;
1385 } else if (connection_name.find("system:midi_") == 0) {
1387 // "system:midi_capture_123" -> "123"
1388 system_port = "M " + connection_name.substr(20);
1390 // "system:midi_playback_123" -> "123"
1391 system_port = "M " + connection_name.substr(21);
1394 if (system_ports.empty()) {
1395 system_ports += system_port;
1397 system_ports += "/" + system_port;
1400 ++system_connection_count;
1402 } else if (connection_name.find("system:") == 0) {
1404 // "system:capture_123" -> "123"
1405 system_port = connection_name.substr(15);
1407 // "system:playback_123" -> "123"
1408 system_port = connection_name.substr(16);
1411 if (system_ports.empty()) {
1412 system_ports += system_port;
1414 system_ports += "/" + system_port;
1417 ++system_connection_count;
1419 if (other_connection_type.empty()) {
1420 // "jamin:in 1" -> "jamin:"
1421 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1424 if (connection_name.find(other_connection_type) == 0) {
1425 ++other_connection_count;
1429 ++total_connection_count;
1430 ++io_connection_count;
1434 if (io_connection_count != 1) {
1435 each_io_has_one_connection = false;
1439 if (total_connection_count == 0) {
1440 tooltip << endl << _("Disconnected");
1443 tooltip_cstr = new char[tooltip.str().size() + 1];
1444 strcpy(tooltip_cstr, tooltip.str().c_str());
1447 set_tooltip (&input_button, tooltip_cstr);
1449 set_tooltip (&output_button, tooltip_cstr);
1452 delete [] tooltip_cstr;
1454 if (each_io_has_one_connection) {
1455 if (total_connection_count == ardour_connection_count) {
1456 // all connections are to the same track in ardour
1457 // "ardour:Master/" -> "Master"
1458 string::size_type slash = ardour_track_name.find("/");
1459 if (slash != string::npos) {
1460 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1461 label << ardour_track_name.substr (ppps, slash - ppps);
1465 else if (total_connection_count == system_connection_count) {
1466 // all connections are to system ports
1467 label << system_ports;
1470 else if (total_connection_count == other_connection_count) {
1471 // all connections are to the same external program eg jamin
1472 // "jamin:" -> "jamin"
1473 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1479 if (total_connection_count == 0) {
1483 // Odd configuration
1484 label << "*" << total_connection_count << "*";
1486 if (typed_connection_count > 0) {
1487 label << "\u2295"; // circled plus
1492 input_button.set_text (label.str());
1494 output_button.set_text (label.str());
1499 MixerStrip::update_input_display ()
1501 update_io_button (_route, _width, true);
1502 panners.setup_pan ();
1504 if (has_audio_outputs ()) {
1505 panners.show_all ();
1507 panners.hide_all ();
1513 MixerStrip::update_output_display ()
1515 update_io_button (_route, _width, false);
1516 gpm.setup_meters ();
1517 panners.setup_pan ();
1519 if (has_audio_outputs ()) {
1520 panners.show_all ();
1522 panners.hide_all ();
1527 MixerStrip::fast_update ()
1529 gpm.update_meters ();
1533 MixerStrip::diskstream_changed ()
1535 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1539 MixerStrip::io_changed_proxy ()
1541 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1542 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_trim_control));
1546 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1548 boost::shared_ptr<Port> a = wa.lock ();
1549 boost::shared_ptr<Port> b = wb.lock ();
1551 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1552 update_input_display ();
1553 set_width_enum (_width, this);
1556 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1557 update_output_display ();
1558 set_width_enum (_width, this);
1563 MixerStrip::setup_comment_button ()
1565 std::string comment = _route->comment();
1567 set_tooltip (_comment_button, comment.empty() ? _("Click to add/edit comments") : _route->comment());
1569 if (comment.empty ()) {
1570 _comment_button.set_name ("generic button");
1571 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1575 _comment_button.set_name ("comment button");
1577 string::size_type pos = comment.find_first_of (" \t\n");
1578 if (pos != string::npos) {
1579 comment = comment.substr (0, pos);
1581 if (comment.empty()) {
1582 _comment_button.set_text (_width == Wide ? _("Comments") : _("Cmt"));
1584 _comment_button.set_text (comment);
1589 MixerStrip::select_route_group (GdkEventButton *ev)
1591 using namespace Menu_Helpers;
1593 if (ev->button == 1) {
1595 if (group_menu == 0) {
1597 PropertyList* plist = new PropertyList();
1599 plist->add (Properties::group_gain, true);
1600 plist->add (Properties::group_mute, true);
1601 plist->add (Properties::group_solo, true);
1603 group_menu = new RouteGroupMenu (_session, plist);
1607 r.push_back (route ());
1608 group_menu->build (r);
1610 RouteGroup *rg = _route->route_group();
1612 Gtkmm2ext::anchored_menu_popup(group_menu->menu(), &group_button,
1613 rg ? rg->name() : _("No Group"),
1621 MixerStrip::route_group_changed ()
1623 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1625 RouteGroup *rg = _route->route_group();
1628 group_button.set_text (PBD::short_version (rg->name(), 5));
1632 group_button.set_text (_("Grp"));
1635 group_button.set_text (_("~G"));
1642 MixerStrip::route_color_changed ()
1644 name_button.modify_bg (STATE_NORMAL, color());
1645 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1646 reset_strip_style ();
1650 MixerStrip::show_passthru_color ()
1652 reset_strip_style ();
1657 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1659 boost::shared_ptr<Processor> processor (p.lock ());
1660 if (!processor || !processor->display_to_user()) {
1663 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
1665 if (pi && pi->is_channelstrip ()) {
1670 ++_plugin_insert_cnt;
1674 MixerStrip::build_route_ops_menu ()
1676 using namespace Menu_Helpers;
1677 route_ops_menu = new Menu;
1678 route_ops_menu->set_name ("ArdourContextMenu");
1680 MenuList& items = route_ops_menu->items();
1682 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1684 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1686 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1688 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1690 if (!Profile->get_mixbus()) {
1691 items.push_back (SeparatorElem());
1694 if (!_route->is_master()
1696 && !_route->mixbus()
1699 if (Profile->get_mixbus()) {
1700 items.push_back (SeparatorElem());
1702 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1705 if (!Profile->get_mixbus()) {
1706 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1707 rename_menu_item = &items.back();
1710 items.push_back (SeparatorElem());
1711 items.push_back (CheckMenuElem (_("Active")));
1712 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1713 i->set_active (_route->active());
1714 i->set_sensitive(! _session->transport_rolling());
1715 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1717 if (!Profile->get_mixbus ()) {
1718 items.push_back (SeparatorElem());
1719 items.push_back (CheckMenuElem (_("Strict I/O")));
1720 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1721 i->set_active (_route->strict_io());
1722 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1725 _plugin_insert_cnt = 0;
1726 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1727 if (_plugin_insert_cnt > 0) {
1728 items.push_back (SeparatorElem());
1729 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1732 if (_route->the_instrument () && _route->the_instrument ()->output_streams().n_audio() > 2) {
1733 // TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
1734 items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
1735 items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
1738 items.push_back (SeparatorElem());
1739 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1741 items.push_back (SeparatorElem());
1742 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1743 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1744 denormal_menu_item->set_active (_route->denormal_protection());
1747 /* note that this relies on selection being shared across editor and
1748 mixer (or global to the backend, in the future), which is the only
1749 sane thing for users anyway.
1752 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1754 Selection& selection (PublicEditor::instance().get_selection());
1755 if (!selection.selected (rtav)) {
1756 selection.set (rtav);
1759 if (!_route->is_master()) {
1760 items.push_back (SeparatorElem());
1761 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1764 items.push_back (SeparatorElem());
1765 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1771 MixerStrip::name_button_button_press (GdkEventButton* ev)
1773 if (ev->button == 1 || ev->button == 3) {
1774 list_route_operations ();
1776 /* do not allow rename if the track is record-enabled */
1777 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1778 if (ev->button == 1) {
1779 Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
1782 route_ops_menu->popup (3, ev->time);
1792 MixerStrip::number_button_button_press (GdkEventButton* ev)
1794 if ( ev->button == 3 ) {
1795 list_route_operations ();
1797 /* do not allow rename if the track is record-enabled */
1798 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1799 route_ops_menu->popup (1, ev->time);
1808 MixerStrip::list_route_operations ()
1810 delete route_ops_menu;
1811 build_route_ops_menu ();
1815 MixerStrip::set_selected (bool yn)
1817 AxisView::set_selected (yn);
1820 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1821 global_frame.set_name ("MixerStripSelectedFrame");
1823 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1824 global_frame.set_name ("MixerStripFrame");
1827 global_frame.queue_draw ();
1830 // processor_box.deselect_all_processors();
1834 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1836 if (what_changed.contains (ARDOUR::Properties::name)) {
1842 MixerStrip::name_changed ()
1846 name_button.set_text (_route->name());
1849 name_button.set_text (PBD::short_version (_route->name(), 5));
1853 set_tooltip (name_button, _route->name());
1855 if (_session->config.get_track_name_number()) {
1856 const int64_t track_number = _route->track_number ();
1857 if (track_number == 0) {
1858 number_label.set_text ("-");
1860 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1863 number_label.set_text ("");
1868 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1870 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1874 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1876 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1880 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1882 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1886 MixerStrip::comment_button_resized (Gtk::Allocation& alloc)
1888 _comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1892 MixerStrip::width_button_pressed (GdkEventButton* ev)
1894 if (ev->button != 1) {
1898 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1901 _mixer.set_strip_width (Narrow, true);
1905 _mixer.set_strip_width (Wide, true);
1911 set_width_enum (Narrow, this);
1914 set_width_enum (Wide, this);
1923 MixerStrip::hide_clicked ()
1925 // LAME fix to reset the button status for when it is redisplayed (part 1)
1926 hide_button.set_sensitive(false);
1929 Hiding(); /* EMIT_SIGNAL */
1931 _mixer.hide_strip (this);
1935 hide_button.set_sensitive(true);
1939 MixerStrip::set_embedded (bool yn)
1945 MixerStrip::map_frozen ()
1947 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1949 boost::shared_ptr<AudioTrack> at = audio_track();
1952 switch (at->freeze_state()) {
1953 case AudioTrack::Frozen:
1954 processor_box.set_sensitive (false);
1955 hide_redirect_editors ();
1958 processor_box.set_sensitive (true);
1959 // XXX need some way, maybe, to retoggle redirect editors
1963 processor_box.set_sensitive (true);
1965 RouteUI::map_frozen ();
1969 MixerStrip::hide_redirect_editors ()
1971 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1975 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1977 boost::shared_ptr<Processor> processor (p.lock ());
1982 Gtk::Window* w = processor_box.get_processor_ui (processor);
1990 MixerStrip::reset_strip_style ()
1992 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1994 gpm.set_fader_name ("SendStripBase");
1998 if (is_midi_track()) {
1999 if (_route->active()) {
2000 set_name ("MidiTrackStripBase");
2002 set_name ("MidiTrackStripBaseInactive");
2004 gpm.set_fader_name ("MidiTrackFader");
2005 } else if (is_audio_track()) {
2006 if (_route->active()) {
2007 set_name ("AudioTrackStripBase");
2009 set_name ("AudioTrackStripBaseInactive");
2011 gpm.set_fader_name ("AudioTrackFader");
2013 if (_route->active()) {
2014 set_name ("AudioBusStripBase");
2016 set_name ("AudioBusStripBaseInactive");
2018 gpm.set_fader_name ("AudioBusFader");
2020 /* (no MIDI busses yet) */
2027 MixerStrip::engine_stopped ()
2032 MixerStrip::engine_running ()
2037 MixerStrip::meter_point_string (MeterPoint mp)
2050 case MeterPostFader:
2067 return S_("Meter|In");
2071 return S_("Meter|Pr");
2074 case MeterPostFader:
2075 return S_("Meter|Po");
2079 return S_("Meter|O");
2084 return S_("Meter|C");
2093 /** Called when the monitor-section state */
2095 MixerStrip::monitor_changed ()
2097 assert (monitor_section_button);
2098 if (_session->monitor_active()) {
2099 monitor_section_button->set_name ("master monitor section button active");
2101 monitor_section_button->set_name ("master monitor section button normal");
2105 /** Called when the metering point has changed */
2107 MixerStrip::meter_changed ()
2109 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2110 gpm.setup_meters ();
2111 // reset peak when meter point changes
2112 gpm.reset_peak_display();
2115 /** The bus that we are displaying sends to has changed, or been turned off.
2116 * @param send_to New bus that we are displaying sends to, or 0.
2119 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2121 RouteUI::bus_send_display_changed (send_to);
2124 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2129 revert_to_default_display ();
2132 revert_to_default_display ();
2137 MixerStrip::drop_send ()
2139 boost::shared_ptr<Send> current_send;
2141 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2142 current_send->set_metering (false);
2145 send_gone_connection.disconnect ();
2146 input_button.set_sensitive (true);
2147 output_button.set_sensitive (true);
2148 group_button.set_sensitive (true);
2149 set_invert_sensitive (true);
2150 gpm.meter_point_button.set_sensitive (true);
2151 mute_button->set_sensitive (true);
2152 solo_button->set_sensitive (true);
2153 solo_isolated_led->set_sensitive (true);
2154 solo_safe_led->set_sensitive (true);
2155 monitor_input_button->set_sensitive (true);
2156 monitor_disk_button->set_sensitive (true);
2157 _comment_button.set_sensitive (true);
2158 RouteUI::check_rec_enable_sensitivity ();
2159 set_button_names (); // update solo button visual state
2163 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2165 _current_delivery = d;
2166 DeliveryChanged (_current_delivery);
2170 MixerStrip::show_send (boost::shared_ptr<Send> send)
2176 set_current_delivery (send);
2178 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2179 send->set_metering (true);
2180 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2182 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2183 gain_meter().setup_meters ();
2185 uint32_t const in = _current_delivery->pans_required();
2186 uint32_t const out = _current_delivery->pan_outs();
2188 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2189 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2190 panner_ui().setup_pan ();
2191 panner_ui().set_send_drawing_mode (true);
2192 panner_ui().show_all ();
2194 input_button.set_sensitive (false);
2195 group_button.set_sensitive (false);
2196 set_invert_sensitive (false);
2197 gpm.meter_point_button.set_sensitive (false);
2198 mute_button->set_sensitive (false);
2199 solo_button->set_sensitive (false);
2200 rec_enable_button->set_sensitive (false);
2201 solo_isolated_led->set_sensitive (false);
2202 solo_safe_led->set_sensitive (false);
2203 monitor_input_button->set_sensitive (false);
2204 monitor_disk_button->set_sensitive (false);
2205 _comment_button.set_sensitive (false);
2207 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2208 output_button.set_sensitive (false);
2211 reset_strip_style ();
2215 MixerStrip::revert_to_default_display ()
2219 set_current_delivery (_route->main_outs ());
2221 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2222 gain_meter().setup_meters ();
2224 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2225 update_panner_choices();
2226 panner_ui().setup_pan ();
2227 panner_ui().set_send_drawing_mode (false);
2229 if (has_audio_outputs ()) {
2230 panners.show_all ();
2232 panners.hide_all ();
2235 reset_strip_style ();
2239 MixerStrip::set_button_names ()
2243 mute_button->set_text (_("Mute"));
2244 monitor_input_button->set_text (_("In"));
2245 monitor_disk_button->set_text (_("Disk"));
2246 if (monitor_section_button) {
2247 monitor_section_button->set_text (_("Mon"));
2250 if (_route && _route->solo_safe_control()->solo_safe()) {
2251 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2253 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2255 if (!Config->get_solo_control_is_listen_control()) {
2256 solo_button->set_text (_("Solo"));
2258 switch (Config->get_listen_position()) {
2259 case AfterFaderListen:
2260 solo_button->set_text (_("AFL"));
2262 case PreFaderListen:
2263 solo_button->set_text (_("PFL"));
2267 solo_isolated_led->set_text (_("Iso"));
2268 solo_safe_led->set_text (S_("SoloLock|Lock"));
2272 mute_button->set_text (S_("Mute|M"));
2273 monitor_input_button->set_text (S_("MonitorInput|I"));
2274 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2275 if (monitor_section_button) {
2276 monitor_section_button->set_text (S_("Mon|O"));
2279 if (_route && _route->solo_safe_control()->solo_safe()) {
2280 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2282 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2284 if (!Config->get_solo_control_is_listen_control()) {
2285 solo_button->set_text (S_("Solo|S"));
2287 switch (Config->get_listen_position()) {
2288 case AfterFaderListen:
2289 solo_button->set_text (S_("AfterFader|A"));
2291 case PreFaderListen:
2292 solo_button->set_text (S_("Prefader|P"));
2297 solo_isolated_led->set_text (S_("SoloIso|I"));
2298 solo_safe_led->set_text (S_("SoloLock|L"));
2303 gpm.meter_point_button.set_text (meter_point_string (_route->meter_point()));
2305 gpm.meter_point_button.set_text ("");
2310 MixerStrip::plugin_selector()
2312 return _mixer.plugin_selector();
2316 MixerStrip::hide_things ()
2318 processor_box.hide_things ();
2322 MixerStrip::input_active_button_press (GdkEventButton*)
2324 /* nothing happens on press */
2329 MixerStrip::input_active_button_release (GdkEventButton* ev)
2331 boost::shared_ptr<MidiTrack> mt = midi_track ();
2337 boost::shared_ptr<RouteList> rl (new RouteList);
2339 rl->push_back (route());
2341 _session->set_exclusive_input_active (rl, !mt->input_active(),
2342 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2348 MixerStrip::midi_input_status_changed ()
2350 if (midi_input_enable_button) {
2351 boost::shared_ptr<MidiTrack> mt = midi_track ();
2353 midi_input_enable_button->set_active (mt->input_active ());
2358 MixerStrip::state_id () const
2360 return string_compose ("strip %1", _route->id().to_s());
2364 MixerStrip::parameter_changed (string p)
2366 if (p == _visibility.get_state_name()) {
2367 /* The user has made changes to the mixer strip visibility, so get
2368 our VisibilityGroup to reflect these changes in our widgets.
2370 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2371 } else if (p == "track-name-number") {
2373 } else if (p == "use-monitor-bus") {
2374 if (monitor_section_button) {
2375 if (mute_button->get_parent()) {
2376 mute_button->get_parent()->remove(*mute_button);
2378 if (monitor_section_button->get_parent()) {
2379 monitor_section_button->get_parent()->remove(*monitor_section_button);
2381 if (Config->get_use_monitor_bus ()) {
2382 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2383 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2384 mute_button->show();
2385 monitor_section_button->show();
2387 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2388 mute_button->show();
2391 } else if (p == "track-name-number") {
2392 update_track_number_visibility();
2396 /** Called to decide whether the solo isolate / solo lock button visibility should
2397 * be overridden from that configured by the user. We do this for the master bus.
2399 * @return optional value that is present if visibility state should be overridden.
2401 boost::optional<bool>
2402 MixerStrip::override_solo_visibility () const
2404 if (_route && _route->is_master ()) {
2405 return boost::optional<bool> (false);
2408 return boost::optional<bool> ();
2412 MixerStrip::add_input_port (DataType t)
2414 _route->input()->add_port ("", this, t);
2418 MixerStrip::add_output_port (DataType t)
2420 _route->output()->add_port ("", this, t);
2424 MixerStrip::route_active_changed ()
2426 reset_strip_style ();
2430 MixerStrip::copy_processors ()
2432 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2436 MixerStrip::cut_processors ()
2438 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2442 MixerStrip::paste_processors ()
2444 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2448 MixerStrip::select_all_processors ()
2450 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2454 MixerStrip::deselect_all_processors ()
2456 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2460 MixerStrip::delete_processors ()
2462 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2466 MixerStrip::toggle_processors ()
2468 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2472 MixerStrip::ab_plugins ()
2474 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2478 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2480 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2483 if (ev->button == 3) {
2484 popup_level_meter_menu (ev);
2492 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2494 using namespace Gtk::Menu_Helpers;
2496 Gtk::Menu* m = manage (new Menu);
2497 MenuList& items = m->items ();
2499 RadioMenuItem::Group group;
2501 _suspend_menu_callbacks = true;
2502 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2503 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2504 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2505 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2506 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2508 if (gpm.meter_channels().n_audio() == 0) {
2509 m->popup (ev->button, ev->time);
2510 _suspend_menu_callbacks = false;
2514 RadioMenuItem::Group tgroup;
2515 items.push_back (SeparatorElem());
2517 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2518 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2519 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2520 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2521 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2522 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2523 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2524 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2525 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2526 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2527 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2530 if (_route->is_master()) {
2533 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2534 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2535 /* non-master bus */
2538 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2545 MeterType cmt = _route->meter_type();
2546 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2548 items.push_back (SeparatorElem());
2549 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2550 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2551 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2552 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2553 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2554 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2556 m->popup (ev->button, ev->time);
2557 _suspend_menu_callbacks = false;
2561 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2562 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2564 using namespace Menu_Helpers;
2566 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2567 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2568 i->set_active (_route->meter_point() == point);
2572 MixerStrip::set_meter_point (MeterPoint p)
2574 if (_suspend_menu_callbacks) return;
2575 _route->set_meter_point (p);
2579 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2580 RadioMenuItem::Group& group, string const & name, MeterType type)
2582 using namespace Menu_Helpers;
2584 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2585 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2586 i->set_active (_route->meter_type() == type);
2590 MixerStrip::set_meter_type (MeterType t)
2592 if (_suspend_menu_callbacks) return;
2597 MixerStrip::update_track_number_visibility ()
2599 DisplaySuspender ds;
2600 bool show_label = _session->config.get_track_name_number();
2602 if (_route && _route->is_master()) {
2607 number_label.show ();
2608 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2609 // except the width of the number label is subtracted from the name-hbox, so we
2610 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2611 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2613 number_label.set_size_request(tnw, -1);
2614 number_label.show ();
2616 number_label.hide ();
2621 MixerStrip::color () const
2623 return route_color ();
2627 MixerStrip::marked_for_display () const
2629 return !_route->presentation_info().hidden();
2633 MixerStrip::set_marked_for_display (bool yn)
2635 return RouteUI::mark_hidden (!yn);