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)
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)
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());
652 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
654 _route->gain_control()->MasterStatusChange.connect (route_connections,
656 boost::bind (&MixerStrip::update_vca_display, this),
659 set_stuff_from_route ();
661 /* now force an update of all the various elements */
663 update_mute_display ();
664 update_solo_display ();
665 update_vca_display ();
668 route_group_changed ();
669 update_track_number_visibility ();
672 panners.setup_pan ();
674 if (has_audio_outputs ()) {
680 update_diskstream_display ();
681 update_input_display ();
682 update_output_display ();
684 add_events (Gdk::BUTTON_RELEASE_MASK);
686 processor_box.show ();
688 if (!route()->is_master() && !route()->is_monitor()) {
689 /* we don't allow master or control routes to be hidden */
694 gpm.reset_peak_display ();
695 gpm.gain_display.show ();
696 gpm.peak_display.show ();
699 width_hide_box.show();
701 global_vpacker.show();
702 mute_solo_table.show();
703 bottom_button_table.show();
705 meter_point_button.show();
706 input_button_box.show_all();
707 output_button.show();
709 _comment_button.show();
711 gpm.gain_automation_state_button.show();
713 parameter_changed ("mixer-element-visibility");
720 MixerStrip::set_stuff_from_route ()
722 /* if width is not set, it will be set by the MixerUI or editor */
724 string str = gui_property ("strip-width");
726 set_width_enum (Width (string_2_enum (str, _width)), this);
731 MixerStrip::set_width_enum (Width w, void* owner)
733 /* always set the gpm width again, things may be hidden */
736 panners.set_width (w);
738 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
740 _width_owner = owner;
744 if (_width_owner == this) {
745 set_gui_property ("strip-width", enum_2_string (_width));
750 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
755 if (show_sends_button) {
756 show_sends_button->set_text (_("Aux"));
759 gpm.gain_automation_style_button.set_text (
760 gpm.astyle_string(gain_automation->automation_style()));
761 gpm.gain_automation_state_button.set_text (
762 gpm.astate_string(gain_automation->automation_state()));
764 if (_route->panner()) {
765 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
766 panners.astyle_string(_route->panner()->automation_style()));
767 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
768 panners.astate_string(_route->panner()->automation_state()));
772 // panners expect an even number of horiz. pixels
773 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
775 set_size_request (width, -1);
781 if (show_sends_button) {
782 show_sends_button->set_text (_("Snd"));
785 gpm.gain_automation_style_button.set_text (
786 gpm.short_astyle_string(gain_automation->automation_style()));
787 gpm.gain_automation_state_button.set_text (
788 gpm.short_astate_string(gain_automation->automation_state()));
789 gain_meter().setup_meters (); // recalc meter width
791 if (_route->panner()) {
792 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
793 panners.short_astyle_string(_route->panner()->automation_style()));
794 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
795 panners.short_astate_string(_route->panner()->automation_state()));
799 // panners expect an even number of horiz. pixels
800 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
802 set_size_request (width, -1);
807 processor_box.set_width (w);
809 update_input_display ();
810 update_output_display ();
811 setup_comment_button ();
812 route_group_changed ();
818 MixerStrip::set_packed (bool yn)
823 set_gui_property ("visible", true);
825 set_gui_property ("visible", false);
830 struct RouteCompareByName {
831 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
832 return a->name().compare (b->name()) < 0;
837 MixerStrip::output_release (GdkEventButton *ev)
839 switch (ev->button) {
841 edit_output_configuration ();
849 MixerStrip::output_press (GdkEventButton *ev)
851 using namespace Menu_Helpers;
852 if (!_session->engine().connected()) {
853 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
858 MenuList& citems = output_menu.items();
859 switch (ev->button) {
862 return false; //wait for the mouse-up to pop the dialog
866 output_menu.set_name ("ArdourContextMenu");
868 output_menu_bundles.clear ();
870 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
872 citems.push_back (SeparatorElem());
873 uint32_t const n_with_separator = citems.size ();
875 ARDOUR::BundleList current = _route->output()->bundles_connected ();
877 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
879 /* give user bundles first chance at being in the menu */
881 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
882 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
883 maybe_add_bundle_to_output_menu (*i, current);
887 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
888 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
889 maybe_add_bundle_to_output_menu (*i, current);
893 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
894 RouteList copy = *routes;
895 copy.sort (RouteCompareByName ());
896 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
897 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
900 if (citems.size() == n_with_separator) {
901 /* no routes added; remove the separator */
905 if (!ARDOUR::Profile->get_mixbus()) {
906 citems.push_back (SeparatorElem());
908 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
911 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
912 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
918 citems.push_back (SeparatorElem());
919 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
921 output_menu.popup (1, ev->time);
932 MixerStrip::input_release (GdkEventButton *ev)
934 switch (ev->button) {
937 edit_input_configuration ();
949 MixerStrip::input_press (GdkEventButton *ev)
951 using namespace Menu_Helpers;
953 MenuList& citems = input_menu.items();
954 input_menu.set_name ("ArdourContextMenu");
957 if (!_session->engine().connected()) {
958 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
963 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
966 switch (ev->button) {
969 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
973 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
975 citems.push_back (SeparatorElem());
976 uint32_t const n_with_separator = citems.size ();
978 input_menu_bundles.clear ();
980 ARDOUR::BundleList current = _route->input()->bundles_connected ();
982 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
984 /* give user bundles first chance at being in the menu */
986 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
987 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
988 maybe_add_bundle_to_input_menu (*i, current);
992 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
993 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
994 maybe_add_bundle_to_input_menu (*i, current);
998 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
999 RouteList copy = *routes;
1000 copy.sort (RouteCompareByName ());
1001 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1002 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1005 if (citems.size() == n_with_separator) {
1006 /* no routes added; remove the separator */
1010 citems.push_back (SeparatorElem());
1011 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1014 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1015 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1020 citems.push_back (SeparatorElem());
1021 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1023 input_menu.popup (1, ev->time);
1034 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1036 if (ignore_toggle) {
1040 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1042 if (std::find (current.begin(), current.end(), c) == current.end()) {
1043 _route->input()->connect_ports_to_bundle (c, true, this);
1045 _route->input()->disconnect_ports_from_bundle (c, this);
1050 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1052 if (ignore_toggle) {
1056 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1058 if (std::find (current.begin(), current.end(), c) == current.end()) {
1059 _route->output()->connect_ports_to_bundle (c, true, this);
1061 _route->output()->disconnect_ports_from_bundle (c, this);
1066 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1068 using namespace Menu_Helpers;
1070 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1074 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1075 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1079 if (i != input_menu_bundles.end()) {
1083 input_menu_bundles.push_back (b);
1085 MenuList& citems = input_menu.items();
1087 std::string n = b->name ();
1088 replace_all (n, "_", " ");
1090 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1094 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1096 using namespace Menu_Helpers;
1098 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1102 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1103 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1107 if (i != output_menu_bundles.end()) {
1111 output_menu_bundles.push_back (b);
1113 MenuList& citems = output_menu.items();
1115 std::string n = b->name ();
1116 replace_all (n, "_", " ");
1118 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1122 MixerStrip::update_diskstream_display ()
1124 if (is_track() && input_selector) {
1125 input_selector->hide_all ();
1128 route_color_changed ();
1132 MixerStrip::connect_to_pan ()
1134 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1136 panstate_connection.disconnect ();
1137 panstyle_connection.disconnect ();
1139 if (!_route->panner()) {
1143 boost::shared_ptr<Pannable> p = _route->pannable ();
1145 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1146 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1148 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1149 * However, that only works a panner was previously set.
1151 * PannerUI must remain subscribed to _panshell->Changed() in case
1152 * we switch the panner eg. AUX-Send and back
1153 * _route->panner_shell()->Changed() vs _panshell->Changed
1155 if (panners._panner == 0) {
1156 panners.panshell_changed ();
1158 update_panner_choices();
1162 MixerStrip::update_panner_choices ()
1164 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1165 if (!_route->panner_shell()) { return; }
1167 uint32_t in = _route->output()->n_ports().n_audio();
1169 if (_route->panner()) {
1170 in = _route->panner()->in().n_audio();
1173 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1177 * Output port labelling
1178 * =====================
1180 * Case 1: Each output has one connection, all connections are to system:playback_%i
1181 * out 1 -> system:playback_1
1182 * out 2 -> system:playback_2
1183 * out 3 -> system:playback_3
1186 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1187 * out 1 -> ardour:track_x/in 1
1188 * out 2 -> ardour:track_x/in 2
1189 * Display as: track_x
1191 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1192 * out 1 -> program x:foo
1193 * out 2 -> program x:foo
1194 * Display as: program x
1196 * Case 4: No connections (Disconnected)
1199 * Default case (unusual routing):
1200 * Display as: *number of connections*
1204 * .-----------------------------------------------.
1206 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1207 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1208 * '-----------------------------------------------'
1209 * .-----------------------------------------------.
1212 * '-----------------------------------------------'
1216 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1220 boost::shared_ptr<Port> port;
1221 vector<string> port_connections;
1223 uint32_t total_connection_count = 0;
1224 uint32_t io_connection_count = 0;
1225 uint32_t ardour_connection_count = 0;
1226 uint32_t system_connection_count = 0;
1227 uint32_t other_connection_count = 0;
1228 uint32_t typed_connection_count = 0;
1230 ostringstream label;
1232 bool have_label = false;
1233 bool each_io_has_one_connection = true;
1235 string connection_name;
1236 string ardour_track_name;
1237 string other_connection_type;
1238 string system_ports;
1241 ostringstream tooltip;
1242 char * tooltip_cstr;
1244 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1245 DataType dt = DataType::AUDIO;
1246 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1247 dt = DataType::MIDI;
1248 // avoid further confusion with Midi-tracks that have a synth.
1249 // Audio-ports may be connected, but button says "Disconnected"
1250 tooltip << _("MIDI ");
1254 io_count = route->n_inputs().n_total();
1255 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1257 io_count = route->n_outputs().n_total();
1258 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1262 for (io_index = 0; io_index < io_count; ++io_index) {
1264 port = route->input()->nth (io_index);
1266 port = route->output()->nth (io_index);
1269 port_connections.clear ();
1270 port->get_connections(port_connections);
1272 //ignore any port connections that don't match our DataType
1273 if (port->type() != dt) {
1274 if (!port_connections.empty()) {
1275 ++typed_connection_count;
1280 io_connection_count = 0;
1282 if (!port_connections.empty()) {
1283 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1285 string& connection_name (*i);
1287 if (connection_name.find("system:") == 0) {
1288 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1291 if (io_connection_count == 0) {
1292 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1294 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1297 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1300 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1301 if (ardour_track_name.empty()) {
1302 // "ardour:Master/in 1" -> "ardour:Master/"
1303 string::size_type slash = connection_name.find("/");
1304 if (slash != string::npos) {
1305 ardour_track_name = connection_name.substr(0, slash + 1);
1309 if (connection_name.find(ardour_track_name) == 0) {
1310 ++ardour_connection_count;
1312 } else if (!pn.empty()) {
1313 if (system_ports.empty()) {
1316 system_ports += "/" + pn;
1318 if (connection_name.find("system:") == 0) {
1319 ++system_connection_count;
1321 } else if (connection_name.find("system:midi_") == 0) {
1323 // "system:midi_capture_123" -> "123"
1324 system_port = "M " + connection_name.substr(20);
1326 // "system:midi_playback_123" -> "123"
1327 system_port = "M " + connection_name.substr(21);
1330 if (system_ports.empty()) {
1331 system_ports += system_port;
1333 system_ports += "/" + system_port;
1336 ++system_connection_count;
1338 } else if (connection_name.find("system:") == 0) {
1340 // "system:capture_123" -> "123"
1341 system_port = connection_name.substr(15);
1343 // "system:playback_123" -> "123"
1344 system_port = connection_name.substr(16);
1347 if (system_ports.empty()) {
1348 system_ports += system_port;
1350 system_ports += "/" + system_port;
1353 ++system_connection_count;
1355 if (other_connection_type.empty()) {
1356 // "jamin:in 1" -> "jamin:"
1357 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1360 if (connection_name.find(other_connection_type) == 0) {
1361 ++other_connection_count;
1365 ++total_connection_count;
1366 ++io_connection_count;
1370 if (io_connection_count != 1) {
1371 each_io_has_one_connection = false;
1375 if (total_connection_count == 0) {
1376 tooltip << endl << _("Disconnected");
1379 tooltip_cstr = new char[tooltip.str().size() + 1];
1380 strcpy(tooltip_cstr, tooltip.str().c_str());
1383 set_tooltip (&input_button, tooltip_cstr);
1385 set_tooltip (&output_button, tooltip_cstr);
1388 delete [] tooltip_cstr;
1390 if (each_io_has_one_connection) {
1391 if (total_connection_count == ardour_connection_count) {
1392 // all connections are to the same track in ardour
1393 // "ardour:Master/" -> "Master"
1394 string::size_type slash = ardour_track_name.find("/");
1395 if (slash != string::npos) {
1396 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1397 label << ardour_track_name.substr (ppps, slash - ppps);
1401 else if (total_connection_count == system_connection_count) {
1402 // all connections are to system ports
1403 label << system_ports;
1406 else if (total_connection_count == other_connection_count) {
1407 // all connections are to the same external program eg jamin
1408 // "jamin:" -> "jamin"
1409 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1415 if (total_connection_count == 0) {
1419 // Odd configuration
1420 label << "*" << total_connection_count << "*";
1422 if (typed_connection_count > 0) {
1423 label << "\u2295"; // circled plus
1428 input_button.set_text (label.str());
1430 output_button.set_text (label.str());
1435 MixerStrip::update_input_display ()
1437 update_io_button (_route, _width, true);
1438 panners.setup_pan ();
1440 if (has_audio_outputs ()) {
1441 panners.show_all ();
1443 panners.hide_all ();
1449 MixerStrip::update_output_display ()
1451 update_io_button (_route, _width, false);
1452 gpm.setup_meters ();
1453 panners.setup_pan ();
1455 if (has_audio_outputs ()) {
1456 panners.show_all ();
1458 panners.hide_all ();
1463 MixerStrip::fast_update ()
1465 gpm.update_meters ();
1469 MixerStrip::diskstream_changed ()
1471 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1475 MixerStrip::io_changed_proxy ()
1477 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1481 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1483 boost::shared_ptr<Port> a = wa.lock ();
1484 boost::shared_ptr<Port> b = wb.lock ();
1486 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1487 update_input_display ();
1488 set_width_enum (_width, this);
1491 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1492 update_output_display ();
1493 set_width_enum (_width, this);
1498 MixerStrip::setup_comment_button ()
1503 if (_route->comment().empty ()) {
1504 _comment_button.unset_bg (STATE_NORMAL);
1505 _comment_button.set_text (_("Comments"));
1507 _comment_button.modify_bg (STATE_NORMAL, color ());
1508 _comment_button.set_text (_("*Comments*"));
1513 if (_route->comment().empty ()) {
1514 _comment_button.unset_bg (STATE_NORMAL);
1515 _comment_button.set_text (_("Cmt"));
1517 _comment_button.modify_bg (STATE_NORMAL, color ());
1518 _comment_button.set_text (_("*Cmt*"));
1524 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1530 MixerStrip::select_route_group (GdkEventButton *ev)
1532 using namespace Menu_Helpers;
1534 if (ev->button == 1) {
1536 if (group_menu == 0) {
1538 PropertyList* plist = new PropertyList();
1540 plist->add (Properties::gain, true);
1541 plist->add (Properties::mute, true);
1542 plist->add (Properties::solo, true);
1544 group_menu = new RouteGroupMenu (_session, plist);
1548 r.push_back (route ());
1549 group_menu->build (r);
1550 group_menu->menu()->popup (1, ev->time);
1557 MixerStrip::route_group_changed ()
1559 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1561 RouteGroup *rg = _route->route_group();
1564 group_button.set_text (PBD::short_version (rg->name(), 5));
1568 group_button.set_text (_("Grp"));
1571 group_button.set_text (_("~G"));
1578 MixerStrip::route_color_changed ()
1580 name_button.modify_bg (STATE_NORMAL, color());
1581 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1582 reset_strip_style ();
1586 MixerStrip::show_passthru_color ()
1588 reset_strip_style ();
1593 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1595 boost::shared_ptr<Processor> processor (p.lock ());
1596 if (!processor || !processor->display_to_user()) {
1599 if (boost::dynamic_pointer_cast<PluginInsert> (processor)) {
1600 ++_plugin_insert_cnt;
1604 MixerStrip::build_route_ops_menu ()
1606 using namespace Menu_Helpers;
1607 route_ops_menu = new Menu;
1608 route_ops_menu->set_name ("ArdourContextMenu");
1610 MenuList& items = route_ops_menu->items();
1612 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1614 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1616 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1618 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1620 items.push_back (SeparatorElem());
1622 if (!_route->is_master()) {
1623 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1625 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1626 rename_menu_item = &items.back();
1628 items.push_back (SeparatorElem());
1629 items.push_back (CheckMenuElem (_("Active")));
1630 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1631 i->set_active (_route->active());
1632 i->set_sensitive(! _session->transport_rolling());
1633 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1635 if (!Profile->get_mixbus ()) {
1636 items.push_back (SeparatorElem());
1637 items.push_back (CheckMenuElem (_("Strict I/O")));
1638 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1639 i->set_active (_route->strict_io());
1640 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1643 _plugin_insert_cnt = 0;
1644 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1645 if (_plugin_insert_cnt > 0) {
1646 items.push_back (SeparatorElem());
1647 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1650 items.push_back (SeparatorElem());
1651 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1653 items.push_back (SeparatorElem());
1654 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1655 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1656 denormal_menu_item->set_active (_route->denormal_protection());
1659 /* note that this relies on selection being shared across editor and
1660 mixer (or global to the backend, in the future), which is the only
1661 sane thing for users anyway.
1664 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1666 Selection& selection (PublicEditor::instance().get_selection());
1667 if (!selection.selected (rtav)) {
1668 selection.set (rtav);
1671 if (!_route->is_master()) {
1672 items.push_back (SeparatorElem());
1673 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1676 items.push_back (SeparatorElem());
1677 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1683 MixerStrip::name_button_button_press (GdkEventButton* ev)
1685 if (ev->button == 3) {
1686 list_route_operations ();
1688 /* do not allow rename if the track is record-enabled */
1689 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1690 route_ops_menu->popup (1, ev->time);
1699 MixerStrip::name_button_button_release (GdkEventButton* ev)
1701 if (ev->button == 1) {
1702 list_route_operations ();
1704 /* do not allow rename if the track is record-enabled */
1705 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1706 route_ops_menu->popup (1, ev->time);
1713 MixerStrip::number_button_button_press (GdkEventButton* ev)
1715 if ( ev->button == 3 ) {
1716 list_route_operations ();
1718 /* do not allow rename if the track is record-enabled */
1719 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1720 route_ops_menu->popup (1, ev->time);
1729 MixerStrip::list_route_operations ()
1731 delete route_ops_menu;
1732 build_route_ops_menu ();
1736 MixerStrip::set_selected (bool yn)
1738 AxisView::set_selected (yn);
1740 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1741 global_frame.set_name ("MixerStripSelectedFrame");
1743 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1744 global_frame.set_name ("MixerStripFrame");
1746 global_frame.queue_draw ();
1749 // processor_box.deselect_all_processors();
1753 MixerStrip::property_changed (const PropertyChange& what_changed)
1755 RouteUI::property_changed (what_changed);
1757 if (what_changed.contains (ARDOUR::Properties::name)) {
1763 MixerStrip::name_changed ()
1767 name_button.set_text (_route->name());
1770 name_button.set_text (PBD::short_version (_route->name(), 5));
1774 set_tooltip (name_button, _route->name());
1776 if (_session->config.get_track_name_number()) {
1777 const int64_t track_number = _route->track_number ();
1778 if (track_number == 0) {
1779 number_label.set_text ("-");
1781 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1784 number_label.set_text ("");
1789 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1791 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1795 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1797 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1801 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1803 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1807 MixerStrip::width_button_pressed (GdkEventButton* ev)
1809 if (ev->button != 1) {
1813 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1816 _mixer.set_strip_width (Narrow, true);
1820 _mixer.set_strip_width (Wide, true);
1826 set_width_enum (Narrow, this);
1829 set_width_enum (Wide, this);
1838 MixerStrip::hide_clicked ()
1840 // LAME fix to reset the button status for when it is redisplayed (part 1)
1841 hide_button.set_sensitive(false);
1844 Hiding(); /* EMIT_SIGNAL */
1846 _mixer.hide_strip (this);
1850 hide_button.set_sensitive(true);
1854 MixerStrip::set_embedded (bool yn)
1860 MixerStrip::map_frozen ()
1862 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1864 boost::shared_ptr<AudioTrack> at = audio_track();
1867 switch (at->freeze_state()) {
1868 case AudioTrack::Frozen:
1869 processor_box.set_sensitive (false);
1870 hide_redirect_editors ();
1873 processor_box.set_sensitive (true);
1874 // XXX need some way, maybe, to retoggle redirect editors
1878 processor_box.set_sensitive (true);
1880 RouteUI::map_frozen ();
1884 MixerStrip::hide_redirect_editors ()
1886 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1890 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1892 boost::shared_ptr<Processor> processor (p.lock ());
1897 Gtk::Window* w = processor_box.get_processor_ui (processor);
1905 MixerStrip::reset_strip_style ()
1907 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1909 gpm.set_fader_name ("SendStripBase");
1913 if (is_midi_track()) {
1914 if (_route->active()) {
1915 set_name ("MidiTrackStripBase");
1917 set_name ("MidiTrackStripBaseInactive");
1919 gpm.set_fader_name ("MidiTrackFader");
1920 } else if (is_audio_track()) {
1921 if (_route->active()) {
1922 set_name ("AudioTrackStripBase");
1924 set_name ("AudioTrackStripBaseInactive");
1926 gpm.set_fader_name ("AudioTrackFader");
1928 if (_route->active()) {
1929 set_name ("AudioBusStripBase");
1931 set_name ("AudioBusStripBaseInactive");
1933 gpm.set_fader_name ("AudioBusFader");
1935 /* (no MIDI busses yet) */
1942 MixerStrip::engine_stopped ()
1947 MixerStrip::engine_running ()
1952 MixerStrip::meter_point_string (MeterPoint mp)
1965 case MeterPostFader:
1982 return S_("Meter|In");
1986 return S_("Meter|Pr");
1989 case MeterPostFader:
1990 return S_("Meter|Po");
1994 return S_("Meter|O");
1999 return S_("Meter|C");
2008 /** Called when the monitor-section state */
2010 MixerStrip::monitor_changed ()
2012 assert (monitor_section_button);
2013 if (_session->monitor_active()) {
2014 monitor_section_button->set_name ("master monitor section button active");
2016 monitor_section_button->set_name ("master monitor section button normal");
2020 /** Called when the metering point has changed */
2022 MixerStrip::meter_changed ()
2024 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2025 gpm.setup_meters ();
2026 // reset peak when meter point changes
2027 gpm.reset_peak_display();
2030 /** The bus that we are displaying sends to has changed, or been turned off.
2031 * @param send_to New bus that we are displaying sends to, or 0.
2034 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2036 RouteUI::bus_send_display_changed (send_to);
2039 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2044 revert_to_default_display ();
2047 revert_to_default_display ();
2052 MixerStrip::drop_send ()
2054 boost::shared_ptr<Send> current_send;
2056 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2057 current_send->set_metering (false);
2060 send_gone_connection.disconnect ();
2061 input_button.set_sensitive (true);
2062 output_button.set_sensitive (true);
2063 group_button.set_sensitive (true);
2064 set_invert_sensitive (true);
2065 meter_point_button.set_sensitive (true);
2066 mute_button->set_sensitive (true);
2067 solo_button->set_sensitive (true);
2068 solo_isolated_led->set_sensitive (true);
2069 solo_safe_led->set_sensitive (true);
2070 monitor_input_button->set_sensitive (true);
2071 monitor_disk_button->set_sensitive (true);
2072 _comment_button.set_sensitive (true);
2073 RouteUI::check_rec_enable_sensitivity ();
2074 set_button_names (); // update solo button visual state
2078 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2080 _current_delivery = d;
2081 DeliveryChanged (_current_delivery);
2085 MixerStrip::show_send (boost::shared_ptr<Send> send)
2091 set_current_delivery (send);
2093 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2094 send->set_metering (true);
2095 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2097 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2098 gain_meter().setup_meters ();
2100 uint32_t const in = _current_delivery->pans_required();
2101 uint32_t const out = _current_delivery->pan_outs();
2103 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2104 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2105 panner_ui().setup_pan ();
2106 panner_ui().set_send_drawing_mode (true);
2107 panner_ui().show_all ();
2109 input_button.set_sensitive (false);
2110 group_button.set_sensitive (false);
2111 set_invert_sensitive (false);
2112 meter_point_button.set_sensitive (false);
2113 mute_button->set_sensitive (false);
2114 solo_button->set_sensitive (false);
2115 rec_enable_button->set_sensitive (false);
2116 solo_isolated_led->set_sensitive (false);
2117 solo_safe_led->set_sensitive (false);
2118 monitor_input_button->set_sensitive (false);
2119 monitor_disk_button->set_sensitive (false);
2120 _comment_button.set_sensitive (false);
2122 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2123 output_button.set_sensitive (false);
2126 reset_strip_style ();
2130 MixerStrip::revert_to_default_display ()
2134 set_current_delivery (_route->main_outs ());
2136 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2137 gain_meter().setup_meters ();
2139 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2140 update_panner_choices();
2141 panner_ui().setup_pan ();
2142 panner_ui().set_send_drawing_mode (false);
2144 if (has_audio_outputs ()) {
2145 panners.show_all ();
2147 panners.hide_all ();
2150 reset_strip_style ();
2154 MixerStrip::set_button_names ()
2158 mute_button->set_text (_("Mute"));
2159 monitor_input_button->set_text (_("In"));
2160 monitor_disk_button->set_text (_("Disk"));
2161 if (monitor_section_button) {
2162 monitor_section_button->set_text (_("Mon"));
2165 if (_route && _route->solo_safe_control()->solo_safe()) {
2166 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2168 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2170 if (!Config->get_solo_control_is_listen_control()) {
2171 solo_button->set_text (_("Solo"));
2173 switch (Config->get_listen_position()) {
2174 case AfterFaderListen:
2175 solo_button->set_text (_("AFL"));
2177 case PreFaderListen:
2178 solo_button->set_text (_("PFL"));
2182 solo_isolated_led->set_text (_("Iso"));
2183 solo_safe_led->set_text (S_("SoloLock|Lock"));
2187 mute_button->set_text (S_("Mute|M"));
2188 monitor_input_button->set_text (S_("MonitorInput|I"));
2189 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2190 if (monitor_section_button) {
2191 monitor_section_button->set_text (S_("Mon|O"));
2194 if (_route && _route->solo_safe_control()->solo_safe()) {
2195 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2197 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2199 if (!Config->get_solo_control_is_listen_control()) {
2200 solo_button->set_text (S_("Solo|S"));
2202 switch (Config->get_listen_position()) {
2203 case AfterFaderListen:
2204 solo_button->set_text (S_("AfterFader|A"));
2206 case PreFaderListen:
2207 solo_button->set_text (S_("Prefader|P"));
2212 solo_isolated_led->set_text (S_("SoloIso|I"));
2213 solo_safe_led->set_text (S_("SoloLock|L"));
2218 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2220 meter_point_button.set_text ("");
2225 MixerStrip::plugin_selector()
2227 return _mixer.plugin_selector();
2231 MixerStrip::hide_things ()
2233 processor_box.hide_things ();
2237 MixerStrip::input_active_button_press (GdkEventButton*)
2239 /* nothing happens on press */
2244 MixerStrip::input_active_button_release (GdkEventButton* ev)
2246 boost::shared_ptr<MidiTrack> mt = midi_track ();
2252 boost::shared_ptr<RouteList> rl (new RouteList);
2254 rl->push_back (route());
2256 _session->set_exclusive_input_active (rl, !mt->input_active(),
2257 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2263 MixerStrip::midi_input_status_changed ()
2265 if (midi_input_enable_button) {
2266 boost::shared_ptr<MidiTrack> mt = midi_track ();
2268 midi_input_enable_button->set_active (mt->input_active ());
2273 MixerStrip::state_id () const
2275 return string_compose ("strip %1", _route->id().to_s());
2279 MixerStrip::parameter_changed (string p)
2281 if (p == _visibility.get_state_name()) {
2282 /* The user has made changes to the mixer strip visibility, so get
2283 our VisibilityGroup to reflect these changes in our widgets.
2285 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2286 } else if (p == "track-name-number") {
2288 } else if (p == "use-monitor-bus") {
2289 if (monitor_section_button) {
2290 if (mute_button->get_parent()) {
2291 mute_button->get_parent()->remove(*mute_button);
2293 if (monitor_section_button->get_parent()) {
2294 monitor_section_button->get_parent()->remove(*monitor_section_button);
2296 if (Config->get_use_monitor_bus ()) {
2297 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2298 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2299 mute_button->show();
2300 monitor_section_button->show();
2302 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2303 mute_button->show();
2306 } else if (p == "track-name-number") {
2307 update_track_number_visibility();
2311 /** Called to decide whether the solo isolate / solo lock button visibility should
2312 * be overridden from that configured by the user. We do this for the master bus.
2314 * @return optional value that is present if visibility state should be overridden.
2316 boost::optional<bool>
2317 MixerStrip::override_solo_visibility () const
2319 if (_route && _route->is_master ()) {
2320 return boost::optional<bool> (false);
2323 return boost::optional<bool> ();
2327 MixerStrip::add_input_port (DataType t)
2329 _route->input()->add_port ("", this, t);
2333 MixerStrip::add_output_port (DataType t)
2335 _route->output()->add_port ("", this, t);
2339 MixerStrip::route_active_changed ()
2341 reset_strip_style ();
2345 MixerStrip::copy_processors ()
2347 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2351 MixerStrip::cut_processors ()
2353 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2357 MixerStrip::paste_processors ()
2359 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2363 MixerStrip::select_all_processors ()
2365 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2369 MixerStrip::deselect_all_processors ()
2371 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2375 MixerStrip::delete_processors ()
2377 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2381 MixerStrip::toggle_processors ()
2383 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2387 MixerStrip::ab_plugins ()
2389 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2393 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2395 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2398 if (ev->button == 3) {
2399 popup_level_meter_menu (ev);
2407 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2409 using namespace Gtk::Menu_Helpers;
2411 Gtk::Menu* m = manage (new Menu);
2412 MenuList& items = m->items ();
2414 RadioMenuItem::Group group;
2416 _suspend_menu_callbacks = true;
2417 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2418 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2419 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2420 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2421 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2423 if (gpm.meter_channels().n_audio() == 0) {
2424 m->popup (ev->button, ev->time);
2425 _suspend_menu_callbacks = false;
2429 RadioMenuItem::Group tgroup;
2430 items.push_back (SeparatorElem());
2432 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2433 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2434 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2435 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2436 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2437 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2438 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2439 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2440 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2441 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2442 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2445 if (_route->is_master()) {
2448 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2449 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2450 /* non-master bus */
2453 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2460 MeterType cmt = _route->meter_type();
2461 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2463 items.push_back (SeparatorElem());
2464 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2465 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2466 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2467 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2468 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2469 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2471 m->popup (ev->button, ev->time);
2472 _suspend_menu_callbacks = false;
2476 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2477 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2479 using namespace Menu_Helpers;
2481 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2482 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2483 i->set_active (_route->meter_point() == point);
2487 MixerStrip::set_meter_point (MeterPoint p)
2489 if (_suspend_menu_callbacks) return;
2490 _route->set_meter_point (p);
2494 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2495 RadioMenuItem::Group& group, string const & name, MeterType type)
2497 using namespace Menu_Helpers;
2499 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2500 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2501 i->set_active (_route->meter_type() == type);
2505 MixerStrip::set_meter_type (MeterType t)
2507 if (_suspend_menu_callbacks) return;
2512 MixerStrip::vca_menu_toggle (Gtk::CheckMenuItem* menuitem, uint32_t n)
2518 boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2525 /* if this strip is not selected, add it before carrying out
2526 changes to assignment. the user probably didn't notice
2527 that they were clicking on an unselected track.
2529 _mixer.select_strip (*this);
2532 if (!menuitem->get_active()) {
2533 _mixer.do_vca_unassign (vca);
2535 _mixer.do_vca_assign (vca);
2540 MixerStrip::vca_assign (boost::shared_ptr<VCA> vca)
2542 if (!vca || !_route) {
2546 _route->assign (vca);
2550 MixerStrip::vca_unassign (boost::shared_ptr<VCA> vca)
2556 _route->unassign (vca);
2560 MixerStrip::vca_button_release (GdkEventButton* ev)
2562 using namespace Gtk::Menu_Helpers;
2568 /* primary click only */
2570 if (ev->button != 1) {
2575 /* no route - nothing to do */
2579 VCAList vcas (_session->vca_manager().vcas());
2582 /* the button should not have been visible under these conditions */
2586 Menu* menu = new Menu;
2587 MenuList& items = menu->items();
2589 items.push_back (MenuElem (_("Unassign"), sigc::bind (sigc::mem_fun (_mixer, &Mixer_UI::do_vca_unassign), boost::shared_ptr<VCA>())));
2591 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2592 items.push_back (CheckMenuElem ((*v)->name()));
2593 Gtk::CheckMenuItem* item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2594 if (_route->slaved_to (*v)) {
2595 item->set_active (true);
2597 item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), item, (*v)->number()));
2600 menu->popup (1, ev->time);
2606 MixerStrip::update_track_number_visibility ()
2608 DisplaySuspender ds;
2609 bool show_label = _session->config.get_track_name_number();
2611 if (_route && _route->is_master()) {
2616 number_label.show ();
2617 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2618 // except the width of the number label is subtracted from the name-hbox, so we
2619 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2620 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2622 number_label.set_size_request(tnw, -1);
2623 number_label.show ();
2625 number_label.hide ();
2630 MixerStrip::color () const
2632 return route_color ();
2636 MixerStrip::marked_for_display () const
2638 return !_route->presentation_info().hidden();
2642 MixerStrip::set_marked_for_display (bool yn)
2644 return RouteUI::mark_hidden (!yn);