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"
55 #include "ardour/vca.h"
56 #include "ardour/vca_manager.h"
58 #include "ardour_window.h"
59 #include "mixer_strip.h"
62 #include "ardour_button.h"
63 #include "public_editor.h"
65 #include "io_selector.h"
67 #include "gui_thread.h"
68 #include "route_group_menu.h"
69 #include "meter_patterns.h"
71 #include "ui_config.h"
75 using namespace ARDOUR;
76 using namespace ARDOUR_UI_UTILS;
79 using namespace Gtkmm2ext;
81 using namespace ArdourMeter;
83 MixerStrip* MixerStrip::_entered_mixer_strip;
84 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
86 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
87 : SessionHandlePtr (sess)
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)
99 , meter_point_button (_("pre"))
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"))
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)
119 : SessionHandlePtr (sess)
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)
131 , meter_point_button (_("pre"))
132 , monitor_section_button (0)
133 , midi_input_enable_button (0)
134 , _comment_button (_("Comments"))
135 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
136 , _visibility (X_("mixer-element-visibility"))
145 _entered_mixer_strip= 0;
148 ignore_comment_edit = false;
149 ignore_toggle = false;
154 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
155 longest_label = "longest label";
157 string t = _("Click to toggle the width of this mixer strip.");
159 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
162 width_button.set_icon (ArdourIcon::StripWidth);
163 set_tooltip (width_button, t);
165 hide_button.set_icon (ArdourIcon::CloseCross);
166 set_tooltip (&hide_button, _("Hide this mixer strip"));
168 input_button_box.set_spacing(2);
170 input_button.set_text (_("Input"));
171 input_button.set_name ("mixer strip button");
172 input_button_box.pack_start (input_button, true, true);
174 output_button.set_text (_("Output"));
175 output_button.set_name ("mixer strip button");
177 set_tooltip (&meter_point_button, _("Click to select metering point"));
178 meter_point_button.set_name ("mixer strip button");
180 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
182 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
183 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
185 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
187 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
188 solo_isolated_led->show ();
189 solo_isolated_led->set_no_show_all (true);
190 solo_isolated_led->set_name (X_("solo isolate"));
191 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
192 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
193 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
195 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
196 solo_safe_led->show ();
197 solo_safe_led->set_no_show_all (true);
198 solo_safe_led->set_name (X_("solo safe"));
199 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
200 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
201 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
203 solo_safe_led->set_text (S_("SoloLock|Lock"));
204 solo_isolated_led->set_text (_("Iso"));
206 solo_iso_table.set_homogeneous (true);
207 solo_iso_table.set_spacings (2);
208 if (!ARDOUR::Profile->get_trx()) {
209 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
210 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
212 solo_iso_table.show ();
214 vca_button = manage (new ArdourButton (ArdourButton::default_elements));
215 vca_button->set_no_show_all (true);
216 vca_button->set_name (X_("vca assign"));
217 vca_button->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
218 vca_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::vca_button_release), false);
219 UI::instance()->set_tip (*vca_button, _("VCA assignments"));
220 vca_button->set_text (_("-vca-"));
223 rec_mon_table.set_homogeneous (true);
224 rec_mon_table.set_row_spacings (2);
225 rec_mon_table.set_col_spacings (2);
226 if (ARDOUR::Profile->get_mixbus()) {
227 rec_mon_table.resize (1, 3);
228 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
229 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
230 } else if (!ARDOUR::Profile->get_trx()) {
231 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
232 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
234 rec_mon_table.show ();
236 if (solo_isolated_led) {
237 button_size_group->add_widget (*solo_isolated_led);
240 button_size_group->add_widget (*solo_safe_led);
243 if (!ARDOUR::Profile->get_mixbus()) {
244 if (rec_enable_button) {
245 button_size_group->add_widget (*rec_enable_button);
247 if (monitor_disk_button) {
248 button_size_group->add_widget (*monitor_disk_button);
250 if (monitor_input_button) {
251 button_size_group->add_widget (*monitor_input_button);
255 mute_solo_table.set_homogeneous (true);
256 mute_solo_table.set_spacings (2);
258 bottom_button_table.set_spacings (2);
259 bottom_button_table.set_homogeneous (true);
260 bottom_button_table.attach (group_button, 1, 2, 0, 1);
261 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
263 name_button.set_name ("mixer strip button");
264 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
265 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
267 set_tooltip (&group_button, _("Mix group"));
268 group_button.set_name ("mixer strip button");
270 _comment_button.set_name (X_("mixer strip button"));
271 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
273 // TODO implement ArdourKnob::on_size_request properly
274 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
275 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
277 trim_control.set_tooltip_prefix (_("Trim: "));
278 trim_control.set_name ("trim knob");
279 trim_control.set_no_show_all (true);
280 input_button_box.pack_start (trim_control, false, false);
282 global_vpacker.set_border_width (1);
283 global_vpacker.set_spacing (0);
285 width_button.set_name ("mixer strip button");
286 hide_button.set_name ("mixer strip button");
288 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
289 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
291 // width_hide_box.set_border_width (1);
292 width_hide_box.set_spacing (2);
293 width_hide_box.pack_start (width_button, false, true);
294 width_hide_box.pack_start (number_label, true, true);
295 width_hide_box.pack_end (hide_button, false, true);
297 number_label.set_text ("-");
298 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
299 number_label.set_no_show_all ();
300 number_label.set_name ("tracknumber label");
301 number_label.set_fixed_colors (0x80808080, 0x80808080);
302 number_label.set_alignment (.5, .5);
303 number_label.set_fallthrough_to_parent (true);
305 global_vpacker.set_spacing (2);
306 if (!ARDOUR::Profile->get_trx()) {
307 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
308 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
311 global_vpacker.pack_start (processor_box, true, true);
313 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
314 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
315 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
316 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
317 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
318 global_vpacker.pack_start (*vca_button, Gtk::PACK_SHRINK);
319 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
320 if (!ARDOUR::Profile->get_trx()) {
321 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
322 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
324 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
327 global_frame.add (global_vpacker);
328 global_frame.set_shadow_type (Gtk::SHADOW_IN);
329 global_frame.set_name ("BaseFrame");
333 /* force setting of visible selected status */
336 set_selected (false);
341 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
342 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
344 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
345 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
346 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
348 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
349 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
351 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
352 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
353 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
355 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
357 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
358 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
360 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
364 /* start off as a passthru strip. we'll correct this, if necessary,
365 in update_diskstream_display().
368 /* start off as a passthru strip. we'll correct this, if necessary,
369 in update_diskstream_display().
372 if (is_midi_track()) {
373 set_name ("MidiTrackStripBase");
375 set_name ("AudioTrackStripBase");
378 add_events (Gdk::BUTTON_RELEASE_MASK|
379 Gdk::ENTER_NOTIFY_MASK|
380 Gdk::LEAVE_NOTIFY_MASK|
382 Gdk::KEY_RELEASE_MASK);
384 set_flags (get_flags() | Gtk::CAN_FOCUS);
386 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
387 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
390 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
391 must be the same as those used in RCOptionEditor so that the configuration changes
392 are recognised when they occur.
394 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
395 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
396 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
397 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
398 _visibility.add (&output_button, X_("Output"), _("Output"), false);
399 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
400 _visibility.add (vca_button, X_("VCA"), _("VCA Assigns"), false);
402 parameter_changed (X_("mixer-element-visibility"));
403 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
404 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
405 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
407 //watch for mouse enter/exit so we can do some stuff
408 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
409 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
411 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
414 MixerStrip::~MixerStrip ()
416 CatchDeletion (this);
418 if (this ==_entered_mixer_strip)
419 _entered_mixer_strip = NULL;
423 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
425 _entered_mixer_strip = this;
427 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
428 //because the mixerstrip control is a parent that encompasses the strip
429 deselect_all_processors();
435 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
437 //if we have moved outside our strip, but not into a child view, then deselect ourselves
438 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
439 _entered_mixer_strip= 0;
441 //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
442 gpm.gain_display.set_sensitive(false);
444 gpm.gain_display.set_sensitive(true);
446 //if we leave this mixer strip we need to clear out any selections
447 //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
454 MixerStrip::name() const
457 return _route->name();
463 MixerStrip::set_route (boost::shared_ptr<Route> rt)
465 //the rec/monitor stuff only shows up for tracks.
466 //the show_sends only shows up for buses.
467 //remove them all here, and we may add them back later
468 if (show_sends_button->get_parent()) {
469 rec_mon_table.remove (*show_sends_button);
471 if (rec_enable_button->get_parent()) {
472 rec_mon_table.remove (*rec_enable_button);
474 if (monitor_input_button->get_parent()) {
475 rec_mon_table.remove (*monitor_input_button);
477 if (monitor_disk_button->get_parent()) {
478 rec_mon_table.remove (*monitor_disk_button);
480 if (group_button.get_parent()) {
481 bottom_button_table.remove (group_button);
484 RouteUI::set_route (rt);
486 /* ProcessorBox needs access to _route so that it can read
489 processor_box.set_route (rt);
491 revert_to_default_display ();
493 /* unpack these from the parent and stuff them into our own
497 if (gpm.peak_display.get_parent()) {
498 gpm.peak_display.get_parent()->remove (gpm.peak_display);
500 if (gpm.gain_display.get_parent()) {
501 gpm.gain_display.get_parent()->remove (gpm.gain_display);
504 gpm.set_type (rt->meter_type());
506 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
507 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
509 if (solo_button->get_parent()) {
510 mute_solo_table.remove (*solo_button);
513 if (mute_button->get_parent()) {
514 mute_solo_table.remove (*mute_button);
517 if (route()->is_master()) {
518 solo_button->hide ();
519 mute_button->show ();
520 rec_mon_table.hide ();
521 if (solo_iso_table.get_parent()) {
522 solo_iso_table.get_parent()->remove(solo_iso_table);
524 if (monitor_section_button == 0) {
525 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
526 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
528 monitor_section_button = manage (new ArdourButton);
530 monitor_section_button->set_related_action (act);
531 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
532 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
533 monitor_section_button->show();
534 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
536 parameter_changed ("use-monitor-bus");
538 bottom_button_table.attach (group_button, 1, 2, 0, 1);
539 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
540 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
541 mute_button->show ();
542 solo_button->show ();
543 rec_mon_table.show ();
546 if (_mixer_owned && route()->is_master() ) {
548 HScrollbar scrollbar;
549 Gtk::Requisition requisition(scrollbar.size_request ());
550 int scrollbar_height = requisition.height;
552 spacer = manage (new EventBox);
553 spacer->set_size_request (-1, scrollbar_height+2);
554 global_vpacker.pack_start (*spacer, false, false);
559 monitor_input_button->show ();
560 monitor_disk_button->show ();
562 monitor_input_button->hide();
563 monitor_disk_button->hide ();
566 if (route()->trim() && route()->trim()->active()) {
567 trim_control.show ();
568 trim_control.set_controllable (route()->trim()->gain_control());
570 trim_control.hide ();
571 boost::shared_ptr<Controllable> none;
572 trim_control.set_controllable (none);
575 if (is_midi_track()) {
576 if (midi_input_enable_button == 0) {
577 midi_input_enable_button = manage (new ArdourButton);
578 midi_input_enable_button->set_name ("midi input button");
579 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
580 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
581 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
582 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
583 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
585 input_button_box.remove (*midi_input_enable_button);
587 /* get current state */
588 midi_input_status_changed ();
589 input_button_box.pack_start (*midi_input_enable_button, false, false);
591 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
593 if (midi_input_enable_button) {
594 /* removal from the container will delete it */
595 input_button_box.remove (*midi_input_enable_button);
596 midi_input_enable_button = 0;
600 if (is_audio_track()) {
601 boost::shared_ptr<AudioTrack> at = audio_track();
602 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
607 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
608 rec_enable_button->show();
610 if (ARDOUR::Profile->get_mixbus()) {
611 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
612 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
613 } else if (ARDOUR::Profile->get_trx()) {
614 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
616 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
617 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
624 if (!_route->is_master()) {
625 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
626 show_sends_button->show();
630 meter_point_button.set_text (meter_point_string (_route->meter_point()));
632 delete route_ops_menu;
635 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
636 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
637 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
638 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
640 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
642 if (_route->panner_shell()) {
643 update_panner_choices();
644 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
647 if (is_audio_track()) {
648 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
651 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
653 _route->gain_control()->MasterStatusChange.connect (route_connections,
655 boost::bind (&MixerStrip::update_vca_display, this),
658 set_stuff_from_route ();
660 /* now force an update of all the various elements */
662 update_mute_display ();
663 update_solo_display ();
664 update_vca_display ();
667 route_group_changed ();
668 update_track_number_visibility ();
671 panners.setup_pan ();
673 if (has_audio_outputs ()) {
679 update_diskstream_display ();
680 update_input_display ();
681 update_output_display ();
683 add_events (Gdk::BUTTON_RELEASE_MASK);
685 processor_box.show ();
687 if (!route()->is_master() && !route()->is_monitor()) {
688 /* we don't allow master or control routes to be hidden */
693 gpm.reset_peak_display ();
694 gpm.gain_display.show ();
695 gpm.peak_display.show ();
698 width_hide_box.show();
700 global_vpacker.show();
701 mute_solo_table.show();
702 bottom_button_table.show();
704 meter_point_button.show();
705 input_button_box.show_all();
706 output_button.show();
708 _comment_button.show();
710 gpm.gain_automation_state_button.show();
712 parameter_changed ("mixer-element-visibility");
719 MixerStrip::set_stuff_from_route ()
721 /* if width is not set, it will be set by the MixerUI or editor */
723 string str = gui_property ("strip-width");
725 set_width_enum (Width (string_2_enum (str, _width)), this);
730 MixerStrip::set_width_enum (Width w, void* owner)
732 /* always set the gpm width again, things may be hidden */
735 panners.set_width (w);
737 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
739 _width_owner = owner;
743 if (_width_owner == this) {
744 set_gui_property ("strip-width", enum_2_string (_width));
749 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
754 if (show_sends_button) {
755 show_sends_button->set_text (_("Aux"));
758 gpm.gain_automation_style_button.set_text (
759 gpm.astyle_string(gain_automation->automation_style()));
760 gpm.gain_automation_state_button.set_text (
761 gpm.astate_string(gain_automation->automation_state()));
763 if (_route->panner()) {
764 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
765 panners.astyle_string(_route->panner()->automation_style()));
766 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
767 panners.astate_string(_route->panner()->automation_state()));
771 // panners expect an even number of horiz. pixels
772 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
774 set_size_request (width, -1);
780 if (show_sends_button) {
781 show_sends_button->set_text (_("Snd"));
784 gpm.gain_automation_style_button.set_text (
785 gpm.short_astyle_string(gain_automation->automation_style()));
786 gpm.gain_automation_state_button.set_text (
787 gpm.short_astate_string(gain_automation->automation_state()));
788 gain_meter().setup_meters (); // recalc meter width
790 if (_route->panner()) {
791 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
792 panners.short_astyle_string(_route->panner()->automation_style()));
793 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
794 panners.short_astate_string(_route->panner()->automation_state()));
798 // panners expect an even number of horiz. pixels
799 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
801 set_size_request (width, -1);
806 processor_box.set_width (w);
808 update_input_display ();
809 update_output_display ();
810 setup_comment_button ();
811 route_group_changed ();
817 MixerStrip::set_packed (bool yn)
822 set_gui_property ("visible", true);
824 set_gui_property ("visible", false);
829 struct RouteCompareByName {
830 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
831 return a->name().compare (b->name()) < 0;
836 MixerStrip::output_release (GdkEventButton *ev)
838 switch (ev->button) {
840 edit_output_configuration ();
848 MixerStrip::output_press (GdkEventButton *ev)
850 using namespace Menu_Helpers;
851 if (!_session->engine().connected()) {
852 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
857 MenuList& citems = output_menu.items();
858 switch (ev->button) {
861 return false; //wait for the mouse-up to pop the dialog
865 output_menu.set_name ("ArdourContextMenu");
867 output_menu_bundles.clear ();
869 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
871 citems.push_back (SeparatorElem());
872 uint32_t const n_with_separator = citems.size ();
874 ARDOUR::BundleList current = _route->output()->bundles_connected ();
876 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
878 /* give user bundles first chance at being in the menu */
880 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
881 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
882 maybe_add_bundle_to_output_menu (*i, current);
886 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
887 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
888 maybe_add_bundle_to_output_menu (*i, current);
892 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
893 RouteList copy = *routes;
894 copy.sort (RouteCompareByName ());
895 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
896 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
899 if (citems.size() == n_with_separator) {
900 /* no routes added; remove the separator */
904 if (!ARDOUR::Profile->get_mixbus()) {
905 citems.push_back (SeparatorElem());
907 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
910 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
911 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
917 citems.push_back (SeparatorElem());
918 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
920 output_menu.popup (1, ev->time);
931 MixerStrip::input_release (GdkEventButton *ev)
933 switch (ev->button) {
936 edit_input_configuration ();
948 MixerStrip::input_press (GdkEventButton *ev)
950 using namespace Menu_Helpers;
952 MenuList& citems = input_menu.items();
953 input_menu.set_name ("ArdourContextMenu");
956 if (!_session->engine().connected()) {
957 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
962 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
965 switch (ev->button) {
968 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
972 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
974 citems.push_back (SeparatorElem());
975 uint32_t const n_with_separator = citems.size ();
977 input_menu_bundles.clear ();
979 ARDOUR::BundleList current = _route->input()->bundles_connected ();
981 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
983 /* give user bundles first chance at being in the menu */
985 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
986 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
987 maybe_add_bundle_to_input_menu (*i, current);
991 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
992 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
993 maybe_add_bundle_to_input_menu (*i, current);
997 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
998 RouteList copy = *routes;
999 copy.sort (RouteCompareByName ());
1000 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1001 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1004 if (citems.size() == n_with_separator) {
1005 /* no routes added; remove the separator */
1009 citems.push_back (SeparatorElem());
1010 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1013 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1014 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1019 citems.push_back (SeparatorElem());
1020 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1022 input_menu.popup (1, ev->time);
1033 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1035 if (ignore_toggle) {
1039 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1041 if (std::find (current.begin(), current.end(), c) == current.end()) {
1042 _route->input()->connect_ports_to_bundle (c, true, this);
1044 _route->input()->disconnect_ports_from_bundle (c, this);
1049 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1051 if (ignore_toggle) {
1055 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1057 if (std::find (current.begin(), current.end(), c) == current.end()) {
1058 _route->output()->connect_ports_to_bundle (c, true, this);
1060 _route->output()->disconnect_ports_from_bundle (c, this);
1065 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1067 using namespace Menu_Helpers;
1069 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1073 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1074 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1078 if (i != input_menu_bundles.end()) {
1082 input_menu_bundles.push_back (b);
1084 MenuList& citems = input_menu.items();
1086 std::string n = b->name ();
1087 replace_all (n, "_", " ");
1089 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1093 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1095 using namespace Menu_Helpers;
1097 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1101 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1102 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1106 if (i != output_menu_bundles.end()) {
1110 output_menu_bundles.push_back (b);
1112 MenuList& citems = output_menu.items();
1114 std::string n = b->name ();
1115 replace_all (n, "_", " ");
1117 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1121 MixerStrip::update_diskstream_display ()
1123 if (is_track() && input_selector) {
1124 input_selector->hide_all ();
1127 route_color_changed ();
1131 MixerStrip::connect_to_pan ()
1133 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1135 panstate_connection.disconnect ();
1136 panstyle_connection.disconnect ();
1138 if (!_route->panner()) {
1142 boost::shared_ptr<Pannable> p = _route->pannable ();
1144 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1145 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1147 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1148 * However, that only works a panner was previously set.
1150 * PannerUI must remain subscribed to _panshell->Changed() in case
1151 * we switch the panner eg. AUX-Send and back
1152 * _route->panner_shell()->Changed() vs _panshell->Changed
1154 if (panners._panner == 0) {
1155 panners.panshell_changed ();
1157 update_panner_choices();
1161 MixerStrip::update_panner_choices ()
1163 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1164 if (!_route->panner_shell()) { return; }
1166 uint32_t in = _route->output()->n_ports().n_audio();
1168 if (_route->panner()) {
1169 in = _route->panner()->in().n_audio();
1172 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1176 * Output port labelling
1177 * =====================
1179 * Case 1: Each output has one connection, all connections are to system:playback_%i
1180 * out 1 -> system:playback_1
1181 * out 2 -> system:playback_2
1182 * out 3 -> system:playback_3
1185 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1186 * out 1 -> ardour:track_x/in 1
1187 * out 2 -> ardour:track_x/in 2
1188 * Display as: track_x
1190 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1191 * out 1 -> program x:foo
1192 * out 2 -> program x:foo
1193 * Display as: program x
1195 * Case 4: No connections (Disconnected)
1198 * Default case (unusual routing):
1199 * Display as: *number of connections*
1203 * .-----------------------------------------------.
1205 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1206 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1207 * '-----------------------------------------------'
1208 * .-----------------------------------------------.
1211 * '-----------------------------------------------'
1215 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1219 boost::shared_ptr<Port> port;
1220 vector<string> port_connections;
1222 uint32_t total_connection_count = 0;
1223 uint32_t io_connection_count = 0;
1224 uint32_t ardour_connection_count = 0;
1225 uint32_t system_connection_count = 0;
1226 uint32_t other_connection_count = 0;
1227 uint32_t typed_connection_count = 0;
1229 ostringstream label;
1231 bool have_label = false;
1232 bool each_io_has_one_connection = true;
1234 string connection_name;
1235 string ardour_track_name;
1236 string other_connection_type;
1237 string system_ports;
1240 ostringstream tooltip;
1241 char * tooltip_cstr;
1243 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1244 DataType dt = DataType::AUDIO;
1245 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1246 dt = DataType::MIDI;
1247 // avoid further confusion with Midi-tracks that have a synth.
1248 // Audio-ports may be connected, but button says "Disconnected"
1249 tooltip << _("MIDI ");
1253 io_count = route->n_inputs().n_total();
1254 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1256 io_count = route->n_outputs().n_total();
1257 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1261 for (io_index = 0; io_index < io_count; ++io_index) {
1263 port = route->input()->nth (io_index);
1265 port = route->output()->nth (io_index);
1268 port_connections.clear ();
1269 port->get_connections(port_connections);
1271 //ignore any port connections that don't match our DataType
1272 if (port->type() != dt) {
1273 if (!port_connections.empty()) {
1274 ++typed_connection_count;
1279 io_connection_count = 0;
1281 if (!port_connections.empty()) {
1282 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1284 string& connection_name (*i);
1286 if (connection_name.find("system:") == 0) {
1287 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1290 if (io_connection_count == 0) {
1291 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1293 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1296 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1299 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1300 if (ardour_track_name.empty()) {
1301 // "ardour:Master/in 1" -> "ardour:Master/"
1302 string::size_type slash = connection_name.find("/");
1303 if (slash != string::npos) {
1304 ardour_track_name = connection_name.substr(0, slash + 1);
1308 if (connection_name.find(ardour_track_name) == 0) {
1309 ++ardour_connection_count;
1311 } else if (!pn.empty()) {
1312 if (system_ports.empty()) {
1315 system_ports += "/" + pn;
1317 if (connection_name.find("system:") == 0) {
1318 ++system_connection_count;
1320 } else if (connection_name.find("system:midi_") == 0) {
1322 // "system:midi_capture_123" -> "123"
1323 system_port = "M " + connection_name.substr(20);
1325 // "system:midi_playback_123" -> "123"
1326 system_port = "M " + connection_name.substr(21);
1329 if (system_ports.empty()) {
1330 system_ports += system_port;
1332 system_ports += "/" + system_port;
1335 ++system_connection_count;
1337 } else if (connection_name.find("system:") == 0) {
1339 // "system:capture_123" -> "123"
1340 system_port = connection_name.substr(15);
1342 // "system:playback_123" -> "123"
1343 system_port = connection_name.substr(16);
1346 if (system_ports.empty()) {
1347 system_ports += system_port;
1349 system_ports += "/" + system_port;
1352 ++system_connection_count;
1354 if (other_connection_type.empty()) {
1355 // "jamin:in 1" -> "jamin:"
1356 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1359 if (connection_name.find(other_connection_type) == 0) {
1360 ++other_connection_count;
1364 ++total_connection_count;
1365 ++io_connection_count;
1369 if (io_connection_count != 1) {
1370 each_io_has_one_connection = false;
1374 if (total_connection_count == 0) {
1375 tooltip << endl << _("Disconnected");
1378 tooltip_cstr = new char[tooltip.str().size() + 1];
1379 strcpy(tooltip_cstr, tooltip.str().c_str());
1382 set_tooltip (&input_button, tooltip_cstr);
1384 set_tooltip (&output_button, tooltip_cstr);
1387 delete [] tooltip_cstr;
1389 if (each_io_has_one_connection) {
1390 if (total_connection_count == ardour_connection_count) {
1391 // all connections are to the same track in ardour
1392 // "ardour:Master/" -> "Master"
1393 string::size_type slash = ardour_track_name.find("/");
1394 if (slash != string::npos) {
1395 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1396 label << ardour_track_name.substr (ppps, slash - ppps);
1400 else if (total_connection_count == system_connection_count) {
1401 // all connections are to system ports
1402 label << system_ports;
1405 else if (total_connection_count == other_connection_count) {
1406 // all connections are to the same external program eg jamin
1407 // "jamin:" -> "jamin"
1408 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1414 if (total_connection_count == 0) {
1418 // Odd configuration
1419 label << "*" << total_connection_count << "*";
1421 if (typed_connection_count > 0) {
1422 label << "\u2295"; // circled plus
1427 input_button.set_text (label.str());
1429 output_button.set_text (label.str());
1434 MixerStrip::update_input_display ()
1436 update_io_button (_route, _width, true);
1437 panners.setup_pan ();
1439 if (has_audio_outputs ()) {
1440 panners.show_all ();
1442 panners.hide_all ();
1448 MixerStrip::update_output_display ()
1450 update_io_button (_route, _width, false);
1451 gpm.setup_meters ();
1452 panners.setup_pan ();
1454 if (has_audio_outputs ()) {
1455 panners.show_all ();
1457 panners.hide_all ();
1462 MixerStrip::fast_update ()
1464 gpm.update_meters ();
1468 MixerStrip::diskstream_changed ()
1470 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1474 MixerStrip::io_changed_proxy ()
1476 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1480 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1482 boost::shared_ptr<Port> a = wa.lock ();
1483 boost::shared_ptr<Port> b = wb.lock ();
1485 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1486 update_input_display ();
1487 set_width_enum (_width, this);
1490 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1491 update_output_display ();
1492 set_width_enum (_width, this);
1497 MixerStrip::setup_comment_button ()
1502 if (_route->comment().empty ()) {
1503 _comment_button.unset_bg (STATE_NORMAL);
1504 _comment_button.set_text (_("Comments"));
1506 _comment_button.modify_bg (STATE_NORMAL, color ());
1507 _comment_button.set_text (_("*Comments*"));
1512 if (_route->comment().empty ()) {
1513 _comment_button.unset_bg (STATE_NORMAL);
1514 _comment_button.set_text (_("Cmt"));
1516 _comment_button.modify_bg (STATE_NORMAL, color ());
1517 _comment_button.set_text (_("*Cmt*"));
1523 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1529 MixerStrip::select_route_group (GdkEventButton *ev)
1531 using namespace Menu_Helpers;
1533 if (ev->button == 1) {
1535 if (group_menu == 0) {
1537 PropertyList* plist = new PropertyList();
1539 plist->add (Properties::group_gain, true);
1540 plist->add (Properties::group_mute, true);
1541 plist->add (Properties::group_solo, true);
1543 group_menu = new RouteGroupMenu (_session, plist);
1547 r.push_back (route ());
1548 group_menu->build (r);
1549 group_menu->menu()->popup (1, ev->time);
1556 MixerStrip::route_group_changed ()
1558 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1560 RouteGroup *rg = _route->route_group();
1563 group_button.set_text (PBD::short_version (rg->name(), 5));
1567 group_button.set_text (_("Grp"));
1570 group_button.set_text (_("~G"));
1577 MixerStrip::route_color_changed ()
1579 name_button.modify_bg (STATE_NORMAL, color());
1580 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1581 reset_strip_style ();
1585 MixerStrip::show_passthru_color ()
1587 reset_strip_style ();
1592 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1594 boost::shared_ptr<Processor> processor (p.lock ());
1595 if (!processor || !processor->display_to_user()) {
1598 if (boost::dynamic_pointer_cast<PluginInsert> (processor)) {
1599 ++_plugin_insert_cnt;
1603 MixerStrip::build_route_ops_menu ()
1605 using namespace Menu_Helpers;
1606 route_ops_menu = new Menu;
1607 route_ops_menu->set_name ("ArdourContextMenu");
1609 MenuList& items = route_ops_menu->items();
1611 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1613 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1615 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1617 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1619 items.push_back (SeparatorElem());
1621 if (!_route->is_master()) {
1622 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1624 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1625 rename_menu_item = &items.back();
1627 items.push_back (SeparatorElem());
1628 items.push_back (CheckMenuElem (_("Active")));
1629 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1630 i->set_active (_route->active());
1631 i->set_sensitive(! _session->transport_rolling());
1632 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1634 if (!Profile->get_mixbus ()) {
1635 items.push_back (SeparatorElem());
1636 items.push_back (CheckMenuElem (_("Strict I/O")));
1637 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1638 i->set_active (_route->strict_io());
1639 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1642 _plugin_insert_cnt = 0;
1643 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1644 if (_plugin_insert_cnt > 0) {
1645 items.push_back (SeparatorElem());
1646 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1649 items.push_back (SeparatorElem());
1650 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1652 items.push_back (SeparatorElem());
1653 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1654 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1655 denormal_menu_item->set_active (_route->denormal_protection());
1658 /* note that this relies on selection being shared across editor and
1659 mixer (or global to the backend, in the future), which is the only
1660 sane thing for users anyway.
1663 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1665 Selection& selection (PublicEditor::instance().get_selection());
1666 if (!selection.selected (rtav)) {
1667 selection.set (rtav);
1670 if (!_route->is_master()) {
1671 items.push_back (SeparatorElem());
1672 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1675 items.push_back (SeparatorElem());
1676 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1682 MixerStrip::name_button_button_press (GdkEventButton* ev)
1684 if (ev->button == 3) {
1685 list_route_operations ();
1687 /* do not allow rename if the track is record-enabled */
1688 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1689 route_ops_menu->popup (1, ev->time);
1698 MixerStrip::name_button_button_release (GdkEventButton* ev)
1700 if (ev->button == 1) {
1701 list_route_operations ();
1703 /* do not allow rename if the track is record-enabled */
1704 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1705 route_ops_menu->popup (1, ev->time);
1712 MixerStrip::number_button_button_press (GdkEventButton* ev)
1714 if ( ev->button == 3 ) {
1715 list_route_operations ();
1717 /* do not allow rename if the track is record-enabled */
1718 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1719 route_ops_menu->popup (1, ev->time);
1728 MixerStrip::list_route_operations ()
1730 delete route_ops_menu;
1731 build_route_ops_menu ();
1735 MixerStrip::show_selected ()
1738 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1739 global_frame.set_name ("MixerStripSelectedFrame");
1741 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1742 global_frame.set_name ("MixerStripFrame");
1745 global_frame.queue_draw ();
1748 // processor_box.deselect_all_processors();
1752 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1754 if (what_changed.contains (ARDOUR::Properties::name)) {
1760 MixerStrip::name_changed ()
1764 name_button.set_text (_route->name());
1767 name_button.set_text (PBD::short_version (_route->name(), 5));
1771 set_tooltip (name_button, _route->name());
1773 if (_session->config.get_track_name_number()) {
1774 const int64_t track_number = _route->track_number ();
1775 if (track_number == 0) {
1776 number_label.set_text ("-");
1778 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1781 number_label.set_text ("");
1786 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1788 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1792 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1794 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1798 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1800 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1804 MixerStrip::width_button_pressed (GdkEventButton* ev)
1806 if (ev->button != 1) {
1810 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1813 _mixer.set_strip_width (Narrow, true);
1817 _mixer.set_strip_width (Wide, true);
1823 set_width_enum (Narrow, this);
1826 set_width_enum (Wide, this);
1835 MixerStrip::hide_clicked ()
1837 // LAME fix to reset the button status for when it is redisplayed (part 1)
1838 hide_button.set_sensitive(false);
1841 Hiding(); /* EMIT_SIGNAL */
1843 _mixer.hide_strip (this);
1847 hide_button.set_sensitive(true);
1851 MixerStrip::set_embedded (bool yn)
1857 MixerStrip::map_frozen ()
1859 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1861 boost::shared_ptr<AudioTrack> at = audio_track();
1864 switch (at->freeze_state()) {
1865 case AudioTrack::Frozen:
1866 processor_box.set_sensitive (false);
1867 hide_redirect_editors ();
1870 processor_box.set_sensitive (true);
1871 // XXX need some way, maybe, to retoggle redirect editors
1875 processor_box.set_sensitive (true);
1877 RouteUI::map_frozen ();
1881 MixerStrip::hide_redirect_editors ()
1883 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1887 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1889 boost::shared_ptr<Processor> processor (p.lock ());
1894 Gtk::Window* w = processor_box.get_processor_ui (processor);
1902 MixerStrip::reset_strip_style ()
1904 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1906 gpm.set_fader_name ("SendStripBase");
1910 if (is_midi_track()) {
1911 if (_route->active()) {
1912 set_name ("MidiTrackStripBase");
1914 set_name ("MidiTrackStripBaseInactive");
1916 gpm.set_fader_name ("MidiTrackFader");
1917 } else if (is_audio_track()) {
1918 if (_route->active()) {
1919 set_name ("AudioTrackStripBase");
1921 set_name ("AudioTrackStripBaseInactive");
1923 gpm.set_fader_name ("AudioTrackFader");
1925 if (_route->active()) {
1926 set_name ("AudioBusStripBase");
1928 set_name ("AudioBusStripBaseInactive");
1930 gpm.set_fader_name ("AudioBusFader");
1932 /* (no MIDI busses yet) */
1939 MixerStrip::engine_stopped ()
1944 MixerStrip::engine_running ()
1949 MixerStrip::meter_point_string (MeterPoint mp)
1962 case MeterPostFader:
1979 return S_("Meter|In");
1983 return S_("Meter|Pr");
1986 case MeterPostFader:
1987 return S_("Meter|Po");
1991 return S_("Meter|O");
1996 return S_("Meter|C");
2005 /** Called when the monitor-section state */
2007 MixerStrip::monitor_changed ()
2009 assert (monitor_section_button);
2010 if (_session->monitor_active()) {
2011 monitor_section_button->set_name ("master monitor section button active");
2013 monitor_section_button->set_name ("master monitor section button normal");
2017 /** Called when the metering point has changed */
2019 MixerStrip::meter_changed ()
2021 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2022 gpm.setup_meters ();
2023 // reset peak when meter point changes
2024 gpm.reset_peak_display();
2027 /** The bus that we are displaying sends to has changed, or been turned off.
2028 * @param send_to New bus that we are displaying sends to, or 0.
2031 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2033 RouteUI::bus_send_display_changed (send_to);
2036 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2041 revert_to_default_display ();
2044 revert_to_default_display ();
2049 MixerStrip::drop_send ()
2051 boost::shared_ptr<Send> current_send;
2053 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2054 current_send->set_metering (false);
2057 send_gone_connection.disconnect ();
2058 input_button.set_sensitive (true);
2059 output_button.set_sensitive (true);
2060 group_button.set_sensitive (true);
2061 set_invert_sensitive (true);
2062 meter_point_button.set_sensitive (true);
2063 mute_button->set_sensitive (true);
2064 solo_button->set_sensitive (true);
2065 solo_isolated_led->set_sensitive (true);
2066 solo_safe_led->set_sensitive (true);
2067 monitor_input_button->set_sensitive (true);
2068 monitor_disk_button->set_sensitive (true);
2069 _comment_button.set_sensitive (true);
2070 RouteUI::check_rec_enable_sensitivity ();
2071 set_button_names (); // update solo button visual state
2075 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2077 _current_delivery = d;
2078 DeliveryChanged (_current_delivery);
2082 MixerStrip::show_send (boost::shared_ptr<Send> send)
2088 set_current_delivery (send);
2090 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2091 send->set_metering (true);
2092 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2094 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2095 gain_meter().setup_meters ();
2097 uint32_t const in = _current_delivery->pans_required();
2098 uint32_t const out = _current_delivery->pan_outs();
2100 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2101 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2102 panner_ui().setup_pan ();
2103 panner_ui().set_send_drawing_mode (true);
2104 panner_ui().show_all ();
2106 input_button.set_sensitive (false);
2107 group_button.set_sensitive (false);
2108 set_invert_sensitive (false);
2109 meter_point_button.set_sensitive (false);
2110 mute_button->set_sensitive (false);
2111 solo_button->set_sensitive (false);
2112 rec_enable_button->set_sensitive (false);
2113 solo_isolated_led->set_sensitive (false);
2114 solo_safe_led->set_sensitive (false);
2115 monitor_input_button->set_sensitive (false);
2116 monitor_disk_button->set_sensitive (false);
2117 _comment_button.set_sensitive (false);
2119 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2120 output_button.set_sensitive (false);
2123 reset_strip_style ();
2127 MixerStrip::revert_to_default_display ()
2131 set_current_delivery (_route->main_outs ());
2133 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2134 gain_meter().setup_meters ();
2136 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2137 update_panner_choices();
2138 panner_ui().setup_pan ();
2139 panner_ui().set_send_drawing_mode (false);
2141 if (has_audio_outputs ()) {
2142 panners.show_all ();
2144 panners.hide_all ();
2147 reset_strip_style ();
2151 MixerStrip::set_button_names ()
2155 mute_button->set_text (_("Mute"));
2156 monitor_input_button->set_text (_("In"));
2157 monitor_disk_button->set_text (_("Disk"));
2158 if (monitor_section_button) {
2159 monitor_section_button->set_text (_("Mon"));
2162 if (_route && _route->solo_safe_control()->solo_safe()) {
2163 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2165 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2167 if (!Config->get_solo_control_is_listen_control()) {
2168 solo_button->set_text (_("Solo"));
2170 switch (Config->get_listen_position()) {
2171 case AfterFaderListen:
2172 solo_button->set_text (_("AFL"));
2174 case PreFaderListen:
2175 solo_button->set_text (_("PFL"));
2179 solo_isolated_led->set_text (_("Iso"));
2180 solo_safe_led->set_text (S_("SoloLock|Lock"));
2184 mute_button->set_text (S_("Mute|M"));
2185 monitor_input_button->set_text (S_("MonitorInput|I"));
2186 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2187 if (monitor_section_button) {
2188 monitor_section_button->set_text (S_("Mon|O"));
2191 if (_route && _route->solo_safe_control()->solo_safe()) {
2192 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2194 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2196 if (!Config->get_solo_control_is_listen_control()) {
2197 solo_button->set_text (S_("Solo|S"));
2199 switch (Config->get_listen_position()) {
2200 case AfterFaderListen:
2201 solo_button->set_text (S_("AfterFader|A"));
2203 case PreFaderListen:
2204 solo_button->set_text (S_("Prefader|P"));
2209 solo_isolated_led->set_text (S_("SoloIso|I"));
2210 solo_safe_led->set_text (S_("SoloLock|L"));
2215 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2217 meter_point_button.set_text ("");
2222 MixerStrip::plugin_selector()
2224 return _mixer.plugin_selector();
2228 MixerStrip::hide_things ()
2230 processor_box.hide_things ();
2234 MixerStrip::input_active_button_press (GdkEventButton*)
2236 /* nothing happens on press */
2241 MixerStrip::input_active_button_release (GdkEventButton* ev)
2243 boost::shared_ptr<MidiTrack> mt = midi_track ();
2249 boost::shared_ptr<RouteList> rl (new RouteList);
2251 rl->push_back (route());
2253 _session->set_exclusive_input_active (rl, !mt->input_active(),
2254 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2260 MixerStrip::midi_input_status_changed ()
2262 if (midi_input_enable_button) {
2263 boost::shared_ptr<MidiTrack> mt = midi_track ();
2265 midi_input_enable_button->set_active (mt->input_active ());
2270 MixerStrip::state_id () const
2272 return string_compose ("strip %1", _route->id().to_s());
2276 MixerStrip::parameter_changed (string p)
2278 if (p == _visibility.get_state_name()) {
2279 /* The user has made changes to the mixer strip visibility, so get
2280 our VisibilityGroup to reflect these changes in our widgets.
2282 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2283 } else if (p == "track-name-number") {
2285 } else if (p == "use-monitor-bus") {
2286 if (monitor_section_button) {
2287 if (mute_button->get_parent()) {
2288 mute_button->get_parent()->remove(*mute_button);
2290 if (monitor_section_button->get_parent()) {
2291 monitor_section_button->get_parent()->remove(*monitor_section_button);
2293 if (Config->get_use_monitor_bus ()) {
2294 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2295 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2296 mute_button->show();
2297 monitor_section_button->show();
2299 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2300 mute_button->show();
2303 } else if (p == "track-name-number") {
2304 update_track_number_visibility();
2308 /** Called to decide whether the solo isolate / solo lock button visibility should
2309 * be overridden from that configured by the user. We do this for the master bus.
2311 * @return optional value that is present if visibility state should be overridden.
2313 boost::optional<bool>
2314 MixerStrip::override_solo_visibility () const
2316 if (_route && _route->is_master ()) {
2317 return boost::optional<bool> (false);
2320 return boost::optional<bool> ();
2324 MixerStrip::add_input_port (DataType t)
2326 _route->input()->add_port ("", this, t);
2330 MixerStrip::add_output_port (DataType t)
2332 _route->output()->add_port ("", this, t);
2336 MixerStrip::route_active_changed ()
2338 reset_strip_style ();
2342 MixerStrip::copy_processors ()
2344 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2348 MixerStrip::cut_processors ()
2350 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2354 MixerStrip::paste_processors ()
2356 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2360 MixerStrip::select_all_processors ()
2362 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2366 MixerStrip::deselect_all_processors ()
2368 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2372 MixerStrip::delete_processors ()
2374 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2378 MixerStrip::toggle_processors ()
2380 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2384 MixerStrip::ab_plugins ()
2386 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2390 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2392 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2395 if (ev->button == 3) {
2396 popup_level_meter_menu (ev);
2404 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2406 using namespace Gtk::Menu_Helpers;
2408 Gtk::Menu* m = manage (new Menu);
2409 MenuList& items = m->items ();
2411 RadioMenuItem::Group group;
2413 _suspend_menu_callbacks = true;
2414 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2415 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2416 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2417 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2418 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2420 if (gpm.meter_channels().n_audio() == 0) {
2421 m->popup (ev->button, ev->time);
2422 _suspend_menu_callbacks = false;
2426 RadioMenuItem::Group tgroup;
2427 items.push_back (SeparatorElem());
2429 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2430 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2431 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2432 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2433 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2434 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2435 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2436 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2437 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2438 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2439 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2442 if (_route->is_master()) {
2445 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2446 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2447 /* non-master bus */
2450 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2457 MeterType cmt = _route->meter_type();
2458 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2460 items.push_back (SeparatorElem());
2461 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2462 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2463 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2464 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2465 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2466 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2468 m->popup (ev->button, ev->time);
2469 _suspend_menu_callbacks = false;
2473 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2474 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2476 using namespace Menu_Helpers;
2478 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2479 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2480 i->set_active (_route->meter_point() == point);
2484 MixerStrip::set_meter_point (MeterPoint p)
2486 if (_suspend_menu_callbacks) return;
2487 _route->set_meter_point (p);
2491 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2492 RadioMenuItem::Group& group, string const & name, MeterType type)
2494 using namespace Menu_Helpers;
2496 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2497 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2498 i->set_active (_route->meter_type() == type);
2502 MixerStrip::set_meter_type (MeterType t)
2504 if (_suspend_menu_callbacks) return;
2509 MixerStrip::vca_menu_toggle (Gtk::CheckMenuItem* menuitem, uint32_t n)
2515 boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2522 /* if this strip is not selected, add it before carrying out
2523 changes to assignment. the user probably didn't notice
2524 that they were clicking on an unselected track.
2526 _mixer.select_strip (*this);
2529 if (!menuitem->get_active()) {
2530 _mixer.do_vca_unassign (vca);
2532 _mixer.do_vca_assign (vca);
2537 MixerStrip::vca_assign (boost::shared_ptr<VCA> vca)
2539 if (!vca || !_route) {
2543 _route->assign (vca);
2547 MixerStrip::vca_unassign (boost::shared_ptr<VCA> vca)
2553 _route->unassign (vca);
2557 MixerStrip::vca_button_release (GdkEventButton* ev)
2559 using namespace Gtk::Menu_Helpers;
2565 /* primary click only */
2567 if (ev->button != 1) {
2572 /* no route - nothing to do */
2576 VCAList vcas (_session->vca_manager().vcas());
2579 /* the button should not have been visible under these conditions */
2583 Menu* menu = new Menu;
2584 MenuList& items = menu->items();
2586 items.push_back (MenuElem (_("Unassign"), sigc::bind (sigc::mem_fun (_mixer, &Mixer_UI::do_vca_unassign), boost::shared_ptr<VCA>())));
2588 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2589 items.push_back (CheckMenuElem ((*v)->name()));
2590 Gtk::CheckMenuItem* item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2591 if (_route->slaved_to (*v)) {
2592 item->set_active (true);
2594 item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), item, (*v)->number()));
2597 menu->popup (1, ev->time);
2603 MixerStrip::update_track_number_visibility ()
2605 DisplaySuspender ds;
2606 bool show_label = _session->config.get_track_name_number();
2608 if (_route && _route->is_master()) {
2613 number_label.show ();
2614 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2615 // except the width of the number label is subtracted from the name-hbox, so we
2616 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2617 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2619 number_label.set_size_request(tnw, -1);
2620 number_label.show ();
2622 number_label.hide ();
2627 MixerStrip::color () const
2629 return route_color ();
2633 MixerStrip::marked_for_display () const
2635 return !_route->presentation_info().hidden();
2639 MixerStrip::set_marked_for_display (bool yn)
2641 return RouteUI::mark_hidden (!yn);