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_window.h"
57 #include "mixer_strip.h"
60 #include "ardour_button.h"
61 #include "public_editor.h"
63 #include "io_selector.h"
65 #include "gui_thread.h"
66 #include "route_group_menu.h"
67 #include "meter_patterns.h"
69 #include "ui_config.h"
73 using namespace ARDOUR;
74 using namespace ARDOUR_UI_UTILS;
77 using namespace Gtkmm2ext;
79 using namespace ArdourMeter;
81 MixerStrip* MixerStrip::_entered_mixer_strip;
83 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
85 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
89 , _mixer_owned (in_mixer)
90 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
93 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
94 , rec_mon_table (2, 2)
95 , solo_iso_table (1, 2)
96 , mute_solo_table (1, 2)
97 , bottom_button_table (1, 3)
98 , meter_point_button (_("pre"))
99 , midi_input_enable_button (0)
100 , _comment_button (_("Comments"))
101 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
102 , _visibility (X_("mixer-element-visibility"))
107 /* the editor mixer strip: don't destroy it every time
108 the underlying route goes away.
111 self_destruct = false;
115 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
119 , _mixer_owned (in_mixer)
120 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
123 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
124 , rec_mon_table (2, 2)
125 , solo_iso_table (1, 2)
126 , mute_solo_table (1, 2)
127 , bottom_button_table (1, 3)
128 , meter_point_button (_("pre"))
129 , midi_input_enable_button (0)
130 , _comment_button (_("Comments"))
131 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
132 , _visibility (X_("mixer-element-visibility"))
141 _entered_mixer_strip= 0;
144 ignore_comment_edit = false;
145 ignore_toggle = false;
150 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
151 longest_label = "longest label";
153 string t = _("Click to toggle the width of this mixer strip.");
155 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
158 width_button.set_icon (ArdourIcon::StripWidth);
159 set_tooltip (width_button, t);
161 hide_button.set_icon (ArdourIcon::CloseCross);
162 set_tooltip (&hide_button, _("Hide this mixer strip"));
164 input_button_box.set_spacing(2);
166 input_button.set_text (_("Input"));
167 input_button.set_name ("mixer strip button");
168 input_button_box.pack_start (input_button, true, true);
170 output_button.set_text (_("Output"));
171 output_button.set_name ("mixer strip button");
173 set_tooltip (&meter_point_button, _("Click to select metering point"));
174 meter_point_button.set_name ("mixer strip button");
176 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
178 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
179 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
181 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
183 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
184 solo_isolated_led->show ();
185 solo_isolated_led->set_no_show_all (true);
186 solo_isolated_led->set_name (X_("solo isolate"));
187 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
188 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
189 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
191 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
192 solo_safe_led->show ();
193 solo_safe_led->set_no_show_all (true);
194 solo_safe_led->set_name (X_("solo safe"));
195 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
196 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
197 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
199 solo_safe_led->set_text (S_("SoloLock|Lock"));
200 solo_isolated_led->set_text (_("Iso"));
202 solo_iso_table.set_homogeneous (true);
203 solo_iso_table.set_spacings (2);
204 if (!ARDOUR::Profile->get_trx()) {
205 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
206 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
208 solo_iso_table.show ();
210 rec_mon_table.set_homogeneous (true);
211 rec_mon_table.set_row_spacings (2);
212 rec_mon_table.set_col_spacings (2);
213 if (ARDOUR::Profile->get_mixbus()) {
214 rec_mon_table.resize (1, 3);
215 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
216 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
217 } else if (!ARDOUR::Profile->get_trx()) {
218 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
219 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
221 rec_mon_table.show ();
223 if (solo_isolated_led) {
224 button_size_group->add_widget (*solo_isolated_led);
227 button_size_group->add_widget (*solo_safe_led);
230 if (!ARDOUR::Profile->get_mixbus()) {
231 if (rec_enable_button) {
232 button_size_group->add_widget (*rec_enable_button);
234 if (monitor_disk_button) {
235 button_size_group->add_widget (*monitor_disk_button);
237 if (monitor_input_button) {
238 button_size_group->add_widget (*monitor_input_button);
242 mute_solo_table.set_homogeneous (true);
243 mute_solo_table.set_spacings (2);
245 bottom_button_table.set_spacings (2);
246 bottom_button_table.set_homogeneous (true);
247 bottom_button_table.attach (group_button, 1, 2, 0, 1);
248 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
250 name_button.set_name ("mixer strip button");
251 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
252 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
254 set_tooltip (&group_button, _("Mix group"));
255 group_button.set_name ("mixer strip button");
257 _comment_button.set_name (X_("mixer strip button"));
258 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
260 // TODO implement ArdourKnob::on_size_request properly
261 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
262 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
264 trim_control.set_tooltip_prefix (_("Trim: "));
265 trim_control.set_name ("trim knob");
266 trim_control.set_no_show_all (true);
267 input_button_box.pack_start (trim_control, false, false);
269 global_vpacker.set_border_width (1);
270 global_vpacker.set_spacing (0);
272 width_button.set_name ("mixer strip button");
273 hide_button.set_name ("mixer strip button");
275 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
276 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
278 // width_hide_box.set_border_width (1);
279 width_hide_box.set_spacing (2);
280 width_hide_box.pack_start (width_button, false, true);
281 width_hide_box.pack_start (number_label, true, true);
282 width_hide_box.pack_end (hide_button, false, true);
284 number_label.set_text ("-");
285 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
286 number_label.set_no_show_all ();
287 number_label.set_name ("tracknumber label");
288 number_label.set_fixed_colors (0x80808080, 0x80808080);
289 number_label.set_alignment (.5, .5);
290 number_label.set_fallthrough_to_parent (true);
292 global_vpacker.set_spacing (2);
293 if (!ARDOUR::Profile->get_trx()) {
294 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
295 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
296 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
298 global_vpacker.pack_start (processor_box, true, true);
300 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
301 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
302 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
305 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
306 if (!ARDOUR::Profile->get_trx()) {
307 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
308 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
313 global_frame.add (global_vpacker);
314 global_frame.set_shadow_type (Gtk::SHADOW_IN);
315 global_frame.set_name ("BaseFrame");
319 /* force setting of visible selected status */
322 set_selected (false);
327 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
328 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
330 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
331 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
332 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
334 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
335 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
337 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
338 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
339 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
341 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
343 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
344 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
346 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
350 /* start off as a passthru strip. we'll correct this, if necessary,
351 in update_diskstream_display().
354 /* start off as a passthru strip. we'll correct this, if necessary,
355 in update_diskstream_display().
358 if (is_midi_track()) {
359 set_name ("MidiTrackStripBase");
361 set_name ("AudioTrackStripBase");
364 add_events (Gdk::BUTTON_RELEASE_MASK|
365 Gdk::ENTER_NOTIFY_MASK|
366 Gdk::LEAVE_NOTIFY_MASK|
368 Gdk::KEY_RELEASE_MASK);
370 set_flags (get_flags() | Gtk::CAN_FOCUS);
372 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
373 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
376 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
377 must be the same as those used in RCOptionEditor so that the configuration changes
378 are recognised when they occur.
380 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
381 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
382 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
383 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
384 _visibility.add (&output_button, X_("Output"), _("Output"), false);
385 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
387 parameter_changed (X_("mixer-element-visibility"));
388 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
389 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
390 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
392 //watch for mouse enter/exit so we can do some stuff
393 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
394 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
396 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
399 MixerStrip::~MixerStrip ()
401 CatchDeletion (this);
403 if (this ==_entered_mixer_strip)
404 _entered_mixer_strip = NULL;
408 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
410 _entered_mixer_strip = this;
412 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
413 //because the mixerstrip control is a parent that encompasses the strip
414 deselect_all_processors();
420 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
422 //if we have moved outside our strip, but not into a child view, then deselect ourselves
423 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
424 _entered_mixer_strip= 0;
426 //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
427 gpm.gain_display.set_sensitive(false);
429 gpm.gain_display.set_sensitive(true);
431 //if we leave this mixer strip we need to clear out any selections
432 //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
439 MixerStrip::set_route (boost::shared_ptr<Route> rt)
441 //the rec/monitor stuff only shows up for tracks.
442 //the show_sends only shows up for buses.
443 //remove them all here, and we may add them back later
444 if (show_sends_button->get_parent()) {
445 rec_mon_table.remove (*show_sends_button);
447 if (rec_enable_button->get_parent()) {
448 rec_mon_table.remove (*rec_enable_button);
450 if (monitor_input_button->get_parent()) {
451 rec_mon_table.remove (*monitor_input_button);
453 if (monitor_disk_button->get_parent()) {
454 rec_mon_table.remove (*monitor_disk_button);
456 if (group_button.get_parent()) {
457 bottom_button_table.remove (group_button);
460 RouteUI::set_route (rt);
462 /* ProcessorBox needs access to _route so that it can read
465 processor_box.set_route (rt);
467 revert_to_default_display ();
469 /* unpack these from the parent and stuff them into our own
473 if (gpm.peak_display.get_parent()) {
474 gpm.peak_display.get_parent()->remove (gpm.peak_display);
476 if (gpm.gain_display.get_parent()) {
477 gpm.gain_display.get_parent()->remove (gpm.gain_display);
480 gpm.set_type (rt->meter_type());
482 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
483 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
485 if (solo_button->get_parent()) {
486 mute_solo_table.remove (*solo_button);
489 if (mute_button->get_parent()) {
490 mute_solo_table.remove (*mute_button);
493 if (route()->is_master()) {
494 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
495 solo_button->hide ();
496 mute_button->show ();
497 rec_mon_table.hide ();
498 if (solo_iso_table.get_parent()) {
499 solo_iso_table.get_parent()->remove(solo_iso_table);
502 bottom_button_table.attach (group_button, 1, 2, 0, 1);
503 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
504 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
505 mute_button->show ();
506 solo_button->show ();
507 rec_mon_table.show ();
510 if (_mixer_owned && route()->is_master() ) {
512 HScrollbar scrollbar;
513 Gtk::Requisition requisition(scrollbar.size_request ());
514 int scrollbar_height = requisition.height;
516 spacer = manage (new EventBox);
517 spacer->set_size_request (-1, scrollbar_height+2);
518 global_vpacker.pack_start (*spacer, false, false);
523 monitor_input_button->show ();
524 monitor_disk_button->show ();
526 monitor_input_button->hide();
527 monitor_disk_button->hide ();
530 if (route()->trim() && route()->trim()->active()) {
531 trim_control.show ();
532 trim_control.set_controllable (route()->trim()->gain_control());
534 trim_control.hide ();
535 boost::shared_ptr<Controllable> none;
536 trim_control.set_controllable (none);
539 if (is_midi_track()) {
540 if (midi_input_enable_button == 0) {
541 midi_input_enable_button = manage (new ArdourButton);
542 midi_input_enable_button->set_name ("midi input button");
543 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
544 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
545 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
546 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
547 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
549 input_button_box.remove (*midi_input_enable_button);
551 /* get current state */
552 midi_input_status_changed ();
553 input_button_box.pack_start (*midi_input_enable_button, false, false);
555 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
557 if (midi_input_enable_button) {
558 /* removal from the container will delete it */
559 input_button_box.remove (*midi_input_enable_button);
560 midi_input_enable_button = 0;
564 if (is_audio_track()) {
565 boost::shared_ptr<AudioTrack> at = audio_track();
566 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
571 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
572 rec_enable_button->set_sensitive (_session->writable());
573 rec_enable_button->show();
575 if (ARDOUR::Profile->get_mixbus()) {
576 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
577 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
578 } else if (ARDOUR::Profile->get_trx()) {
579 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
581 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
582 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
589 if (!_route->is_master()) {
590 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
591 show_sends_button->show();
595 meter_point_button.set_text (meter_point_string (_route->meter_point()));
597 delete route_ops_menu;
600 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
601 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
602 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
603 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
605 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
607 if (_route->panner_shell()) {
608 update_panner_choices();
609 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
612 if (is_audio_track()) {
613 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
616 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
617 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
619 set_stuff_from_route ();
621 /* now force an update of all the various elements */
623 update_mute_display ();
624 update_solo_display ();
627 route_group_changed ();
630 panners.setup_pan ();
632 if (has_audio_outputs ()) {
638 update_diskstream_display ();
639 update_input_display ();
640 update_output_display ();
642 add_events (Gdk::BUTTON_RELEASE_MASK);
644 processor_box.show ();
646 if (!route()->is_master() && !route()->is_monitor()) {
647 /* we don't allow master or control routes to be hidden */
652 gpm.reset_peak_display ();
653 gpm.gain_display.show ();
654 gpm.peak_display.show ();
657 width_hide_box.show();
659 global_vpacker.show();
660 mute_solo_table.show();
661 bottom_button_table.show();
663 meter_point_button.show();
664 input_button_box.show_all();
665 output_button.show();
667 _comment_button.show();
669 gpm.gain_automation_state_button.show();
671 parameter_changed ("mixer-element-visibility");
677 MixerStrip::set_stuff_from_route ()
679 /* if width is not set, it will be set by the MixerUI or editor */
681 string str = gui_property ("strip-width");
683 set_width_enum (Width (string_2_enum (str, _width)), this);
688 MixerStrip::set_width_enum (Width w, void* owner)
690 /* always set the gpm width again, things may be hidden */
693 panners.set_width (w);
695 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
697 _width_owner = owner;
701 if (_width_owner == this) {
702 set_gui_property ("strip-width", enum_2_string (_width));
707 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
712 if (show_sends_button) {
713 show_sends_button->set_text (_("Aux"));
716 gpm.gain_automation_style_button.set_text (
717 gpm.astyle_string(gain_automation->automation_style()));
718 gpm.gain_automation_state_button.set_text (
719 gpm.astate_string(gain_automation->automation_state()));
721 if (_route->panner()) {
722 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
723 panners.astyle_string(_route->panner()->automation_style()));
724 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
725 panners.astate_string(_route->panner()->automation_state()));
729 // panners expect an even number of horiz. pixels
730 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
732 set_size_request (width, -1);
738 if (show_sends_button) {
739 show_sends_button->set_text (_("Snd"));
742 gpm.gain_automation_style_button.set_text (
743 gpm.short_astyle_string(gain_automation->automation_style()));
744 gpm.gain_automation_state_button.set_text (
745 gpm.short_astate_string(gain_automation->automation_state()));
746 gain_meter().setup_meters (); // recalc meter width
748 if (_route->panner()) {
749 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
750 panners.short_astyle_string(_route->panner()->automation_style()));
751 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
752 panners.short_astate_string(_route->panner()->automation_state()));
756 // panners expect an even number of horiz. pixels
757 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
759 set_size_request (width, -1);
764 processor_box.set_width (w);
766 update_input_display ();
767 update_output_display ();
768 setup_comment_button ();
769 route_group_changed ();
775 MixerStrip::set_packed (bool yn)
780 set_gui_property ("visible", true);
782 set_gui_property ("visible", false);
787 struct RouteCompareByName {
788 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
789 return a->name().compare (b->name()) < 0;
794 MixerStrip::output_release (GdkEventButton *ev)
796 switch (ev->button) {
798 edit_output_configuration ();
806 MixerStrip::output_press (GdkEventButton *ev)
808 using namespace Menu_Helpers;
809 if (!_session->engine().connected()) {
810 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
815 MenuList& citems = output_menu.items();
816 switch (ev->button) {
819 return false; //wait for the mouse-up to pop the dialog
823 output_menu.set_name ("ArdourContextMenu");
825 output_menu_bundles.clear ();
827 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
829 citems.push_back (SeparatorElem());
830 uint32_t const n_with_separator = citems.size ();
832 ARDOUR::BundleList current = _route->output()->bundles_connected ();
834 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
836 /* give user bundles first chance at being in the menu */
838 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
839 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
840 maybe_add_bundle_to_output_menu (*i, current);
844 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
845 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
846 maybe_add_bundle_to_output_menu (*i, current);
850 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
851 RouteList copy = *routes;
852 copy.sort (RouteCompareByName ());
853 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
854 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
857 if (citems.size() == n_with_separator) {
858 /* no routes added; remove the separator */
862 citems.push_back (SeparatorElem());
864 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
867 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
868 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
873 citems.push_back (SeparatorElem());
874 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
876 output_menu.popup (1, ev->time);
887 MixerStrip::input_release (GdkEventButton *ev)
889 switch (ev->button) {
892 edit_input_configuration ();
904 MixerStrip::input_press (GdkEventButton *ev)
906 using namespace Menu_Helpers;
908 MenuList& citems = input_menu.items();
909 input_menu.set_name ("ArdourContextMenu");
912 if (!_session->engine().connected()) {
913 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
918 if (_session->actively_recording() && _route->record_enabled())
921 switch (ev->button) {
924 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
928 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
930 citems.push_back (SeparatorElem());
931 uint32_t const n_with_separator = citems.size ();
933 input_menu_bundles.clear ();
935 ARDOUR::BundleList current = _route->input()->bundles_connected ();
937 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
939 /* give user bundles first chance at being in the menu */
941 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
942 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
943 maybe_add_bundle_to_input_menu (*i, current);
947 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
948 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
949 maybe_add_bundle_to_input_menu (*i, current);
953 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
954 RouteList copy = *routes;
955 copy.sort (RouteCompareByName ());
956 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
957 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
960 if (citems.size() == n_with_separator) {
961 /* no routes added; remove the separator */
965 citems.push_back (SeparatorElem());
966 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
969 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
970 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
975 citems.push_back (SeparatorElem());
976 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
978 input_menu.popup (1, ev->time);
989 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
995 ARDOUR::BundleList current = _route->input()->bundles_connected ();
997 if (std::find (current.begin(), current.end(), c) == current.end()) {
998 _route->input()->connect_ports_to_bundle (c, true, this);
1000 _route->input()->disconnect_ports_from_bundle (c, this);
1005 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1007 if (ignore_toggle) {
1011 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1013 if (std::find (current.begin(), current.end(), c) == current.end()) {
1014 _route->output()->connect_ports_to_bundle (c, true, this);
1016 _route->output()->disconnect_ports_from_bundle (c, this);
1021 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1023 using namespace Menu_Helpers;
1025 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1029 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1030 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1034 if (i != input_menu_bundles.end()) {
1038 input_menu_bundles.push_back (b);
1040 MenuList& citems = input_menu.items();
1042 std::string n = b->name ();
1043 replace_all (n, "_", " ");
1045 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1049 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1051 using namespace Menu_Helpers;
1053 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1057 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1058 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1062 if (i != output_menu_bundles.end()) {
1066 output_menu_bundles.push_back (b);
1068 MenuList& citems = output_menu.items();
1070 std::string n = b->name ();
1071 replace_all (n, "_", " ");
1073 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1077 MixerStrip::update_diskstream_display ()
1079 if (is_track() && input_selector) {
1080 input_selector->hide_all ();
1083 route_color_changed ();
1087 MixerStrip::connect_to_pan ()
1089 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1091 panstate_connection.disconnect ();
1092 panstyle_connection.disconnect ();
1094 if (!_route->panner()) {
1098 boost::shared_ptr<Pannable> p = _route->pannable ();
1100 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1101 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1103 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1104 * However, that only works a panner was previously set.
1106 * PannerUI must remain subscribed to _panshell->Changed() in case
1107 * we switch the panner eg. AUX-Send and back
1108 * _route->panner_shell()->Changed() vs _panshell->Changed
1110 if (panners._panner == 0) {
1111 panners.panshell_changed ();
1113 update_panner_choices();
1117 MixerStrip::update_panner_choices ()
1119 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1120 if (!_route->panner_shell()) { return; }
1122 uint32_t in = _route->output()->n_ports().n_audio();
1124 if (_route->panner()) {
1125 in = _route->panner()->in().n_audio();
1128 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1132 * Output port labelling
1133 * =====================
1135 * Case 1: Each output has one connection, all connections are to system:playback_%i
1136 * out 1 -> system:playback_1
1137 * out 2 -> system:playback_2
1138 * out 3 -> system:playback_3
1141 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1142 * out 1 -> ardour:track_x/in 1
1143 * out 2 -> ardour:track_x/in 2
1144 * Display as: track_x
1146 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1147 * out 1 -> program x:foo
1148 * out 2 -> program x:foo
1149 * Display as: program x
1151 * Case 4: No connections (Disconnected)
1154 * Default case (unusual routing):
1155 * Display as: *number of connections*
1159 * .-----------------------------------------------.
1161 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1162 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1163 * '-----------------------------------------------'
1164 * .-----------------------------------------------.
1167 * '-----------------------------------------------'
1171 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1175 boost::shared_ptr<Port> port;
1176 vector<string> port_connections;
1178 uint32_t total_connection_count = 0;
1179 uint32_t io_connection_count = 0;
1180 uint32_t ardour_connection_count = 0;
1181 uint32_t system_connection_count = 0;
1182 uint32_t other_connection_count = 0;
1183 uint32_t typed_connection_count = 0;
1185 ostringstream label;
1187 bool have_label = false;
1188 bool each_io_has_one_connection = true;
1190 string connection_name;
1191 string ardour_track_name;
1192 string other_connection_type;
1193 string system_ports;
1196 ostringstream tooltip;
1197 char * tooltip_cstr;
1199 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1200 DataType dt = DataType::AUDIO;
1201 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1202 dt = DataType::MIDI;
1203 // avoid further confusion with Midi-tracks that have a synth.
1204 // Audio-ports may be connected, but button says "Disconnected"
1205 tooltip << _("MIDI ");
1209 io_count = route->n_inputs().n_total();
1210 tooltip << string_compose (_("<b>INPUT</b> to %1"), Glib::Markup::escape_text(route->name()).c_str());
1212 io_count = route->n_outputs().n_total();
1213 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(route->name()).c_str());
1217 for (io_index = 0; io_index < io_count; ++io_index) {
1219 port = route->input()->nth (io_index);
1221 port = route->output()->nth (io_index);
1224 port_connections.clear ();
1225 port->get_connections(port_connections);
1227 //ignore any port connections that don't match our DataType
1228 if (port->type() != dt) {
1229 if (!port_connections.empty()) {
1230 ++typed_connection_count;
1235 io_connection_count = 0;
1237 if (!port_connections.empty()) {
1238 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1240 string& connection_name (*i);
1242 if (connection_name.find("system:") == 0) {
1243 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1246 if (io_connection_count == 0) {
1247 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1)).c_str()
1249 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn ).c_str();
1252 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn ).c_str();
1255 if (connection_name.find("ardour:") == 0) {
1256 if (ardour_track_name.empty()) {
1257 // "ardour:Master/in 1" -> "ardour:Master/"
1258 string::size_type slash = connection_name.find("/");
1259 if (slash != string::npos) {
1260 ardour_track_name = connection_name.substr(0, slash + 1);
1264 if (connection_name.find(ardour_track_name) == 0) {
1265 ++ardour_connection_count;
1267 } else if (!pn.empty()) {
1268 if (system_ports.empty()) {
1271 system_ports += "/" + pn;
1273 if (connection_name.find("system:") == 0) {
1274 ++system_connection_count;
1276 } else if (connection_name.find("system:midi_") == 0) {
1278 // "system:midi_capture_123" -> "123"
1279 system_port = "M " + connection_name.substr(20);
1281 // "system:midi_playback_123" -> "123"
1282 system_port = "M " + connection_name.substr(21);
1285 if (system_ports.empty()) {
1286 system_ports += system_port;
1288 system_ports += "/" + system_port;
1291 ++system_connection_count;
1293 } else if (connection_name.find("system:") == 0) {
1295 // "system:capture_123" -> "123"
1296 system_port = connection_name.substr(15);
1298 // "system:playback_123" -> "123"
1299 system_port = connection_name.substr(16);
1302 if (system_ports.empty()) {
1303 system_ports += system_port;
1305 system_ports += "/" + system_port;
1308 ++system_connection_count;
1310 if (other_connection_type.empty()) {
1311 // "jamin:in 1" -> "jamin:"
1312 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1315 if (connection_name.find(other_connection_type) == 0) {
1316 ++other_connection_count;
1320 ++total_connection_count;
1321 ++io_connection_count;
1325 if (io_connection_count != 1) {
1326 each_io_has_one_connection = false;
1330 if (total_connection_count == 0) {
1331 tooltip << endl << _("Disconnected");
1334 tooltip_cstr = new char[tooltip.str().size() + 1];
1335 strcpy(tooltip_cstr, tooltip.str().c_str());
1338 set_tooltip (&input_button, tooltip_cstr);
1340 set_tooltip (&output_button, tooltip_cstr);
1343 if (each_io_has_one_connection) {
1344 if (total_connection_count == ardour_connection_count) {
1345 // all connections are to the same track in ardour
1346 // "ardour:Master/" -> "Master"
1347 string::size_type slash = ardour_track_name.find("/");
1348 if (slash != string::npos) {
1349 label << ardour_track_name.substr(7, slash - 7);
1353 else if (total_connection_count == system_connection_count) {
1354 // all connections are to system ports
1355 label << system_ports;
1358 else if (total_connection_count == other_connection_count) {
1359 // all connections are to the same external program eg jamin
1360 // "jamin:" -> "jamin"
1361 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1367 if (total_connection_count == 0) {
1371 // Odd configuration
1372 label << "*" << total_connection_count << "*";
1374 if (typed_connection_count > 0) {
1375 label << "\u2295"; // circled plus
1380 input_button.set_text (label.str());
1382 output_button.set_text (label.str());
1387 MixerStrip::update_input_display ()
1389 update_io_button (_route, _width, true);
1390 panners.setup_pan ();
1392 if (has_audio_outputs ()) {
1393 panners.show_all ();
1395 panners.hide_all ();
1401 MixerStrip::update_output_display ()
1403 update_io_button (_route, _width, false);
1404 gpm.setup_meters ();
1405 panners.setup_pan ();
1407 if (has_audio_outputs ()) {
1408 panners.show_all ();
1410 panners.hide_all ();
1415 MixerStrip::fast_update ()
1417 gpm.update_meters ();
1421 MixerStrip::diskstream_changed ()
1423 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1427 MixerStrip::io_changed_proxy ()
1429 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1433 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1435 boost::shared_ptr<Port> a = wa.lock ();
1436 boost::shared_ptr<Port> b = wb.lock ();
1438 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1439 update_input_display ();
1440 set_width_enum (_width, this);
1443 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1444 update_output_display ();
1445 set_width_enum (_width, this);
1450 MixerStrip::setup_comment_button ()
1455 if (_route->comment().empty ()) {
1456 _comment_button.unset_bg (STATE_NORMAL);
1457 _comment_button.set_text (_("Comments"));
1459 _comment_button.modify_bg (STATE_NORMAL, color ());
1460 _comment_button.set_text (_("*Comments*"));
1465 if (_route->comment().empty ()) {
1466 _comment_button.unset_bg (STATE_NORMAL);
1467 _comment_button.set_text (_("Cmt"));
1469 _comment_button.modify_bg (STATE_NORMAL, color ());
1470 _comment_button.set_text (_("*Cmt*"));
1476 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1482 MixerStrip::select_route_group (GdkEventButton *ev)
1484 using namespace Menu_Helpers;
1486 if (ev->button == 1) {
1488 if (group_menu == 0) {
1490 PropertyList* plist = new PropertyList();
1492 plist->add (Properties::gain, true);
1493 plist->add (Properties::mute, true);
1494 plist->add (Properties::solo, true);
1496 group_menu = new RouteGroupMenu (_session, plist);
1500 r.push_back (route ());
1501 group_menu->build (r);
1502 group_menu->menu()->popup (1, ev->time);
1509 MixerStrip::route_group_changed ()
1511 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1513 RouteGroup *rg = _route->route_group();
1516 group_button.set_text (PBD::short_version (rg->name(), 5));
1520 group_button.set_text (_("Grp"));
1523 group_button.set_text (_("~G"));
1530 MixerStrip::route_color_changed ()
1532 name_button.modify_bg (STATE_NORMAL, color());
1533 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1534 reset_strip_style ();
1538 MixerStrip::show_passthru_color ()
1540 reset_strip_style ();
1544 MixerStrip::build_route_ops_menu ()
1546 using namespace Menu_Helpers;
1547 route_ops_menu = new Menu;
1548 route_ops_menu->set_name ("ArdourContextMenu");
1550 MenuList& items = route_ops_menu->items();
1552 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1554 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1556 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1558 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1560 items.push_back (SeparatorElem());
1562 if (!_route->is_master()) {
1563 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1565 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1566 rename_menu_item = &items.back();
1568 items.push_back (SeparatorElem());
1569 items.push_back (CheckMenuElem (_("Active")));
1570 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1571 i->set_active (_route->active());
1572 i->set_sensitive(! _session->transport_rolling());
1573 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1575 items.push_back (SeparatorElem());
1577 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1579 items.push_back (SeparatorElem());
1580 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1581 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1582 denormal_menu_item->set_active (_route->denormal_protection());
1584 if (!Profile->get_sae()) {
1585 items.push_back (SeparatorElem());
1586 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1589 items.push_back (SeparatorElem());
1592 /* note that this relies on selection being shared across editor and
1593 mixer (or global to the backend, in the future), which is the only
1594 sane thing for users anyway.
1597 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1599 Selection& selection (PublicEditor::instance().get_selection());
1600 if (!selection.selected (rtav)) {
1601 selection.set (rtav);
1604 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1610 MixerStrip::name_button_button_press (GdkEventButton* ev)
1612 if (ev->button == 3) {
1613 list_route_operations ();
1615 /* do not allow rename if the track is record-enabled */
1616 rename_menu_item->set_sensitive (!_route->record_enabled());
1617 route_ops_menu->popup (1, ev->time);
1626 MixerStrip::name_button_button_release (GdkEventButton* ev)
1628 if (ev->button == 1) {
1629 list_route_operations ();
1631 /* do not allow rename if the track is record-enabled */
1632 rename_menu_item->set_sensitive (!_route->record_enabled());
1633 route_ops_menu->popup (1, ev->time);
1640 MixerStrip::number_button_button_press (GdkEventButton* ev)
1642 if ( ev->button == 3 ) {
1643 list_route_operations ();
1645 /* do not allow rename if the track is record-enabled */
1646 rename_menu_item->set_sensitive (!_route->record_enabled());
1647 route_ops_menu->popup (1, ev->time);
1656 MixerStrip::list_route_operations ()
1658 delete route_ops_menu;
1659 build_route_ops_menu ();
1663 MixerStrip::set_selected (bool yn)
1665 AxisView::set_selected (yn);
1667 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1668 global_frame.set_name ("MixerStripSelectedFrame");
1670 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1671 global_frame.set_name ("MixerStripFrame");
1673 global_frame.queue_draw ();
1676 // processor_box.deselect_all_processors();
1680 MixerStrip::property_changed (const PropertyChange& what_changed)
1682 RouteUI::property_changed (what_changed);
1684 if (what_changed.contains (ARDOUR::Properties::name)) {
1690 MixerStrip::name_changed ()
1694 name_button.set_text (_route->name());
1697 name_button.set_text (PBD::short_version (_route->name(), 5));
1701 set_tooltip (name_button, _route->name());
1703 if (_session->config.get_track_name_number()) {
1704 const int64_t track_number = _route->track_number ();
1705 if (track_number == 0) {
1706 number_label.set_text ("-");
1708 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1711 number_label.set_text ("");
1716 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1718 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1722 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1724 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1728 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1730 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1734 MixerStrip::width_button_pressed (GdkEventButton* ev)
1736 if (ev->button != 1) {
1740 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1743 _mixer.set_strip_width (Narrow, true);
1747 _mixer.set_strip_width (Wide, true);
1753 set_width_enum (Narrow, this);
1756 set_width_enum (Wide, this);
1765 MixerStrip::hide_clicked ()
1767 // LAME fix to reset the button status for when it is redisplayed (part 1)
1768 hide_button.set_sensitive(false);
1771 Hiding(); /* EMIT_SIGNAL */
1773 _mixer.hide_strip (this);
1777 hide_button.set_sensitive(true);
1781 MixerStrip::set_embedded (bool yn)
1787 MixerStrip::map_frozen ()
1789 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1791 boost::shared_ptr<AudioTrack> at = audio_track();
1794 switch (at->freeze_state()) {
1795 case AudioTrack::Frozen:
1796 processor_box.set_sensitive (false);
1797 hide_redirect_editors ();
1800 processor_box.set_sensitive (true);
1801 // XXX need some way, maybe, to retoggle redirect editors
1808 MixerStrip::hide_redirect_editors ()
1810 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1814 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1816 boost::shared_ptr<Processor> processor (p.lock ());
1821 Gtk::Window* w = processor_box.get_processor_ui (processor);
1829 MixerStrip::reset_strip_style ()
1831 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1833 gpm.set_fader_name ("SendStripBase");
1837 if (is_midi_track()) {
1838 if (_route->active()) {
1839 set_name ("MidiTrackStripBase");
1841 set_name ("MidiTrackStripBaseInactive");
1843 gpm.set_fader_name ("MidiTrackFader");
1844 } else if (is_audio_track()) {
1845 if (_route->active()) {
1846 set_name ("AudioTrackStripBase");
1848 set_name ("AudioTrackStripBaseInactive");
1850 gpm.set_fader_name ("AudioTrackFader");
1852 if (_route->active()) {
1853 set_name ("AudioBusStripBase");
1855 set_name ("AudioBusStripBaseInactive");
1857 gpm.set_fader_name ("AudioBusFader");
1859 /* (no MIDI busses yet) */
1866 MixerStrip::engine_stopped ()
1871 MixerStrip::engine_running ()
1876 MixerStrip::meter_point_string (MeterPoint mp)
1889 case MeterPostFader:
1906 return S_("Meter|In");
1910 return S_("Meter|Pr");
1913 case MeterPostFader:
1914 return S_("Meter|Po");
1918 return S_("Meter|O");
1923 return S_("Meter|C");
1932 /** Called when the metering point has changed */
1934 MixerStrip::meter_changed ()
1936 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1937 gpm.setup_meters ();
1938 // reset peak when meter point changes
1939 gpm.reset_peak_display();
1942 /** The bus that we are displaying sends to has changed, or been turned off.
1943 * @param send_to New bus that we are displaying sends to, or 0.
1946 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1948 RouteUI::bus_send_display_changed (send_to);
1951 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1956 revert_to_default_display ();
1959 revert_to_default_display ();
1964 MixerStrip::drop_send ()
1966 boost::shared_ptr<Send> current_send;
1968 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1969 current_send->set_metering (false);
1972 send_gone_connection.disconnect ();
1973 input_button.set_sensitive (true);
1974 output_button.set_sensitive (true);
1975 group_button.set_sensitive (true);
1976 set_invert_sensitive (true);
1977 meter_point_button.set_sensitive (true);
1978 mute_button->set_sensitive (true);
1979 solo_button->set_sensitive (true);
1980 rec_enable_button->set_sensitive (true);
1981 solo_isolated_led->set_sensitive (true);
1982 solo_safe_led->set_sensitive (true);
1983 monitor_input_button->set_sensitive (true);
1984 monitor_disk_button->set_sensitive (true);
1985 _comment_button.set_sensitive (true);
1989 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1991 _current_delivery = d;
1992 DeliveryChanged (_current_delivery);
1996 MixerStrip::show_send (boost::shared_ptr<Send> send)
2002 set_current_delivery (send);
2004 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2005 send->set_metering (true);
2006 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2008 gain_meter().set_controls (_route, send->meter(), send->amp());
2009 gain_meter().setup_meters ();
2011 uint32_t const in = _current_delivery->pans_required();
2012 uint32_t const out = _current_delivery->pan_outs();
2014 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2015 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2016 panner_ui().setup_pan ();
2017 panner_ui().set_send_drawing_mode (true);
2018 panner_ui().show_all ();
2020 input_button.set_sensitive (false);
2021 group_button.set_sensitive (false);
2022 set_invert_sensitive (false);
2023 meter_point_button.set_sensitive (false);
2024 mute_button->set_sensitive (false);
2025 solo_button->set_sensitive (false);
2026 rec_enable_button->set_sensitive (false);
2027 solo_isolated_led->set_sensitive (false);
2028 solo_safe_led->set_sensitive (false);
2029 monitor_input_button->set_sensitive (false);
2030 monitor_disk_button->set_sensitive (false);
2031 _comment_button.set_sensitive (false);
2033 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2034 output_button.set_sensitive (false);
2037 reset_strip_style ();
2041 MixerStrip::revert_to_default_display ()
2045 set_current_delivery (_route->main_outs ());
2047 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2048 gain_meter().setup_meters ();
2050 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2051 update_panner_choices();
2052 panner_ui().setup_pan ();
2053 panner_ui().set_send_drawing_mode (false);
2055 if (has_audio_outputs ()) {
2056 panners.show_all ();
2058 panners.hide_all ();
2061 reset_strip_style ();
2065 MixerStrip::set_button_names ()
2069 mute_button->set_text (_("Mute"));
2070 monitor_input_button->set_text (_("In"));
2071 monitor_disk_button->set_text (_("Disk"));
2073 if (_route && _route->solo_safe()) {
2074 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2076 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2078 if (!Config->get_solo_control_is_listen_control()) {
2079 solo_button->set_text (_("Solo"));
2081 switch (Config->get_listen_position()) {
2082 case AfterFaderListen:
2083 solo_button->set_text (_("AFL"));
2085 case PreFaderListen:
2086 solo_button->set_text (_("PFL"));
2090 solo_isolated_led->set_text (_("Iso"));
2091 solo_safe_led->set_text (S_("SoloLock|Lock"));
2095 mute_button->set_text (S_("Mute|M"));
2096 monitor_input_button->set_text (S_("MonitorInput|I"));
2097 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2099 if (_route && _route->solo_safe()) {
2100 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2102 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2104 if (!Config->get_solo_control_is_listen_control()) {
2105 solo_button->set_text (S_("Solo|S"));
2107 switch (Config->get_listen_position()) {
2108 case AfterFaderListen:
2109 solo_button->set_text (S_("AfterFader|A"));
2111 case PreFaderListen:
2112 solo_button->set_text (S_("Prefader|P"));
2117 solo_isolated_led->set_text (S_("SoloIso|I"));
2118 solo_safe_led->set_text (S_("SoloLock|L"));
2123 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2125 meter_point_button.set_text ("");
2130 MixerStrip::plugin_selector()
2132 return _mixer.plugin_selector();
2136 MixerStrip::hide_things ()
2138 processor_box.hide_things ();
2142 MixerStrip::input_active_button_press (GdkEventButton*)
2144 /* nothing happens on press */
2149 MixerStrip::input_active_button_release (GdkEventButton* ev)
2151 boost::shared_ptr<MidiTrack> mt = midi_track ();
2157 boost::shared_ptr<RouteList> rl (new RouteList);
2159 rl->push_back (route());
2161 _session->set_exclusive_input_active (rl, !mt->input_active(),
2162 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2168 MixerStrip::midi_input_status_changed ()
2170 if (midi_input_enable_button) {
2171 boost::shared_ptr<MidiTrack> mt = midi_track ();
2173 midi_input_enable_button->set_active (mt->input_active ());
2178 MixerStrip::state_id () const
2180 return string_compose ("strip %1", _route->id().to_s());
2184 MixerStrip::parameter_changed (string p)
2186 if (p == _visibility.get_state_name()) {
2187 /* The user has made changes to the mixer strip visibility, so get
2188 our VisibilityGroup to reflect these changes in our widgets.
2190 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2192 else if (p == "track-name-number") {
2197 /** Called to decide whether the solo isolate / solo lock button visibility should
2198 * be overridden from that configured by the user. We do this for the master bus.
2200 * @return optional value that is present if visibility state should be overridden.
2202 boost::optional<bool>
2203 MixerStrip::override_solo_visibility () const
2205 if (_route && _route->is_master ()) {
2206 return boost::optional<bool> (false);
2209 return boost::optional<bool> ();
2213 MixerStrip::add_input_port (DataType t)
2215 _route->input()->add_port ("", this, t);
2219 MixerStrip::add_output_port (DataType t)
2221 _route->output()->add_port ("", this, t);
2225 MixerStrip::route_active_changed ()
2227 reset_strip_style ();
2231 MixerStrip::copy_processors ()
2233 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2237 MixerStrip::cut_processors ()
2239 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2243 MixerStrip::paste_processors ()
2245 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2249 MixerStrip::select_all_processors ()
2251 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2255 MixerStrip::deselect_all_processors ()
2257 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2261 MixerStrip::delete_processors ()
2263 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2267 MixerStrip::toggle_processors ()
2269 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2273 MixerStrip::ab_plugins ()
2275 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2279 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2281 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2284 if (ev->button == 3) {
2285 popup_level_meter_menu (ev);
2293 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2295 using namespace Gtk::Menu_Helpers;
2297 Gtk::Menu* m = manage (new Menu);
2298 MenuList& items = m->items ();
2300 RadioMenuItem::Group group;
2302 _suspend_menu_callbacks = true;
2303 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2304 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2305 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2306 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2307 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2309 if (gpm.meter_channels().n_audio() == 0) {
2310 m->popup (ev->button, ev->time);
2311 _suspend_menu_callbacks = false;
2315 RadioMenuItem::Group tgroup;
2316 items.push_back (SeparatorElem());
2318 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2319 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2320 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2321 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2322 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2323 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2324 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2325 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2326 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2327 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2328 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2331 if (_route->is_master()) {
2334 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2335 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2336 /* non-master bus */
2339 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2346 MeterType cmt = _route->meter_type();
2347 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2349 items.push_back (SeparatorElem());
2350 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2351 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2352 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2353 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2354 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2355 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2357 m->popup (ev->button, ev->time);
2358 _suspend_menu_callbacks = false;
2362 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2363 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2365 using namespace Menu_Helpers;
2367 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2368 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2369 i->set_active (_route->meter_point() == point);
2373 MixerStrip::set_meter_point (MeterPoint p)
2375 if (_suspend_menu_callbacks) return;
2376 _route->set_meter_point (p);
2380 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2381 RadioMenuItem::Group& group, string const & name, MeterType type)
2383 using namespace Menu_Helpers;
2385 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2386 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2387 i->set_active (_route->meter_type() == type);
2391 MixerStrip::set_meter_type (MeterType t)
2393 if (_suspend_menu_callbacks) return;