2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <sigc++/bind.h>
25 #include "pbd/convert.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/replace_all.h"
28 #include "pbd/stacktrace.h"
30 #include <gtkmm2ext/gtk_ui.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/choice.h>
33 #include <gtkmm2ext/doi.h>
34 #include <gtkmm2ext/slider_controller.h>
35 #include <gtkmm2ext/bindable_button.h>
37 #include "ardour/amp.h"
38 #include "ardour/audio_track.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/internal_send.h"
41 #include "ardour/meter.h"
42 #include "ardour/midi_track.h"
43 #include "ardour/pannable.h"
44 #include "ardour/panner.h"
45 #include "ardour/panner_shell.h"
46 #include "ardour/panner_manager.h"
47 #include "ardour/port.h"
48 #include "ardour/profile.h"
49 #include "ardour/route.h"
50 #include "ardour/route_group.h"
51 #include "ardour/send.h"
52 #include "ardour/session.h"
53 #include "ardour/types.h"
54 #include "ardour/user_bundle.h"
56 #include "ardour_ui.h"
57 #include "ardour_window.h"
58 #include "mixer_strip.h"
61 #include "ardour_button.h"
62 #include "public_editor.h"
64 #include "io_selector.h"
66 #include "gui_thread.h"
67 #include "route_group_menu.h"
68 #include "meter_patterns.h"
72 using namespace ARDOUR;
73 using namespace ARDOUR_UI_UTILS;
76 using namespace Gtkmm2ext;
78 using namespace ArdourMeter;
80 MixerStrip* MixerStrip::_entered_mixer_strip;
82 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
84 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
88 , _mixer_owned (in_mixer)
89 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
92 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
93 , rec_mon_table (2, 2)
94 , solo_iso_table (1, 2)
95 , mute_solo_table (1, 2)
96 , bottom_button_table (1, 3)
97 , meter_point_button (_("pre"))
98 , midi_input_enable_button (0)
99 , _comment_button (_("Comments"))
100 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
101 , _visibility (X_("mixer-element-visibility"))
106 /* the editor mixer strip: don't destroy it every time
107 the underlying route goes away.
110 self_destruct = false;
114 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
118 , _mixer_owned (in_mixer)
119 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
122 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
123 , rec_mon_table (2, 2)
124 , solo_iso_table (1, 2)
125 , mute_solo_table (1, 2)
126 , bottom_button_table (1, 3)
127 , meter_point_button (_("pre"))
128 , midi_input_enable_button (0)
129 , _comment_button (_("Comments"))
130 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
131 , _visibility (X_("mixer-element-visibility"))
140 _entered_mixer_strip= 0;
143 ignore_comment_edit = false;
144 ignore_toggle = false;
149 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
150 longest_label = "longest label";
152 string t = _("Click to toggle the width of this mixer strip.");
154 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
157 width_button.set_icon (ArdourIcon::StripWidth);
158 ARDOUR_UI::instance()->set_tip (width_button, t);
160 hide_button.set_icon (ArdourIcon::CloseCross);
161 ARDOUR_UI::instance()->set_tip (&hide_button, _("Hide this mixer strip"));
163 input_button_box.set_spacing(2);
165 input_button.set_text (_("Input"));
166 input_button.set_name ("mixer strip button");
167 input_button_box.pack_start (input_button, true, true);
169 output_button.set_text (_("Output"));
170 output_button.set_name ("mixer strip button");
172 ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Click to select metering point"), "");
173 meter_point_button.set_name ("mixer strip button");
175 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
177 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
178 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
180 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
182 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
183 solo_isolated_led->show ();
184 solo_isolated_led->set_no_show_all (true);
185 solo_isolated_led->set_name (X_("solo isolate"));
186 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
187 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
188 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
190 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
191 solo_safe_led->show ();
192 solo_safe_led->set_no_show_all (true);
193 solo_safe_led->set_name (X_("solo safe"));
194 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
195 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
196 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
198 solo_safe_led->set_text (S_("SoloLock|Lock"));
199 solo_isolated_led->set_text (_("Iso"));
201 solo_iso_table.set_homogeneous (true);
202 solo_iso_table.set_spacings (2);
203 if (!ARDOUR::Profile->get_trx()) {
204 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
205 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
207 solo_iso_table.show ();
209 rec_mon_table.set_homogeneous (true);
210 rec_mon_table.set_row_spacings (2);
211 rec_mon_table.set_col_spacings (2);
212 if (ARDOUR::Profile->get_mixbus()) {
213 rec_mon_table.resize (1, 3);
214 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
215 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
216 } else if (!ARDOUR::Profile->get_trx()) {
217 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
218 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
220 rec_mon_table.show ();
222 if (solo_isolated_led) {
223 button_size_group->add_widget (*solo_isolated_led);
226 button_size_group->add_widget (*solo_safe_led);
229 if (!ARDOUR::Profile->get_mixbus()) {
230 if (rec_enable_button) {
231 button_size_group->add_widget (*rec_enable_button);
233 if (monitor_disk_button) {
234 button_size_group->add_widget (*monitor_disk_button);
236 if (monitor_input_button) {
237 button_size_group->add_widget (*monitor_input_button);
241 mute_solo_table.set_homogeneous (true);
242 mute_solo_table.set_spacings (2);
244 bottom_button_table.set_spacings (2);
245 bottom_button_table.set_homogeneous (true);
246 bottom_button_table.attach (group_button, 1, 2, 0, 1);
247 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
249 name_button.set_name ("mixer strip button");
250 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
251 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
253 ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
254 group_button.set_name ("mixer strip button");
256 _comment_button.set_name (X_("mixer strip button"));
257 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
259 // TODO implement ArdourKnob::on_size_request properly
260 #define PX_SCALE(px) std::max((float)px, rintf((float)px * ARDOUR_UI::ui_scale))
261 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
263 trim_control.set_tooltip_prefix (_("Trim: "));
264 trim_control.set_name ("trim knob");
265 trim_control.set_no_show_all (true);
266 input_button_box.pack_start (trim_control, false, false);
268 global_vpacker.set_border_width (1);
269 global_vpacker.set_spacing (0);
271 width_button.set_name ("mixer strip button");
272 hide_button.set_name ("mixer strip button");
274 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
275 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
277 // width_hide_box.set_border_width (1);
278 width_hide_box.set_spacing (2);
279 width_hide_box.pack_start (width_button, false, true);
280 width_hide_box.pack_start (number_label, true, true);
281 width_hide_box.pack_end (hide_button, false, true);
283 number_label.set_text ("-");
284 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
285 number_label.set_no_show_all ();
286 number_label.set_name ("tracknumber label");
287 number_label.set_fixed_colors (0x80808080, 0x80808080);
288 number_label.set_alignment (.5, .5);
289 number_label.set_fallthrough_to_parent (true);
291 global_vpacker.set_spacing (2);
292 if (!ARDOUR::Profile->get_trx()) {
293 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
294 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
295 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
296 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (processor_box, true, true);
299 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
300 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
301 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
302 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
305 if (!ARDOUR::Profile->get_trx()) {
306 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
312 global_frame.add (global_vpacker);
313 global_frame.set_shadow_type (Gtk::SHADOW_IN);
314 global_frame.set_name ("BaseFrame");
318 /* force setting of visible selected status */
321 set_selected (false);
326 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
327 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
329 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
330 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
331 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
333 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
334 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
336 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
337 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
338 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
340 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
342 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
343 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
345 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
349 /* start off as a passthru strip. we'll correct this, if necessary,
350 in update_diskstream_display().
353 /* start off as a passthru strip. we'll correct this, if necessary,
354 in update_diskstream_display().
357 if (is_midi_track()) {
358 set_name ("MidiTrackStripBase");
360 set_name ("AudioTrackStripBase");
363 add_events (Gdk::BUTTON_RELEASE_MASK|
364 Gdk::ENTER_NOTIFY_MASK|
365 Gdk::LEAVE_NOTIFY_MASK|
367 Gdk::KEY_RELEASE_MASK);
369 set_flags (get_flags() | Gtk::CAN_FOCUS);
371 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
372 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
375 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
376 must be the same as those used in RCOptionEditor so that the configuration changes
377 are recognised when they occur.
379 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
380 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
381 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
382 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
383 _visibility.add (&output_button, X_("Output"), _("Output"), false);
384 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
386 parameter_changed (X_("mixer-element-visibility"));
387 ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
388 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
389 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
391 //watch for mouse enter/exit so we can do some stuff
392 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
393 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
395 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
398 MixerStrip::~MixerStrip ()
400 CatchDeletion (this);
402 if (this ==_entered_mixer_strip)
403 _entered_mixer_strip = NULL;
407 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
409 _entered_mixer_strip = this;
411 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
412 //because the mixerstrip control is a parent that encompasses the strip
413 deselect_all_processors();
419 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
421 //if we have moved outside our strip, but not into a child view, then deselect ourselves
422 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
423 _entered_mixer_strip= 0;
425 //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
426 gpm.gain_display.set_sensitive(false);
428 gpm.gain_display.set_sensitive(true);
430 //if we leave this mixer strip we need to clear out any selections
431 //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
438 MixerStrip::set_route (boost::shared_ptr<Route> rt)
440 //the rec/monitor stuff only shows up for tracks.
441 //the show_sends only shows up for buses.
442 //remove them all here, and we may add them back later
443 if (show_sends_button->get_parent()) {
444 rec_mon_table.remove (*show_sends_button);
446 if (rec_enable_button->get_parent()) {
447 rec_mon_table.remove (*rec_enable_button);
449 if (monitor_input_button->get_parent()) {
450 rec_mon_table.remove (*monitor_input_button);
452 if (monitor_disk_button->get_parent()) {
453 rec_mon_table.remove (*monitor_disk_button);
455 if (group_button.get_parent()) {
456 bottom_button_table.remove (group_button);
459 RouteUI::set_route (rt);
461 /* ProcessorBox needs access to _route so that it can read
464 processor_box.set_route (rt);
466 revert_to_default_display ();
468 /* unpack these from the parent and stuff them into our own
472 if (gpm.peak_display.get_parent()) {
473 gpm.peak_display.get_parent()->remove (gpm.peak_display);
475 if (gpm.gain_display.get_parent()) {
476 gpm.gain_display.get_parent()->remove (gpm.gain_display);
479 gpm.set_type (rt->meter_type());
481 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
482 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
484 if (solo_button->get_parent()) {
485 mute_solo_table.remove (*solo_button);
488 if (mute_button->get_parent()) {
489 mute_solo_table.remove (*mute_button);
492 if (route()->is_master()) {
493 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
494 solo_button->hide ();
495 mute_button->show ();
496 rec_mon_table.hide ();
497 if (solo_iso_table.get_parent()) {
498 solo_iso_table.get_parent()->remove(solo_iso_table);
501 bottom_button_table.attach (group_button, 1, 2, 0, 1);
502 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
503 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
504 mute_button->show ();
505 solo_button->show ();
506 rec_mon_table.show ();
509 if (_mixer_owned && route()->is_master() ) {
511 HScrollbar scrollbar;
512 Gtk::Requisition requisition(scrollbar.size_request ());
513 int scrollbar_height = requisition.height;
515 spacer = manage (new EventBox);
516 spacer->set_size_request (-1, scrollbar_height+2);
517 global_vpacker.pack_start (*spacer, false, false);
522 monitor_input_button->show ();
523 monitor_disk_button->show ();
525 monitor_input_button->hide();
526 monitor_disk_button->hide ();
529 if (route()->trim() && route()->trim()->active()) {
530 trim_control.show ();
531 trim_control.set_controllable (route()->trim()->gain_control());
533 trim_control.hide ();
534 boost::shared_ptr<Controllable> none;
535 trim_control.set_controllable (none);
538 if (is_midi_track()) {
539 if (midi_input_enable_button == 0) {
540 midi_input_enable_button = manage (new ArdourButton);
541 midi_input_enable_button->set_name ("midi input button");
542 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
543 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
544 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
545 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
546 ARDOUR_UI::instance()->set_tip (midi_input_enable_button, _("Enable/Disable MIDI input"));
548 input_button_box.remove (*midi_input_enable_button);
550 /* get current state */
551 midi_input_status_changed ();
552 input_button_box.pack_start (*midi_input_enable_button, false, false);
554 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
556 if (midi_input_enable_button) {
557 /* removal from the container will delete it */
558 input_button_box.remove (*midi_input_enable_button);
559 midi_input_enable_button = 0;
563 if (is_audio_track()) {
564 boost::shared_ptr<AudioTrack> at = audio_track();
565 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
570 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
571 rec_enable_button->set_sensitive (_session->writable());
572 rec_enable_button->show();
574 if (ARDOUR::Profile->get_mixbus()) {
575 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
576 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
577 } else if (ARDOUR::Profile->get_trx()) {
578 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
580 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
581 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
588 if (!_route->is_master()) {
589 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
590 show_sends_button->show();
594 meter_point_button.set_text (meter_point_string (_route->meter_point()));
596 delete route_ops_menu;
599 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
600 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
601 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
602 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
604 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
606 if (_route->panner_shell()) {
607 update_panner_choices();
608 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
611 if (is_audio_track()) {
612 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
615 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
616 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
618 set_stuff_from_route ();
620 /* now force an update of all the various elements */
622 update_mute_display ();
623 update_solo_display ();
626 route_group_changed ();
629 panners.setup_pan ();
631 if (has_audio_outputs ()) {
637 update_diskstream_display ();
638 update_input_display ();
639 update_output_display ();
641 add_events (Gdk::BUTTON_RELEASE_MASK);
643 processor_box.show ();
645 if (!route()->is_master() && !route()->is_monitor()) {
646 /* we don't allow master or control routes to be hidden */
651 gpm.reset_peak_display ();
652 gpm.gain_display.show ();
653 gpm.peak_display.show ();
656 width_hide_box.show();
658 global_vpacker.show();
659 mute_solo_table.show();
660 bottom_button_table.show();
662 meter_point_button.show();
663 input_button_box.show_all();
664 output_button.show();
666 _comment_button.show();
668 gpm.gain_automation_state_button.show();
670 parameter_changed ("mixer-element-visibility");
676 MixerStrip::set_stuff_from_route ()
678 /* if width is not set, it will be set by the MixerUI or editor */
680 string str = gui_property ("strip-width");
682 set_width_enum (Width (string_2_enum (str, _width)), this);
687 MixerStrip::set_width_enum (Width w, void* owner)
689 /* always set the gpm width again, things may be hidden */
692 panners.set_width (w);
694 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
696 _width_owner = owner;
700 if (_width_owner == this) {
701 set_gui_property ("strip-width", enum_2_string (_width));
706 const float scale = std::max(1.f, ARDOUR_UI::ui_scale);
711 if (show_sends_button) {
712 show_sends_button->set_text (_("Aux"));
715 gpm.gain_automation_style_button.set_text (
716 gpm.astyle_string(gain_automation->automation_style()));
717 gpm.gain_automation_state_button.set_text (
718 gpm.astate_string(gain_automation->automation_state()));
720 if (_route->panner()) {
721 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
722 panners.astyle_string(_route->panner()->automation_style()));
723 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
724 panners.astate_string(_route->panner()->automation_state()));
728 // panners expect an even number of horiz. pixels
729 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
731 set_size_request (width, -1);
737 if (show_sends_button) {
738 show_sends_button->set_text (_("Snd"));
741 gpm.gain_automation_style_button.set_text (
742 gpm.short_astyle_string(gain_automation->automation_style()));
743 gpm.gain_automation_state_button.set_text (
744 gpm.short_astate_string(gain_automation->automation_state()));
745 gain_meter().setup_meters (); // recalc meter width
747 if (_route->panner()) {
748 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
749 panners.short_astyle_string(_route->panner()->automation_style()));
750 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
751 panners.short_astate_string(_route->panner()->automation_state()));
755 // panners expect an even number of horiz. pixels
756 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
758 set_size_request (width, -1);
763 processor_box.set_width (w);
765 update_input_display ();
766 update_output_display ();
767 setup_comment_button ();
768 route_group_changed ();
774 MixerStrip::set_packed (bool yn)
779 set_gui_property ("visible", true);
781 set_gui_property ("visible", false);
786 struct RouteCompareByName {
787 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
788 return a->name().compare (b->name()) < 0;
793 MixerStrip::output_release (GdkEventButton *ev)
795 switch (ev->button) {
797 edit_output_configuration ();
805 MixerStrip::output_press (GdkEventButton *ev)
807 using namespace Menu_Helpers;
808 if (!_session->engine().connected()) {
809 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
814 MenuList& citems = output_menu.items();
815 switch (ev->button) {
818 return false; //wait for the mouse-up to pop the dialog
822 output_menu.set_name ("ArdourContextMenu");
824 output_menu_bundles.clear ();
826 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
828 citems.push_back (SeparatorElem());
829 uint32_t const n_with_separator = citems.size ();
831 ARDOUR::BundleList current = _route->output()->bundles_connected ();
833 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
835 /* give user bundles first chance at being in the menu */
837 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
838 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
839 maybe_add_bundle_to_output_menu (*i, current);
843 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
844 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
845 maybe_add_bundle_to_output_menu (*i, current);
849 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
850 RouteList copy = *routes;
851 copy.sort (RouteCompareByName ());
852 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
853 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
856 if (citems.size() == n_with_separator) {
857 /* no routes added; remove the separator */
861 citems.push_back (SeparatorElem());
863 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
866 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
867 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
872 citems.push_back (SeparatorElem());
873 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
875 output_menu.popup (1, ev->time);
886 MixerStrip::input_release (GdkEventButton *ev)
888 switch (ev->button) {
891 edit_input_configuration ();
903 MixerStrip::input_press (GdkEventButton *ev)
905 using namespace Menu_Helpers;
907 MenuList& citems = input_menu.items();
908 input_menu.set_name ("ArdourContextMenu");
911 if (!_session->engine().connected()) {
912 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
917 if (_session->actively_recording() && _route->record_enabled())
920 switch (ev->button) {
923 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
927 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
929 citems.push_back (SeparatorElem());
930 uint32_t const n_with_separator = citems.size ();
932 input_menu_bundles.clear ();
934 ARDOUR::BundleList current = _route->input()->bundles_connected ();
936 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
938 /* give user bundles first chance at being in the menu */
940 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
941 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
942 maybe_add_bundle_to_input_menu (*i, current);
946 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
947 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
948 maybe_add_bundle_to_input_menu (*i, current);
952 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
953 RouteList copy = *routes;
954 copy.sort (RouteCompareByName ());
955 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
956 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
959 if (citems.size() == n_with_separator) {
960 /* no routes added; remove the separator */
964 citems.push_back (SeparatorElem());
965 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
968 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
969 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
974 citems.push_back (SeparatorElem());
975 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
977 input_menu.popup (1, ev->time);
988 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
994 ARDOUR::BundleList current = _route->input()->bundles_connected ();
996 if (std::find (current.begin(), current.end(), c) == current.end()) {
997 _route->input()->connect_ports_to_bundle (c, true, this);
999 _route->input()->disconnect_ports_from_bundle (c, this);
1004 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1006 if (ignore_toggle) {
1010 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1012 if (std::find (current.begin(), current.end(), c) == current.end()) {
1013 _route->output()->connect_ports_to_bundle (c, true, this);
1015 _route->output()->disconnect_ports_from_bundle (c, this);
1020 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1022 using namespace Menu_Helpers;
1024 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1028 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1029 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1033 if (i != input_menu_bundles.end()) {
1037 input_menu_bundles.push_back (b);
1039 MenuList& citems = input_menu.items();
1041 std::string n = b->name ();
1042 replace_all (n, "_", " ");
1044 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1048 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1050 using namespace Menu_Helpers;
1052 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1056 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1057 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1061 if (i != output_menu_bundles.end()) {
1065 output_menu_bundles.push_back (b);
1067 MenuList& citems = output_menu.items();
1069 std::string n = b->name ();
1070 replace_all (n, "_", " ");
1072 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1076 MixerStrip::update_diskstream_display ()
1078 if (is_track() && input_selector) {
1079 input_selector->hide_all ();
1082 route_color_changed ();
1086 MixerStrip::connect_to_pan ()
1088 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1090 panstate_connection.disconnect ();
1091 panstyle_connection.disconnect ();
1093 if (!_route->panner()) {
1097 boost::shared_ptr<Pannable> p = _route->pannable ();
1099 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1100 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1102 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1103 * However, that only works a panner was previously set.
1105 * PannerUI must remain subscribed to _panshell->Changed() in case
1106 * we switch the panner eg. AUX-Send and back
1107 * _route->panner_shell()->Changed() vs _panshell->Changed
1109 if (panners._panner == 0) {
1110 panners.panshell_changed ();
1112 update_panner_choices();
1116 MixerStrip::update_panner_choices ()
1118 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1119 if (!_route->panner_shell()) { return; }
1121 uint32_t in = _route->output()->n_ports().n_audio();
1123 if (_route->panner()) {
1124 in = _route->panner()->in().n_audio();
1127 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1131 * Output port labelling
1132 * =====================
1134 * Case 1: Each output has one connection, all connections are to system:playback_%i
1135 * out 1 -> system:playback_1
1136 * out 2 -> system:playback_2
1137 * out 3 -> system:playback_3
1140 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1141 * out 1 -> ardour:track_x/in 1
1142 * out 2 -> ardour:track_x/in 2
1143 * Display as: track_x
1145 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1146 * out 1 -> program x:foo
1147 * out 2 -> program x:foo
1148 * Display as: program x
1150 * Case 4: No connections (Disconnected)
1153 * Default case (unusual routing):
1154 * Display as: *number of connections*
1158 * .-----------------------------------------------.
1160 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1161 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1162 * '-----------------------------------------------'
1163 * .-----------------------------------------------.
1166 * '-----------------------------------------------'
1170 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1174 boost::shared_ptr<Port> port;
1175 vector<string> port_connections;
1177 uint32_t total_connection_count = 0;
1178 uint32_t io_connection_count = 0;
1179 uint32_t ardour_connection_count = 0;
1180 uint32_t system_connection_count = 0;
1181 uint32_t other_connection_count = 0;
1182 uint32_t typed_connection_count = 0;
1184 ostringstream label;
1186 bool have_label = false;
1187 bool each_io_has_one_connection = true;
1189 string connection_name;
1190 string ardour_track_name;
1191 string other_connection_type;
1192 string system_ports;
1195 ostringstream tooltip;
1196 char * tooltip_cstr;
1198 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1199 DataType dt = DataType::AUDIO;
1200 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1201 dt = DataType::MIDI;
1202 // avoid further confusion with Midi-tracks that have a synth.
1203 // Audio-ports may be connected, but button says "Disconnected"
1204 tooltip << _("MIDI ");
1208 io_count = route->n_inputs().n_total();
1209 tooltip << string_compose (_("<b>INPUT</b> to %1"), Glib::Markup::escape_text(route->name()));
1211 io_count = route->n_outputs().n_total();
1212 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(route->name()));
1216 for (io_index = 0; io_index < io_count; ++io_index) {
1218 port = route->input()->nth (io_index);
1220 port = route->output()->nth (io_index);
1223 port_connections.clear ();
1224 port->get_connections(port_connections);
1226 //ignore any port connections that don't match our DataType
1227 if (port->type() != dt) {
1228 if (!port_connections.empty()) {
1229 ++typed_connection_count;
1234 io_connection_count = 0;
1236 if (!port_connections.empty()) {
1237 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1239 string& connection_name (*i);
1241 if (connection_name.find("system:") == 0) {
1242 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1245 if (io_connection_count == 0) {
1246 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1248 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1251 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1254 if (connection_name.find("ardour:") == 0) {
1255 if (ardour_track_name.empty()) {
1256 // "ardour:Master/in 1" -> "ardour:Master/"
1257 string::size_type slash = connection_name.find("/");
1258 if (slash != string::npos) {
1259 ardour_track_name = connection_name.substr(0, slash + 1);
1263 if (connection_name.find(ardour_track_name) == 0) {
1264 ++ardour_connection_count;
1266 } else if (!pn.empty()) {
1267 if (system_ports.empty()) {
1270 system_ports += "/" + pn;
1272 if (connection_name.find("system:") == 0) {
1273 ++system_connection_count;
1275 } else if (connection_name.find("system:midi_") == 0) {
1277 // "system:midi_capture_123" -> "123"
1278 system_port = "M " + connection_name.substr(20);
1280 // "system:midi_playback_123" -> "123"
1281 system_port = "M " + connection_name.substr(21);
1284 if (system_ports.empty()) {
1285 system_ports += system_port;
1287 system_ports += "/" + system_port;
1290 ++system_connection_count;
1292 } else if (connection_name.find("system:") == 0) {
1294 // "system:capture_123" -> "123"
1295 system_port = connection_name.substr(15);
1297 // "system:playback_123" -> "123"
1298 system_port = connection_name.substr(16);
1301 if (system_ports.empty()) {
1302 system_ports += system_port;
1304 system_ports += "/" + system_port;
1307 ++system_connection_count;
1309 if (other_connection_type.empty()) {
1310 // "jamin:in 1" -> "jamin:"
1311 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1314 if (connection_name.find(other_connection_type) == 0) {
1315 ++other_connection_count;
1319 ++total_connection_count;
1320 ++io_connection_count;
1324 if (io_connection_count != 1) {
1325 each_io_has_one_connection = false;
1329 if (total_connection_count == 0) {
1330 tooltip << endl << _("Disconnected");
1333 tooltip_cstr = new char[tooltip.str().size() + 1];
1334 strcpy(tooltip_cstr, tooltip.str().c_str());
1337 ARDOUR_UI::instance()->set_tip (&input_button, tooltip_cstr, "");
1339 ARDOUR_UI::instance()->set_tip (&output_button, tooltip_cstr, "");
1342 if (each_io_has_one_connection) {
1343 if (total_connection_count == ardour_connection_count) {
1344 // all connections are to the same track in ardour
1345 // "ardour:Master/" -> "Master"
1346 string::size_type slash = ardour_track_name.find("/");
1347 if (slash != string::npos) {
1348 label << ardour_track_name.substr(7, slash - 7);
1352 else if (total_connection_count == system_connection_count) {
1353 // all connections are to system ports
1354 label << system_ports;
1357 else if (total_connection_count == other_connection_count) {
1358 // all connections are to the same external program eg jamin
1359 // "jamin:" -> "jamin"
1360 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1366 if (total_connection_count == 0) {
1370 // Odd configuration
1371 label << "*" << total_connection_count << "*";
1373 if (typed_connection_count > 0) {
1374 label << "\u2295"; // circled plus
1379 input_button.set_text (label.str());
1381 output_button.set_text (label.str());
1386 MixerStrip::update_input_display ()
1388 update_io_button (_route, _width, true);
1389 panners.setup_pan ();
1391 if (has_audio_outputs ()) {
1392 panners.show_all ();
1394 panners.hide_all ();
1400 MixerStrip::update_output_display ()
1402 update_io_button (_route, _width, false);
1403 gpm.setup_meters ();
1404 panners.setup_pan ();
1406 if (has_audio_outputs ()) {
1407 panners.show_all ();
1409 panners.hide_all ();
1414 MixerStrip::fast_update ()
1416 gpm.update_meters ();
1420 MixerStrip::diskstream_changed ()
1422 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1426 MixerStrip::io_changed_proxy ()
1428 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1432 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1434 boost::shared_ptr<Port> a = wa.lock ();
1435 boost::shared_ptr<Port> b = wb.lock ();
1437 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1438 update_input_display ();
1439 set_width_enum (_width, this);
1442 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1443 update_output_display ();
1444 set_width_enum (_width, this);
1449 MixerStrip::setup_comment_button ()
1454 if (_route->comment().empty ()) {
1455 _comment_button.unset_bg (STATE_NORMAL);
1456 _comment_button.set_text (_("Comments"));
1458 _comment_button.modify_bg (STATE_NORMAL, color ());
1459 _comment_button.set_text (_("*Comments*"));
1464 if (_route->comment().empty ()) {
1465 _comment_button.unset_bg (STATE_NORMAL);
1466 _comment_button.set_text (_("Cmt"));
1468 _comment_button.modify_bg (STATE_NORMAL, color ());
1469 _comment_button.set_text (_("*Cmt*"));
1474 ARDOUR_UI::instance()->set_tip (
1475 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1481 MixerStrip::select_route_group (GdkEventButton *ev)
1483 using namespace Menu_Helpers;
1485 if (ev->button == 1) {
1487 if (group_menu == 0) {
1489 PropertyList* plist = new PropertyList();
1491 plist->add (Properties::gain, true);
1492 plist->add (Properties::mute, true);
1493 plist->add (Properties::solo, true);
1495 group_menu = new RouteGroupMenu (_session, plist);
1499 r.push_back (route ());
1500 group_menu->build (r);
1501 group_menu->menu()->popup (1, ev->time);
1508 MixerStrip::route_group_changed ()
1510 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1512 RouteGroup *rg = _route->route_group();
1515 group_button.set_text (PBD::short_version (rg->name(), 5));
1519 group_button.set_text (_("Grp"));
1522 group_button.set_text (_("~G"));
1529 MixerStrip::route_color_changed ()
1531 name_button.modify_bg (STATE_NORMAL, color());
1532 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1533 reset_strip_style ();
1537 MixerStrip::show_passthru_color ()
1539 reset_strip_style ();
1543 MixerStrip::build_route_ops_menu ()
1545 using namespace Menu_Helpers;
1546 route_ops_menu = new Menu;
1547 route_ops_menu->set_name ("ArdourContextMenu");
1549 MenuList& items = route_ops_menu->items();
1551 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1553 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1555 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1557 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1559 items.push_back (SeparatorElem());
1561 if (!_route->is_master()) {
1562 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1564 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1565 rename_menu_item = &items.back();
1567 items.push_back (SeparatorElem());
1568 items.push_back (CheckMenuElem (_("Active")));
1569 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1570 i->set_active (_route->active());
1571 i->set_sensitive(! _session->transport_rolling());
1572 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1574 items.push_back (SeparatorElem());
1576 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1578 items.push_back (SeparatorElem());
1579 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1580 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1581 denormal_menu_item->set_active (_route->denormal_protection());
1583 if (!Profile->get_sae()) {
1584 items.push_back (SeparatorElem());
1585 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1588 items.push_back (SeparatorElem());
1591 /* note that this relies on selection being shared across editor and
1592 mixer (or global to the backend, in the future), which is the only
1593 sane thing for users anyway.
1596 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1598 Selection& selection (PublicEditor::instance().get_selection());
1599 if (!selection.selected (rtav)) {
1600 selection.set (rtav);
1603 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1609 MixerStrip::name_button_button_press (GdkEventButton* ev)
1611 if (ev->button == 3) {
1612 list_route_operations ();
1614 /* do not allow rename if the track is record-enabled */
1615 rename_menu_item->set_sensitive (!_route->record_enabled());
1616 route_ops_menu->popup (1, ev->time);
1625 MixerStrip::name_button_button_release (GdkEventButton* ev)
1627 if (ev->button == 1) {
1628 list_route_operations ();
1630 /* do not allow rename if the track is record-enabled */
1631 rename_menu_item->set_sensitive (!_route->record_enabled());
1632 route_ops_menu->popup (1, ev->time);
1639 MixerStrip::number_button_button_press (GdkEventButton* ev)
1641 if ( ev->button == 3 ) {
1642 list_route_operations ();
1644 /* do not allow rename if the track is record-enabled */
1645 rename_menu_item->set_sensitive (!_route->record_enabled());
1646 route_ops_menu->popup (1, ev->time);
1655 MixerStrip::list_route_operations ()
1657 delete route_ops_menu;
1658 build_route_ops_menu ();
1662 MixerStrip::set_selected (bool yn)
1664 AxisView::set_selected (yn);
1666 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1667 global_frame.set_name ("MixerStripSelectedFrame");
1669 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1670 global_frame.set_name ("MixerStripFrame");
1672 global_frame.queue_draw ();
1675 // processor_box.deselect_all_processors();
1679 MixerStrip::property_changed (const PropertyChange& what_changed)
1681 RouteUI::property_changed (what_changed);
1683 if (what_changed.contains (ARDOUR::Properties::name)) {
1689 MixerStrip::name_changed ()
1693 name_button.set_text (_route->name());
1696 name_button.set_text (PBD::short_version (_route->name(), 5));
1700 ARDOUR_UI::instance()->set_tip (name_button, _route->name());
1702 if (_session->config.get_track_name_number()) {
1703 const int64_t track_number = _route->track_number ();
1704 if (track_number == 0) {
1705 number_label.set_text ("-");
1707 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1710 number_label.set_text ("");
1715 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1717 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1721 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1723 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1727 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1729 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1733 MixerStrip::width_button_pressed (GdkEventButton* ev)
1735 if (ev->button != 1) {
1739 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1742 _mixer.set_strip_width (Narrow, true);
1746 _mixer.set_strip_width (Wide, true);
1752 set_width_enum (Narrow, this);
1755 set_width_enum (Wide, this);
1764 MixerStrip::hide_clicked ()
1766 // LAME fix to reset the button status for when it is redisplayed (part 1)
1767 hide_button.set_sensitive(false);
1770 Hiding(); /* EMIT_SIGNAL */
1772 _mixer.hide_strip (this);
1776 hide_button.set_sensitive(true);
1780 MixerStrip::set_embedded (bool yn)
1786 MixerStrip::map_frozen ()
1788 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1790 boost::shared_ptr<AudioTrack> at = audio_track();
1793 switch (at->freeze_state()) {
1794 case AudioTrack::Frozen:
1795 processor_box.set_sensitive (false);
1796 hide_redirect_editors ();
1799 processor_box.set_sensitive (true);
1800 // XXX need some way, maybe, to retoggle redirect editors
1807 MixerStrip::hide_redirect_editors ()
1809 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1813 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1815 boost::shared_ptr<Processor> processor (p.lock ());
1820 Gtk::Window* w = processor_box.get_processor_ui (processor);
1828 MixerStrip::reset_strip_style ()
1830 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1832 gpm.set_fader_name ("SendStripBase");
1836 if (is_midi_track()) {
1837 if (_route->active()) {
1838 set_name ("MidiTrackStripBase");
1840 set_name ("MidiTrackStripBaseInactive");
1842 gpm.set_fader_name ("MidiTrackFader");
1843 } else if (is_audio_track()) {
1844 if (_route->active()) {
1845 set_name ("AudioTrackStripBase");
1847 set_name ("AudioTrackStripBaseInactive");
1849 gpm.set_fader_name ("AudioTrackFader");
1851 if (_route->active()) {
1852 set_name ("AudioBusStripBase");
1854 set_name ("AudioBusStripBaseInactive");
1856 gpm.set_fader_name ("AudioBusFader");
1858 /* (no MIDI busses yet) */
1865 MixerStrip::engine_stopped ()
1870 MixerStrip::engine_running ()
1875 MixerStrip::meter_point_string (MeterPoint mp)
1888 case MeterPostFader:
1905 return S_("Meter|In");
1909 return S_("Meter|Pr");
1912 case MeterPostFader:
1913 return S_("Meter|Po");
1917 return S_("Meter|O");
1922 return S_("Meter|C");
1931 /** Called when the metering point has changed */
1933 MixerStrip::meter_changed ()
1935 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1936 gpm.setup_meters ();
1937 // reset peak when meter point changes
1938 gpm.reset_peak_display();
1941 /** The bus that we are displaying sends to has changed, or been turned off.
1942 * @param send_to New bus that we are displaying sends to, or 0.
1945 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1947 RouteUI::bus_send_display_changed (send_to);
1950 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1955 revert_to_default_display ();
1958 revert_to_default_display ();
1963 MixerStrip::drop_send ()
1965 boost::shared_ptr<Send> current_send;
1967 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1968 current_send->set_metering (false);
1971 send_gone_connection.disconnect ();
1972 input_button.set_sensitive (true);
1973 output_button.set_sensitive (true);
1974 group_button.set_sensitive (true);
1975 set_invert_sensitive (true);
1976 meter_point_button.set_sensitive (true);
1977 mute_button->set_sensitive (true);
1978 solo_button->set_sensitive (true);
1979 rec_enable_button->set_sensitive (true);
1980 solo_isolated_led->set_sensitive (true);
1981 solo_safe_led->set_sensitive (true);
1982 monitor_input_button->set_sensitive (true);
1983 monitor_disk_button->set_sensitive (true);
1984 _comment_button.set_sensitive (true);
1988 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1990 _current_delivery = d;
1991 DeliveryChanged (_current_delivery);
1995 MixerStrip::show_send (boost::shared_ptr<Send> send)
2001 set_current_delivery (send);
2003 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2004 send->set_metering (true);
2005 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2007 gain_meter().set_controls (_route, send->meter(), send->amp());
2008 gain_meter().setup_meters ();
2010 uint32_t const in = _current_delivery->pans_required();
2011 uint32_t const out = _current_delivery->pan_outs();
2013 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2014 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2015 panner_ui().setup_pan ();
2016 panner_ui().set_send_drawing_mode (true);
2017 panner_ui().show_all ();
2019 input_button.set_sensitive (false);
2020 group_button.set_sensitive (false);
2021 set_invert_sensitive (false);
2022 meter_point_button.set_sensitive (false);
2023 mute_button->set_sensitive (false);
2024 solo_button->set_sensitive (false);
2025 rec_enable_button->set_sensitive (false);
2026 solo_isolated_led->set_sensitive (false);
2027 solo_safe_led->set_sensitive (false);
2028 monitor_input_button->set_sensitive (false);
2029 monitor_disk_button->set_sensitive (false);
2030 _comment_button.set_sensitive (false);
2032 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2033 output_button.set_sensitive (false);
2036 reset_strip_style ();
2040 MixerStrip::revert_to_default_display ()
2044 set_current_delivery (_route->main_outs ());
2046 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2047 gain_meter().setup_meters ();
2049 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2050 update_panner_choices();
2051 panner_ui().setup_pan ();
2052 panner_ui().set_send_drawing_mode (false);
2054 if (has_audio_outputs ()) {
2055 panners.show_all ();
2057 panners.hide_all ();
2060 reset_strip_style ();
2064 MixerStrip::set_button_names ()
2068 mute_button->set_text (_("Mute"));
2069 monitor_input_button->set_text (_("In"));
2070 monitor_disk_button->set_text (_("Disk"));
2072 if (_route && _route->solo_safe()) {
2073 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2075 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2077 if (!Config->get_solo_control_is_listen_control()) {
2078 solo_button->set_text (_("Solo"));
2080 switch (Config->get_listen_position()) {
2081 case AfterFaderListen:
2082 solo_button->set_text (_("AFL"));
2084 case PreFaderListen:
2085 solo_button->set_text (_("PFL"));
2089 solo_isolated_led->set_text (_("Iso"));
2090 solo_safe_led->set_text (S_("SoloLock|Lock"));
2094 mute_button->set_text (S_("Mute|M"));
2095 monitor_input_button->set_text (S_("MonitorInput|I"));
2096 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2098 if (_route && _route->solo_safe()) {
2099 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2101 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2103 if (!Config->get_solo_control_is_listen_control()) {
2104 solo_button->set_text (S_("Solo|S"));
2106 switch (Config->get_listen_position()) {
2107 case AfterFaderListen:
2108 solo_button->set_text (S_("AfterFader|A"));
2110 case PreFaderListen:
2111 solo_button->set_text (S_("Prefader|P"));
2116 solo_isolated_led->set_text (S_("SoloIso|I"));
2117 solo_safe_led->set_text (S_("SoloLock|L"));
2122 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2124 meter_point_button.set_text ("");
2129 MixerStrip::plugin_selector()
2131 return _mixer.plugin_selector();
2135 MixerStrip::hide_things ()
2137 processor_box.hide_things ();
2141 MixerStrip::input_active_button_press (GdkEventButton*)
2143 /* nothing happens on press */
2148 MixerStrip::input_active_button_release (GdkEventButton* ev)
2150 boost::shared_ptr<MidiTrack> mt = midi_track ();
2156 boost::shared_ptr<RouteList> rl (new RouteList);
2158 rl->push_back (route());
2160 _session->set_exclusive_input_active (rl, !mt->input_active(),
2161 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2167 MixerStrip::midi_input_status_changed ()
2169 if (midi_input_enable_button) {
2170 boost::shared_ptr<MidiTrack> mt = midi_track ();
2172 midi_input_enable_button->set_active (mt->input_active ());
2177 MixerStrip::state_id () const
2179 return string_compose ("strip %1", _route->id().to_s());
2183 MixerStrip::parameter_changed (string p)
2185 if (p == _visibility.get_state_name()) {
2186 /* The user has made changes to the mixer strip visibility, so get
2187 our VisibilityGroup to reflect these changes in our widgets.
2189 _visibility.set_state (ARDOUR_UI::config()->get_mixer_strip_visibility ());
2191 else if (p == "track-name-number") {
2196 /** Called to decide whether the solo isolate / solo lock button visibility should
2197 * be overridden from that configured by the user. We do this for the master bus.
2199 * @return optional value that is present if visibility state should be overridden.
2201 boost::optional<bool>
2202 MixerStrip::override_solo_visibility () const
2204 if (_route && _route->is_master ()) {
2205 return boost::optional<bool> (false);
2208 return boost::optional<bool> ();
2212 MixerStrip::add_input_port (DataType t)
2214 _route->input()->add_port ("", this, t);
2218 MixerStrip::add_output_port (DataType t)
2220 _route->output()->add_port ("", this, t);
2224 MixerStrip::route_active_changed ()
2226 reset_strip_style ();
2230 MixerStrip::copy_processors ()
2232 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2236 MixerStrip::cut_processors ()
2238 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2242 MixerStrip::paste_processors ()
2244 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2248 MixerStrip::select_all_processors ()
2250 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2254 MixerStrip::deselect_all_processors ()
2256 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2260 MixerStrip::delete_processors ()
2262 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2266 MixerStrip::toggle_processors ()
2268 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2272 MixerStrip::ab_plugins ()
2274 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2278 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2280 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2283 if (ev->button == 3) {
2284 popup_level_meter_menu (ev);
2292 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2294 using namespace Gtk::Menu_Helpers;
2296 Gtk::Menu* m = manage (new Menu);
2297 MenuList& items = m->items ();
2299 RadioMenuItem::Group group;
2301 _suspend_menu_callbacks = true;
2302 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2303 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2304 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2305 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2306 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2308 if (gpm.meter_channels().n_audio() == 0) {
2309 m->popup (ev->button, ev->time);
2310 _suspend_menu_callbacks = false;
2314 RadioMenuItem::Group tgroup;
2315 items.push_back (SeparatorElem());
2317 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2318 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2319 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2320 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2321 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2322 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2323 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2324 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2325 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2326 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2327 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2330 if (_route->is_master()) {
2333 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2334 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2335 /* non-master bus */
2338 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2345 MeterType cmt = _route->meter_type();
2346 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2348 items.push_back (SeparatorElem());
2349 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2350 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2351 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2352 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2353 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2354 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2356 m->popup (ev->button, ev->time);
2357 _suspend_menu_callbacks = false;
2361 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2362 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2364 using namespace Menu_Helpers;
2366 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2367 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2368 i->set_active (_route->meter_point() == point);
2372 MixerStrip::set_meter_point (MeterPoint p)
2374 if (_suspend_menu_callbacks) return;
2375 _route->set_meter_point (p);
2379 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2380 RadioMenuItem::Group& group, string const & name, MeterType type)
2382 using namespace Menu_Helpers;
2384 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2385 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2386 i->set_active (_route->meter_type() == type);
2390 MixerStrip::set_meter_type (MeterType t)
2392 if (_suspend_menu_callbacks) return;