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"), Gtkmm2ext::markup_escape_text (route->name()));
1212 io_count = route->n_outputs().n_total();
1213 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
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 << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1249 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1252 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
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)));
1590 /* note that this relies on selection being shared across editor and
1591 mixer (or global to the backend, in the future), which is the only
1592 sane thing for users anyway.
1595 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1597 Selection& selection (PublicEditor::instance().get_selection());
1598 if (!selection.selected (rtav)) {
1599 selection.set (rtav);
1602 items.push_front (SeparatorElem());
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 set_tooltip (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 (UIConfiguration::instance().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;