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::set_route (boost::shared_ptr<Route> rt)
456 //the rec/monitor stuff only shows up for tracks.
457 //the show_sends only shows up for buses.
458 //remove them all here, and we may add them back later
459 if (show_sends_button->get_parent()) {
460 rec_mon_table.remove (*show_sends_button);
462 if (rec_enable_button->get_parent()) {
463 rec_mon_table.remove (*rec_enable_button);
465 if (monitor_input_button->get_parent()) {
466 rec_mon_table.remove (*monitor_input_button);
468 if (monitor_disk_button->get_parent()) {
469 rec_mon_table.remove (*monitor_disk_button);
471 if (group_button.get_parent()) {
472 bottom_button_table.remove (group_button);
475 RouteUI::set_route (rt);
477 /* ProcessorBox needs access to _route so that it can read
480 processor_box.set_route (rt);
482 revert_to_default_display ();
484 /* unpack these from the parent and stuff them into our own
488 if (gpm.peak_display.get_parent()) {
489 gpm.peak_display.get_parent()->remove (gpm.peak_display);
491 if (gpm.gain_display.get_parent()) {
492 gpm.gain_display.get_parent()->remove (gpm.gain_display);
495 gpm.set_type (rt->meter_type());
497 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
498 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
500 if (solo_button->get_parent()) {
501 mute_solo_table.remove (*solo_button);
504 if (mute_button->get_parent()) {
505 mute_solo_table.remove (*mute_button);
508 if (route()->is_master()) {
509 solo_button->hide ();
510 mute_button->show ();
511 rec_mon_table.hide ();
512 if (solo_iso_table.get_parent()) {
513 solo_iso_table.get_parent()->remove(solo_iso_table);
515 if (monitor_section_button == 0) {
516 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
517 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
519 monitor_section_button = manage (new ArdourButton);
521 monitor_section_button->set_related_action (act);
522 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
523 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
524 monitor_section_button->show();
525 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
527 parameter_changed ("use-monitor-bus");
529 bottom_button_table.attach (group_button, 1, 2, 0, 1);
530 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
531 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
532 mute_button->show ();
533 solo_button->show ();
534 rec_mon_table.show ();
537 if (_mixer_owned && route()->is_master() ) {
539 HScrollbar scrollbar;
540 Gtk::Requisition requisition(scrollbar.size_request ());
541 int scrollbar_height = requisition.height;
543 spacer = manage (new EventBox);
544 spacer->set_size_request (-1, scrollbar_height+2);
545 global_vpacker.pack_start (*spacer, false, false);
550 monitor_input_button->show ();
551 monitor_disk_button->show ();
553 monitor_input_button->hide();
554 monitor_disk_button->hide ();
557 if (route()->trim() && route()->trim()->active()) {
558 trim_control.show ();
559 trim_control.set_controllable (route()->trim()->gain_control());
561 trim_control.hide ();
562 boost::shared_ptr<Controllable> none;
563 trim_control.set_controllable (none);
566 if (is_midi_track()) {
567 if (midi_input_enable_button == 0) {
568 midi_input_enable_button = manage (new ArdourButton);
569 midi_input_enable_button->set_name ("midi input button");
570 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
571 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
572 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
573 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
574 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
576 input_button_box.remove (*midi_input_enable_button);
578 /* get current state */
579 midi_input_status_changed ();
580 input_button_box.pack_start (*midi_input_enable_button, false, false);
582 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
584 if (midi_input_enable_button) {
585 /* removal from the container will delete it */
586 input_button_box.remove (*midi_input_enable_button);
587 midi_input_enable_button = 0;
591 if (is_audio_track()) {
592 boost::shared_ptr<AudioTrack> at = audio_track();
593 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
598 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
599 rec_enable_button->show();
601 if (ARDOUR::Profile->get_mixbus()) {
602 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
603 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
604 } else if (ARDOUR::Profile->get_trx()) {
605 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
607 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
608 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
615 if (!_route->is_master()) {
616 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
617 show_sends_button->show();
621 meter_point_button.set_text (meter_point_string (_route->meter_point()));
623 delete route_ops_menu;
626 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
627 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
628 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
629 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
631 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
633 if (_route->panner_shell()) {
634 update_panner_choices();
635 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
638 if (is_audio_track()) {
639 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
642 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
643 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
645 _route->gain_control()->MasterStatusChange.connect (route_connections,
647 boost::bind (&MixerStrip::update_vca_display, this),
650 set_stuff_from_route ();
652 /* now force an update of all the various elements */
654 update_mute_display ();
655 update_solo_display ();
656 update_vca_display ();
659 route_group_changed ();
660 update_track_number_visibility ();
663 panners.setup_pan ();
665 if (has_audio_outputs ()) {
671 update_diskstream_display ();
672 update_input_display ();
673 update_output_display ();
675 add_events (Gdk::BUTTON_RELEASE_MASK);
677 processor_box.show ();
679 if (!route()->is_master() && !route()->is_monitor()) {
680 /* we don't allow master or control routes to be hidden */
685 gpm.reset_peak_display ();
686 gpm.gain_display.show ();
687 gpm.peak_display.show ();
690 width_hide_box.show();
692 global_vpacker.show();
693 mute_solo_table.show();
694 bottom_button_table.show();
696 meter_point_button.show();
697 input_button_box.show_all();
698 output_button.show();
700 _comment_button.show();
702 gpm.gain_automation_state_button.show();
704 parameter_changed ("mixer-element-visibility");
711 MixerStrip::set_stuff_from_route ()
713 /* if width is not set, it will be set by the MixerUI or editor */
715 string str = gui_property ("strip-width");
717 set_width_enum (Width (string_2_enum (str, _width)), this);
722 MixerStrip::set_width_enum (Width w, void* owner)
724 /* always set the gpm width again, things may be hidden */
727 panners.set_width (w);
729 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
731 _width_owner = owner;
735 if (_width_owner == this) {
736 set_gui_property ("strip-width", enum_2_string (_width));
741 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
746 if (show_sends_button) {
747 show_sends_button->set_text (_("Aux"));
750 gpm.gain_automation_style_button.set_text (
751 gpm.astyle_string(gain_automation->automation_style()));
752 gpm.gain_automation_state_button.set_text (
753 gpm.astate_string(gain_automation->automation_state()));
755 if (_route->panner()) {
756 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
757 panners.astyle_string(_route->panner()->automation_style()));
758 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
759 panners.astate_string(_route->panner()->automation_state()));
763 // panners expect an even number of horiz. pixels
764 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
766 set_size_request (width, -1);
772 if (show_sends_button) {
773 show_sends_button->set_text (_("Snd"));
776 gpm.gain_automation_style_button.set_text (
777 gpm.short_astyle_string(gain_automation->automation_style()));
778 gpm.gain_automation_state_button.set_text (
779 gpm.short_astate_string(gain_automation->automation_state()));
780 gain_meter().setup_meters (); // recalc meter width
782 if (_route->panner()) {
783 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
784 panners.short_astyle_string(_route->panner()->automation_style()));
785 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
786 panners.short_astate_string(_route->panner()->automation_state()));
790 // panners expect an even number of horiz. pixels
791 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
793 set_size_request (width, -1);
798 processor_box.set_width (w);
800 update_input_display ();
801 update_output_display ();
802 setup_comment_button ();
803 route_group_changed ();
809 MixerStrip::set_packed (bool yn)
814 set_gui_property ("visible", true);
816 set_gui_property ("visible", false);
821 struct RouteCompareByName {
822 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
823 return a->name().compare (b->name()) < 0;
828 MixerStrip::output_release (GdkEventButton *ev)
830 switch (ev->button) {
832 edit_output_configuration ();
840 MixerStrip::output_press (GdkEventButton *ev)
842 using namespace Menu_Helpers;
843 if (!_session->engine().connected()) {
844 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
849 MenuList& citems = output_menu.items();
850 switch (ev->button) {
853 return false; //wait for the mouse-up to pop the dialog
857 output_menu.set_name ("ArdourContextMenu");
859 output_menu_bundles.clear ();
861 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
863 citems.push_back (SeparatorElem());
864 uint32_t const n_with_separator = citems.size ();
866 ARDOUR::BundleList current = _route->output()->bundles_connected ();
868 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
870 /* give user bundles first chance at being in the menu */
872 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
873 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
874 maybe_add_bundle_to_output_menu (*i, current);
878 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
879 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
880 maybe_add_bundle_to_output_menu (*i, current);
884 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
885 RouteList copy = *routes;
886 copy.sort (RouteCompareByName ());
887 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
888 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
891 if (citems.size() == n_with_separator) {
892 /* no routes added; remove the separator */
896 if (!ARDOUR::Profile->get_mixbus()) {
897 citems.push_back (SeparatorElem());
899 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
902 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
903 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
909 citems.push_back (SeparatorElem());
910 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
912 output_menu.popup (1, ev->time);
923 MixerStrip::input_release (GdkEventButton *ev)
925 switch (ev->button) {
928 edit_input_configuration ();
940 MixerStrip::input_press (GdkEventButton *ev)
942 using namespace Menu_Helpers;
944 MenuList& citems = input_menu.items();
945 input_menu.set_name ("ArdourContextMenu");
948 if (!_session->engine().connected()) {
949 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
954 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
957 switch (ev->button) {
960 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
964 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
966 citems.push_back (SeparatorElem());
967 uint32_t const n_with_separator = citems.size ();
969 input_menu_bundles.clear ();
971 ARDOUR::BundleList current = _route->input()->bundles_connected ();
973 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
975 /* give user bundles first chance at being in the menu */
977 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
978 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
979 maybe_add_bundle_to_input_menu (*i, current);
983 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
984 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
985 maybe_add_bundle_to_input_menu (*i, current);
989 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
990 RouteList copy = *routes;
991 copy.sort (RouteCompareByName ());
992 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
993 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
996 if (citems.size() == n_with_separator) {
997 /* no routes added; remove the separator */
1001 citems.push_back (SeparatorElem());
1002 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1005 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1006 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1011 citems.push_back (SeparatorElem());
1012 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1014 input_menu.popup (1, ev->time);
1025 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1027 if (ignore_toggle) {
1031 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1033 if (std::find (current.begin(), current.end(), c) == current.end()) {
1034 _route->input()->connect_ports_to_bundle (c, true, this);
1036 _route->input()->disconnect_ports_from_bundle (c, this);
1041 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1043 if (ignore_toggle) {
1047 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1049 if (std::find (current.begin(), current.end(), c) == current.end()) {
1050 _route->output()->connect_ports_to_bundle (c, true, this);
1052 _route->output()->disconnect_ports_from_bundle (c, this);
1057 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1059 using namespace Menu_Helpers;
1061 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1065 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1066 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1070 if (i != input_menu_bundles.end()) {
1074 input_menu_bundles.push_back (b);
1076 MenuList& citems = input_menu.items();
1078 std::string n = b->name ();
1079 replace_all (n, "_", " ");
1081 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1085 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1087 using namespace Menu_Helpers;
1089 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1093 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1094 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1098 if (i != output_menu_bundles.end()) {
1102 output_menu_bundles.push_back (b);
1104 MenuList& citems = output_menu.items();
1106 std::string n = b->name ();
1107 replace_all (n, "_", " ");
1109 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1113 MixerStrip::update_diskstream_display ()
1115 if (is_track() && input_selector) {
1116 input_selector->hide_all ();
1119 route_color_changed ();
1123 MixerStrip::connect_to_pan ()
1125 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1127 panstate_connection.disconnect ();
1128 panstyle_connection.disconnect ();
1130 if (!_route->panner()) {
1134 boost::shared_ptr<Pannable> p = _route->pannable ();
1136 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1137 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1139 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1140 * However, that only works a panner was previously set.
1142 * PannerUI must remain subscribed to _panshell->Changed() in case
1143 * we switch the panner eg. AUX-Send and back
1144 * _route->panner_shell()->Changed() vs _panshell->Changed
1146 if (panners._panner == 0) {
1147 panners.panshell_changed ();
1149 update_panner_choices();
1153 MixerStrip::update_panner_choices ()
1155 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1156 if (!_route->panner_shell()) { return; }
1158 uint32_t in = _route->output()->n_ports().n_audio();
1160 if (_route->panner()) {
1161 in = _route->panner()->in().n_audio();
1164 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1168 * Output port labelling
1169 * =====================
1171 * Case 1: Each output has one connection, all connections are to system:playback_%i
1172 * out 1 -> system:playback_1
1173 * out 2 -> system:playback_2
1174 * out 3 -> system:playback_3
1177 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1178 * out 1 -> ardour:track_x/in 1
1179 * out 2 -> ardour:track_x/in 2
1180 * Display as: track_x
1182 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1183 * out 1 -> program x:foo
1184 * out 2 -> program x:foo
1185 * Display as: program x
1187 * Case 4: No connections (Disconnected)
1190 * Default case (unusual routing):
1191 * Display as: *number of connections*
1195 * .-----------------------------------------------.
1197 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1198 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1199 * '-----------------------------------------------'
1200 * .-----------------------------------------------.
1203 * '-----------------------------------------------'
1207 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1211 boost::shared_ptr<Port> port;
1212 vector<string> port_connections;
1214 uint32_t total_connection_count = 0;
1215 uint32_t io_connection_count = 0;
1216 uint32_t ardour_connection_count = 0;
1217 uint32_t system_connection_count = 0;
1218 uint32_t other_connection_count = 0;
1219 uint32_t typed_connection_count = 0;
1221 ostringstream label;
1223 bool have_label = false;
1224 bool each_io_has_one_connection = true;
1226 string connection_name;
1227 string ardour_track_name;
1228 string other_connection_type;
1229 string system_ports;
1232 ostringstream tooltip;
1233 char * tooltip_cstr;
1235 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1236 DataType dt = DataType::AUDIO;
1237 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1238 dt = DataType::MIDI;
1239 // avoid further confusion with Midi-tracks that have a synth.
1240 // Audio-ports may be connected, but button says "Disconnected"
1241 tooltip << _("MIDI ");
1245 io_count = route->n_inputs().n_total();
1246 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1248 io_count = route->n_outputs().n_total();
1249 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1253 for (io_index = 0; io_index < io_count; ++io_index) {
1255 port = route->input()->nth (io_index);
1257 port = route->output()->nth (io_index);
1260 port_connections.clear ();
1261 port->get_connections(port_connections);
1263 //ignore any port connections that don't match our DataType
1264 if (port->type() != dt) {
1265 if (!port_connections.empty()) {
1266 ++typed_connection_count;
1271 io_connection_count = 0;
1273 if (!port_connections.empty()) {
1274 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1276 string& connection_name (*i);
1278 if (connection_name.find("system:") == 0) {
1279 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1282 if (io_connection_count == 0) {
1283 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1285 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1288 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1291 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1292 if (ardour_track_name.empty()) {
1293 // "ardour:Master/in 1" -> "ardour:Master/"
1294 string::size_type slash = connection_name.find("/");
1295 if (slash != string::npos) {
1296 ardour_track_name = connection_name.substr(0, slash + 1);
1300 if (connection_name.find(ardour_track_name) == 0) {
1301 ++ardour_connection_count;
1303 } else if (!pn.empty()) {
1304 if (system_ports.empty()) {
1307 system_ports += "/" + pn;
1309 if (connection_name.find("system:") == 0) {
1310 ++system_connection_count;
1312 } else if (connection_name.find("system:midi_") == 0) {
1314 // "system:midi_capture_123" -> "123"
1315 system_port = "M " + connection_name.substr(20);
1317 // "system:midi_playback_123" -> "123"
1318 system_port = "M " + connection_name.substr(21);
1321 if (system_ports.empty()) {
1322 system_ports += system_port;
1324 system_ports += "/" + system_port;
1327 ++system_connection_count;
1329 } else if (connection_name.find("system:") == 0) {
1331 // "system:capture_123" -> "123"
1332 system_port = connection_name.substr(15);
1334 // "system:playback_123" -> "123"
1335 system_port = connection_name.substr(16);
1338 if (system_ports.empty()) {
1339 system_ports += system_port;
1341 system_ports += "/" + system_port;
1344 ++system_connection_count;
1346 if (other_connection_type.empty()) {
1347 // "jamin:in 1" -> "jamin:"
1348 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1351 if (connection_name.find(other_connection_type) == 0) {
1352 ++other_connection_count;
1356 ++total_connection_count;
1357 ++io_connection_count;
1361 if (io_connection_count != 1) {
1362 each_io_has_one_connection = false;
1366 if (total_connection_count == 0) {
1367 tooltip << endl << _("Disconnected");
1370 tooltip_cstr = new char[tooltip.str().size() + 1];
1371 strcpy(tooltip_cstr, tooltip.str().c_str());
1374 set_tooltip (&input_button, tooltip_cstr);
1376 set_tooltip (&output_button, tooltip_cstr);
1379 delete [] tooltip_cstr;
1381 if (each_io_has_one_connection) {
1382 if (total_connection_count == ardour_connection_count) {
1383 // all connections are to the same track in ardour
1384 // "ardour:Master/" -> "Master"
1385 string::size_type slash = ardour_track_name.find("/");
1386 if (slash != string::npos) {
1387 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1388 label << ardour_track_name.substr (ppps, slash - ppps);
1392 else if (total_connection_count == system_connection_count) {
1393 // all connections are to system ports
1394 label << system_ports;
1397 else if (total_connection_count == other_connection_count) {
1398 // all connections are to the same external program eg jamin
1399 // "jamin:" -> "jamin"
1400 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1406 if (total_connection_count == 0) {
1410 // Odd configuration
1411 label << "*" << total_connection_count << "*";
1413 if (typed_connection_count > 0) {
1414 label << "\u2295"; // circled plus
1419 input_button.set_text (label.str());
1421 output_button.set_text (label.str());
1426 MixerStrip::update_input_display ()
1428 update_io_button (_route, _width, true);
1429 panners.setup_pan ();
1431 if (has_audio_outputs ()) {
1432 panners.show_all ();
1434 panners.hide_all ();
1440 MixerStrip::update_output_display ()
1442 update_io_button (_route, _width, false);
1443 gpm.setup_meters ();
1444 panners.setup_pan ();
1446 if (has_audio_outputs ()) {
1447 panners.show_all ();
1449 panners.hide_all ();
1454 MixerStrip::fast_update ()
1456 gpm.update_meters ();
1460 MixerStrip::diskstream_changed ()
1462 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1466 MixerStrip::io_changed_proxy ()
1468 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1472 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1474 boost::shared_ptr<Port> a = wa.lock ();
1475 boost::shared_ptr<Port> b = wb.lock ();
1477 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1478 update_input_display ();
1479 set_width_enum (_width, this);
1482 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1483 update_output_display ();
1484 set_width_enum (_width, this);
1489 MixerStrip::setup_comment_button ()
1494 if (_route->comment().empty ()) {
1495 _comment_button.unset_bg (STATE_NORMAL);
1496 _comment_button.set_text (_("Comments"));
1498 _comment_button.modify_bg (STATE_NORMAL, color ());
1499 _comment_button.set_text (_("*Comments*"));
1504 if (_route->comment().empty ()) {
1505 _comment_button.unset_bg (STATE_NORMAL);
1506 _comment_button.set_text (_("Cmt"));
1508 _comment_button.modify_bg (STATE_NORMAL, color ());
1509 _comment_button.set_text (_("*Cmt*"));
1515 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1521 MixerStrip::select_route_group (GdkEventButton *ev)
1523 using namespace Menu_Helpers;
1525 if (ev->button == 1) {
1527 if (group_menu == 0) {
1529 PropertyList* plist = new PropertyList();
1531 plist->add (Properties::gain, true);
1532 plist->add (Properties::mute, true);
1533 plist->add (Properties::solo, true);
1535 group_menu = new RouteGroupMenu (_session, plist);
1539 r.push_back (route ());
1540 group_menu->build (r);
1541 group_menu->menu()->popup (1, ev->time);
1548 MixerStrip::route_group_changed ()
1550 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1552 RouteGroup *rg = _route->route_group();
1555 group_button.set_text (PBD::short_version (rg->name(), 5));
1559 group_button.set_text (_("Grp"));
1562 group_button.set_text (_("~G"));
1569 MixerStrip::route_color_changed ()
1571 name_button.modify_bg (STATE_NORMAL, color());
1572 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1573 reset_strip_style ();
1577 MixerStrip::show_passthru_color ()
1579 reset_strip_style ();
1584 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1586 boost::shared_ptr<Processor> processor (p.lock ());
1587 if (!processor || !processor->display_to_user()) {
1590 if (boost::dynamic_pointer_cast<PluginInsert> (processor)) {
1591 ++_plugin_insert_cnt;
1595 MixerStrip::build_route_ops_menu ()
1597 using namespace Menu_Helpers;
1598 route_ops_menu = new Menu;
1599 route_ops_menu->set_name ("ArdourContextMenu");
1601 MenuList& items = route_ops_menu->items();
1603 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1605 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1607 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1609 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1611 items.push_back (SeparatorElem());
1613 if (!_route->is_master()) {
1614 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1616 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1617 rename_menu_item = &items.back();
1619 items.push_back (SeparatorElem());
1620 items.push_back (CheckMenuElem (_("Active")));
1621 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1622 i->set_active (_route->active());
1623 i->set_sensitive(! _session->transport_rolling());
1624 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1626 if (!Profile->get_mixbus ()) {
1627 items.push_back (SeparatorElem());
1628 items.push_back (CheckMenuElem (_("Strict I/O")));
1629 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1630 i->set_active (_route->strict_io());
1631 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1634 _plugin_insert_cnt = 0;
1635 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1636 if (_plugin_insert_cnt > 0) {
1637 items.push_back (SeparatorElem());
1638 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1641 items.push_back (SeparatorElem());
1642 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1644 items.push_back (SeparatorElem());
1645 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1646 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1647 denormal_menu_item->set_active (_route->denormal_protection());
1649 items.push_back (SeparatorElem());
1650 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1653 /* note that this relies on selection being shared across editor and
1654 mixer (or global to the backend, in the future), which is the only
1655 sane thing for users anyway.
1658 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1660 Selection& selection (PublicEditor::instance().get_selection());
1661 if (!selection.selected (rtav)) {
1662 selection.set (rtav);
1665 if (!_route->is_master()) {
1666 items.push_back (SeparatorElem());
1667 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1670 items.push_back (SeparatorElem());
1671 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1677 MixerStrip::name_button_button_press (GdkEventButton* ev)
1679 if (ev->button == 3) {
1680 list_route_operations ();
1682 /* do not allow rename if the track is record-enabled */
1683 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1684 route_ops_menu->popup (1, ev->time);
1693 MixerStrip::name_button_button_release (GdkEventButton* ev)
1695 if (ev->button == 1) {
1696 list_route_operations ();
1698 /* do not allow rename if the track is record-enabled */
1699 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1700 route_ops_menu->popup (1, ev->time);
1707 MixerStrip::number_button_button_press (GdkEventButton* ev)
1709 if ( ev->button == 3 ) {
1710 list_route_operations ();
1712 /* do not allow rename if the track is record-enabled */
1713 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1714 route_ops_menu->popup (1, ev->time);
1723 MixerStrip::list_route_operations ()
1725 delete route_ops_menu;
1726 build_route_ops_menu ();
1730 MixerStrip::set_selected (bool yn)
1732 AxisView::set_selected (yn);
1734 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1735 global_frame.set_name ("MixerStripSelectedFrame");
1737 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1738 global_frame.set_name ("MixerStripFrame");
1740 global_frame.queue_draw ();
1743 // processor_box.deselect_all_processors();
1747 MixerStrip::property_changed (const PropertyChange& what_changed)
1749 RouteUI::property_changed (what_changed);
1751 if (what_changed.contains (ARDOUR::Properties::name)) {
1757 MixerStrip::name_changed ()
1761 name_button.set_text (_route->name());
1764 name_button.set_text (PBD::short_version (_route->name(), 5));
1768 set_tooltip (name_button, _route->name());
1770 if (_session->config.get_track_name_number()) {
1771 const int64_t track_number = _route->track_number ();
1772 if (track_number == 0) {
1773 number_label.set_text ("-");
1775 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1778 number_label.set_text ("");
1783 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1785 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1789 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1791 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1795 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1797 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1801 MixerStrip::width_button_pressed (GdkEventButton* ev)
1803 if (ev->button != 1) {
1807 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1810 _mixer.set_strip_width (Narrow, true);
1814 _mixer.set_strip_width (Wide, true);
1820 set_width_enum (Narrow, this);
1823 set_width_enum (Wide, this);
1832 MixerStrip::hide_clicked ()
1834 // LAME fix to reset the button status for when it is redisplayed (part 1)
1835 hide_button.set_sensitive(false);
1838 Hiding(); /* EMIT_SIGNAL */
1840 _mixer.hide_strip (this);
1844 hide_button.set_sensitive(true);
1848 MixerStrip::set_embedded (bool yn)
1854 MixerStrip::map_frozen ()
1856 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1858 boost::shared_ptr<AudioTrack> at = audio_track();
1861 switch (at->freeze_state()) {
1862 case AudioTrack::Frozen:
1863 processor_box.set_sensitive (false);
1864 hide_redirect_editors ();
1867 processor_box.set_sensitive (true);
1868 // XXX need some way, maybe, to retoggle redirect editors
1872 processor_box.set_sensitive (true);
1874 RouteUI::map_frozen ();
1878 MixerStrip::hide_redirect_editors ()
1880 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1884 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1886 boost::shared_ptr<Processor> processor (p.lock ());
1891 Gtk::Window* w = processor_box.get_processor_ui (processor);
1899 MixerStrip::reset_strip_style ()
1901 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1903 gpm.set_fader_name ("SendStripBase");
1907 if (is_midi_track()) {
1908 if (_route->active()) {
1909 set_name ("MidiTrackStripBase");
1911 set_name ("MidiTrackStripBaseInactive");
1913 gpm.set_fader_name ("MidiTrackFader");
1914 } else if (is_audio_track()) {
1915 if (_route->active()) {
1916 set_name ("AudioTrackStripBase");
1918 set_name ("AudioTrackStripBaseInactive");
1920 gpm.set_fader_name ("AudioTrackFader");
1922 if (_route->active()) {
1923 set_name ("AudioBusStripBase");
1925 set_name ("AudioBusStripBaseInactive");
1927 gpm.set_fader_name ("AudioBusFader");
1929 /* (no MIDI busses yet) */
1936 MixerStrip::engine_stopped ()
1941 MixerStrip::engine_running ()
1946 MixerStrip::meter_point_string (MeterPoint mp)
1959 case MeterPostFader:
1976 return S_("Meter|In");
1980 return S_("Meter|Pr");
1983 case MeterPostFader:
1984 return S_("Meter|Po");
1988 return S_("Meter|O");
1993 return S_("Meter|C");
2002 /** Called when the monitor-section state */
2004 MixerStrip::monitor_changed ()
2006 assert (monitor_section_button);
2007 if (_session->monitor_active()) {
2008 monitor_section_button->set_name ("master monitor section button active");
2010 monitor_section_button->set_name ("master monitor section button normal");
2014 /** Called when the metering point has changed */
2016 MixerStrip::meter_changed ()
2018 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2019 gpm.setup_meters ();
2020 // reset peak when meter point changes
2021 gpm.reset_peak_display();
2024 /** The bus that we are displaying sends to has changed, or been turned off.
2025 * @param send_to New bus that we are displaying sends to, or 0.
2028 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2030 RouteUI::bus_send_display_changed (send_to);
2033 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2038 revert_to_default_display ();
2041 revert_to_default_display ();
2046 MixerStrip::drop_send ()
2048 boost::shared_ptr<Send> current_send;
2050 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2051 current_send->set_metering (false);
2054 send_gone_connection.disconnect ();
2055 input_button.set_sensitive (true);
2056 output_button.set_sensitive (true);
2057 group_button.set_sensitive (true);
2058 set_invert_sensitive (true);
2059 meter_point_button.set_sensitive (true);
2060 mute_button->set_sensitive (true);
2061 solo_button->set_sensitive (true);
2062 solo_isolated_led->set_sensitive (true);
2063 solo_safe_led->set_sensitive (true);
2064 monitor_input_button->set_sensitive (true);
2065 monitor_disk_button->set_sensitive (true);
2066 _comment_button.set_sensitive (true);
2067 RouteUI::check_rec_enable_sensitivity ();
2068 set_button_names (); // update solo button visual state
2072 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2074 _current_delivery = d;
2075 DeliveryChanged (_current_delivery);
2079 MixerStrip::show_send (boost::shared_ptr<Send> send)
2085 set_current_delivery (send);
2087 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2088 send->set_metering (true);
2089 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2091 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2092 gain_meter().setup_meters ();
2094 uint32_t const in = _current_delivery->pans_required();
2095 uint32_t const out = _current_delivery->pan_outs();
2097 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2098 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2099 panner_ui().setup_pan ();
2100 panner_ui().set_send_drawing_mode (true);
2101 panner_ui().show_all ();
2103 input_button.set_sensitive (false);
2104 group_button.set_sensitive (false);
2105 set_invert_sensitive (false);
2106 meter_point_button.set_sensitive (false);
2107 mute_button->set_sensitive (false);
2108 solo_button->set_sensitive (false);
2109 rec_enable_button->set_sensitive (false);
2110 solo_isolated_led->set_sensitive (false);
2111 solo_safe_led->set_sensitive (false);
2112 monitor_input_button->set_sensitive (false);
2113 monitor_disk_button->set_sensitive (false);
2114 _comment_button.set_sensitive (false);
2116 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2117 output_button.set_sensitive (false);
2120 reset_strip_style ();
2124 MixerStrip::revert_to_default_display ()
2128 set_current_delivery (_route->main_outs ());
2130 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2131 gain_meter().setup_meters ();
2133 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2134 update_panner_choices();
2135 panner_ui().setup_pan ();
2136 panner_ui().set_send_drawing_mode (false);
2138 if (has_audio_outputs ()) {
2139 panners.show_all ();
2141 panners.hide_all ();
2144 reset_strip_style ();
2148 MixerStrip::set_button_names ()
2152 mute_button->set_text (_("Mute"));
2153 monitor_input_button->set_text (_("In"));
2154 monitor_disk_button->set_text (_("Disk"));
2155 if (monitor_section_button) {
2156 monitor_section_button->set_text (_("Mon"));
2159 if (_route && _route->solo_safe_control()->solo_safe()) {
2160 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2162 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2164 if (!Config->get_solo_control_is_listen_control()) {
2165 solo_button->set_text (_("Solo"));
2167 switch (Config->get_listen_position()) {
2168 case AfterFaderListen:
2169 solo_button->set_text (_("AFL"));
2171 case PreFaderListen:
2172 solo_button->set_text (_("PFL"));
2176 solo_isolated_led->set_text (_("Iso"));
2177 solo_safe_led->set_text (S_("SoloLock|Lock"));
2181 mute_button->set_text (S_("Mute|M"));
2182 monitor_input_button->set_text (S_("MonitorInput|I"));
2183 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2184 if (monitor_section_button) {
2185 monitor_section_button->set_text (S_("Mon|O"));
2188 if (_route && _route->solo_safe_control()->solo_safe()) {
2189 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2191 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2193 if (!Config->get_solo_control_is_listen_control()) {
2194 solo_button->set_text (S_("Solo|S"));
2196 switch (Config->get_listen_position()) {
2197 case AfterFaderListen:
2198 solo_button->set_text (S_("AfterFader|A"));
2200 case PreFaderListen:
2201 solo_button->set_text (S_("Prefader|P"));
2206 solo_isolated_led->set_text (S_("SoloIso|I"));
2207 solo_safe_led->set_text (S_("SoloLock|L"));
2212 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2214 meter_point_button.set_text ("");
2219 MixerStrip::plugin_selector()
2221 return _mixer.plugin_selector();
2225 MixerStrip::hide_things ()
2227 processor_box.hide_things ();
2231 MixerStrip::input_active_button_press (GdkEventButton*)
2233 /* nothing happens on press */
2238 MixerStrip::input_active_button_release (GdkEventButton* ev)
2240 boost::shared_ptr<MidiTrack> mt = midi_track ();
2246 boost::shared_ptr<RouteList> rl (new RouteList);
2248 rl->push_back (route());
2250 _session->set_exclusive_input_active (rl, !mt->input_active(),
2251 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2257 MixerStrip::midi_input_status_changed ()
2259 if (midi_input_enable_button) {
2260 boost::shared_ptr<MidiTrack> mt = midi_track ();
2262 midi_input_enable_button->set_active (mt->input_active ());
2267 MixerStrip::state_id () const
2269 return string_compose ("strip %1", _route->id().to_s());
2273 MixerStrip::parameter_changed (string p)
2275 if (p == _visibility.get_state_name()) {
2276 /* The user has made changes to the mixer strip visibility, so get
2277 our VisibilityGroup to reflect these changes in our widgets.
2279 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2280 } else if (p == "track-name-number") {
2282 } else if (p == "use-monitor-bus") {
2283 if (monitor_section_button) {
2284 if (mute_button->get_parent()) {
2285 mute_button->get_parent()->remove(*mute_button);
2287 if (monitor_section_button->get_parent()) {
2288 monitor_section_button->get_parent()->remove(*monitor_section_button);
2290 if (Config->get_use_monitor_bus ()) {
2291 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2292 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2293 mute_button->show();
2294 monitor_section_button->show();
2296 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2297 mute_button->show();
2300 } else if (p == "track-name-number") {
2301 update_track_number_visibility();
2305 /** Called to decide whether the solo isolate / solo lock button visibility should
2306 * be overridden from that configured by the user. We do this for the master bus.
2308 * @return optional value that is present if visibility state should be overridden.
2310 boost::optional<bool>
2311 MixerStrip::override_solo_visibility () const
2313 if (_route && _route->is_master ()) {
2314 return boost::optional<bool> (false);
2317 return boost::optional<bool> ();
2321 MixerStrip::add_input_port (DataType t)
2323 _route->input()->add_port ("", this, t);
2327 MixerStrip::add_output_port (DataType t)
2329 _route->output()->add_port ("", this, t);
2333 MixerStrip::route_active_changed ()
2335 reset_strip_style ();
2339 MixerStrip::copy_processors ()
2341 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2345 MixerStrip::cut_processors ()
2347 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2351 MixerStrip::paste_processors ()
2353 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2357 MixerStrip::select_all_processors ()
2359 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2363 MixerStrip::deselect_all_processors ()
2365 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2369 MixerStrip::delete_processors ()
2371 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2375 MixerStrip::toggle_processors ()
2377 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2381 MixerStrip::ab_plugins ()
2383 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2387 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2389 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2392 if (ev->button == 3) {
2393 popup_level_meter_menu (ev);
2401 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2403 using namespace Gtk::Menu_Helpers;
2405 Gtk::Menu* m = manage (new Menu);
2406 MenuList& items = m->items ();
2408 RadioMenuItem::Group group;
2410 _suspend_menu_callbacks = true;
2411 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2412 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2413 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2414 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2415 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2417 if (gpm.meter_channels().n_audio() == 0) {
2418 m->popup (ev->button, ev->time);
2419 _suspend_menu_callbacks = false;
2423 RadioMenuItem::Group tgroup;
2424 items.push_back (SeparatorElem());
2426 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2427 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2428 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2429 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2430 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2431 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2432 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2433 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2434 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2435 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2436 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2439 if (_route->is_master()) {
2442 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2443 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2444 /* non-master bus */
2447 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2454 MeterType cmt = _route->meter_type();
2455 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2457 items.push_back (SeparatorElem());
2458 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2459 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2460 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2461 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2462 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2463 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2465 m->popup (ev->button, ev->time);
2466 _suspend_menu_callbacks = false;
2470 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2471 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2473 using namespace Menu_Helpers;
2475 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2476 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2477 i->set_active (_route->meter_point() == point);
2481 MixerStrip::set_meter_point (MeterPoint p)
2483 if (_suspend_menu_callbacks) return;
2484 _route->set_meter_point (p);
2488 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2489 RadioMenuItem::Group& group, string const & name, MeterType type)
2491 using namespace Menu_Helpers;
2493 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2494 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2495 i->set_active (_route->meter_type() == type);
2499 MixerStrip::set_meter_type (MeterType t)
2501 if (_suspend_menu_callbacks) return;
2506 MixerStrip::vca_menu_toggle (Gtk::CheckMenuItem* menuitem, uint32_t n)
2512 boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2519 /* if this strip is not selected, add it before carrying out
2520 changes to assignment. the user probably didn't notice
2521 that they were clicking on an unselected track.
2523 _mixer.select_strip (*this);
2526 if (!menuitem->get_active()) {
2527 _mixer.do_vca_unassign (vca);
2529 _mixer.do_vca_assign (vca);
2534 MixerStrip::vca_assign (boost::shared_ptr<VCA> vca)
2536 if (!vca || !_route) {
2540 _route->assign (vca);
2544 MixerStrip::vca_unassign (boost::shared_ptr<VCA> vca)
2550 _route->unassign (vca);
2554 MixerStrip::vca_button_release (GdkEventButton* ev)
2556 using namespace Gtk::Menu_Helpers;
2562 /* primary click only */
2564 if (ev->button != 1) {
2569 /* no route - nothing to do */
2573 VCAList vcas (_session->vca_manager().vcas());
2576 /* the button should not have been visible under these conditions */
2580 Menu* menu = new Menu;
2581 MenuList& items = menu->items();
2583 items.push_back (MenuElem (_("Unassign"), sigc::bind (sigc::mem_fun (_mixer, &Mixer_UI::do_vca_unassign), boost::shared_ptr<VCA>())));
2585 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2586 items.push_back (CheckMenuElem ((*v)->name()));
2587 Gtk::CheckMenuItem* item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2588 if (_route->slaved_to (*v)) {
2589 item->set_active (true);
2591 item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), item, (*v)->number()));
2594 menu->popup (1, ev->time);
2600 MixerStrip::update_track_number_visibility ()
2602 DisplaySuspender ds;
2603 bool show_label = _session->config.get_track_name_number();
2605 if (_route && _route->is_master()) {
2610 number_label.show ();
2611 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2612 // except the width of the number label is subtracted from the name-hbox, so we
2613 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2614 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2616 number_label.set_size_request(tnw, -1);
2617 number_label.show ();
2619 number_label.hide ();