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");
678 MixerStrip::set_stuff_from_route ()
680 /* if width is not set, it will be set by the MixerUI or editor */
682 string str = gui_property ("strip-width");
684 set_width_enum (Width (string_2_enum (str, _width)), this);
689 MixerStrip::set_width_enum (Width w, void* owner)
691 /* always set the gpm width again, things may be hidden */
694 panners.set_width (w);
696 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
698 _width_owner = owner;
702 if (_width_owner == this) {
703 set_gui_property ("strip-width", enum_2_string (_width));
708 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
713 if (show_sends_button) {
714 show_sends_button->set_text (_("Aux"));
717 gpm.gain_automation_style_button.set_text (
718 gpm.astyle_string(gain_automation->automation_style()));
719 gpm.gain_automation_state_button.set_text (
720 gpm.astate_string(gain_automation->automation_state()));
722 if (_route->panner()) {
723 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
724 panners.astyle_string(_route->panner()->automation_style()));
725 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
726 panners.astate_string(_route->panner()->automation_state()));
730 // panners expect an even number of horiz. pixels
731 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
733 set_size_request (width, -1);
739 if (show_sends_button) {
740 show_sends_button->set_text (_("Snd"));
743 gpm.gain_automation_style_button.set_text (
744 gpm.short_astyle_string(gain_automation->automation_style()));
745 gpm.gain_automation_state_button.set_text (
746 gpm.short_astate_string(gain_automation->automation_state()));
747 gain_meter().setup_meters (); // recalc meter width
749 if (_route->panner()) {
750 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
751 panners.short_astyle_string(_route->panner()->automation_style()));
752 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
753 panners.short_astate_string(_route->panner()->automation_state()));
757 // panners expect an even number of horiz. pixels
758 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
760 set_size_request (width, -1);
765 processor_box.set_width (w);
767 update_input_display ();
768 update_output_display ();
769 setup_comment_button ();
770 route_group_changed ();
776 MixerStrip::set_packed (bool yn)
781 set_gui_property ("visible", true);
783 set_gui_property ("visible", false);
788 struct RouteCompareByName {
789 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
790 return a->name().compare (b->name()) < 0;
795 MixerStrip::output_release (GdkEventButton *ev)
797 switch (ev->button) {
799 edit_output_configuration ();
807 MixerStrip::output_press (GdkEventButton *ev)
809 using namespace Menu_Helpers;
810 if (!_session->engine().connected()) {
811 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
816 MenuList& citems = output_menu.items();
817 switch (ev->button) {
820 return false; //wait for the mouse-up to pop the dialog
824 output_menu.set_name ("ArdourContextMenu");
826 output_menu_bundles.clear ();
828 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
830 citems.push_back (SeparatorElem());
831 uint32_t const n_with_separator = citems.size ();
833 ARDOUR::BundleList current = _route->output()->bundles_connected ();
835 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
837 /* give user bundles first chance at being in the menu */
839 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
840 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
841 maybe_add_bundle_to_output_menu (*i, current);
845 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
846 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
847 maybe_add_bundle_to_output_menu (*i, current);
851 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
852 RouteList copy = *routes;
853 copy.sort (RouteCompareByName ());
854 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
855 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
858 if (citems.size() == n_with_separator) {
859 /* no routes added; remove the separator */
863 citems.push_back (SeparatorElem());
865 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
868 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
869 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
874 citems.push_back (SeparatorElem());
875 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
877 output_menu.popup (1, ev->time);
888 MixerStrip::input_release (GdkEventButton *ev)
890 switch (ev->button) {
893 edit_input_configuration ();
905 MixerStrip::input_press (GdkEventButton *ev)
907 using namespace Menu_Helpers;
909 MenuList& citems = input_menu.items();
910 input_menu.set_name ("ArdourContextMenu");
913 if (!_session->engine().connected()) {
914 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
919 if (_session->actively_recording() && _route->record_enabled())
922 switch (ev->button) {
925 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
929 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
931 citems.push_back (SeparatorElem());
932 uint32_t const n_with_separator = citems.size ();
934 input_menu_bundles.clear ();
936 ARDOUR::BundleList current = _route->input()->bundles_connected ();
938 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
940 /* give user bundles first chance at being in the menu */
942 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
943 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
944 maybe_add_bundle_to_input_menu (*i, current);
948 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
949 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
950 maybe_add_bundle_to_input_menu (*i, current);
954 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
955 RouteList copy = *routes;
956 copy.sort (RouteCompareByName ());
957 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
958 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
961 if (citems.size() == n_with_separator) {
962 /* no routes added; remove the separator */
966 citems.push_back (SeparatorElem());
967 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
970 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
971 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
976 citems.push_back (SeparatorElem());
977 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
979 input_menu.popup (1, ev->time);
990 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
996 ARDOUR::BundleList current = _route->input()->bundles_connected ();
998 if (std::find (current.begin(), current.end(), c) == current.end()) {
999 _route->input()->connect_ports_to_bundle (c, true, this);
1001 _route->input()->disconnect_ports_from_bundle (c, this);
1006 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1008 if (ignore_toggle) {
1012 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1014 if (std::find (current.begin(), current.end(), c) == current.end()) {
1015 _route->output()->connect_ports_to_bundle (c, true, this);
1017 _route->output()->disconnect_ports_from_bundle (c, this);
1022 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1024 using namespace Menu_Helpers;
1026 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1030 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1031 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1035 if (i != input_menu_bundles.end()) {
1039 input_menu_bundles.push_back (b);
1041 MenuList& citems = input_menu.items();
1043 std::string n = b->name ();
1044 replace_all (n, "_", " ");
1046 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1050 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1052 using namespace Menu_Helpers;
1054 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1058 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1059 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1063 if (i != output_menu_bundles.end()) {
1067 output_menu_bundles.push_back (b);
1069 MenuList& citems = output_menu.items();
1071 std::string n = b->name ();
1072 replace_all (n, "_", " ");
1074 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1078 MixerStrip::update_diskstream_display ()
1080 if (is_track() && input_selector) {
1081 input_selector->hide_all ();
1084 route_color_changed ();
1088 MixerStrip::connect_to_pan ()
1090 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1092 panstate_connection.disconnect ();
1093 panstyle_connection.disconnect ();
1095 if (!_route->panner()) {
1099 boost::shared_ptr<Pannable> p = _route->pannable ();
1101 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1102 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1104 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1105 * However, that only works a panner was previously set.
1107 * PannerUI must remain subscribed to _panshell->Changed() in case
1108 * we switch the panner eg. AUX-Send and back
1109 * _route->panner_shell()->Changed() vs _panshell->Changed
1111 if (panners._panner == 0) {
1112 panners.panshell_changed ();
1114 update_panner_choices();
1118 MixerStrip::update_panner_choices ()
1120 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1121 if (!_route->panner_shell()) { return; }
1123 uint32_t in = _route->output()->n_ports().n_audio();
1125 if (_route->panner()) {
1126 in = _route->panner()->in().n_audio();
1129 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1133 * Output port labelling
1134 * =====================
1136 * Case 1: Each output has one connection, all connections are to system:playback_%i
1137 * out 1 -> system:playback_1
1138 * out 2 -> system:playback_2
1139 * out 3 -> system:playback_3
1142 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1143 * out 1 -> ardour:track_x/in 1
1144 * out 2 -> ardour:track_x/in 2
1145 * Display as: track_x
1147 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1148 * out 1 -> program x:foo
1149 * out 2 -> program x:foo
1150 * Display as: program x
1152 * Case 4: No connections (Disconnected)
1155 * Default case (unusual routing):
1156 * Display as: *number of connections*
1160 * .-----------------------------------------------.
1162 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1163 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1164 * '-----------------------------------------------'
1165 * .-----------------------------------------------.
1168 * '-----------------------------------------------'
1172 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1176 boost::shared_ptr<Port> port;
1177 vector<string> port_connections;
1179 uint32_t total_connection_count = 0;
1180 uint32_t io_connection_count = 0;
1181 uint32_t ardour_connection_count = 0;
1182 uint32_t system_connection_count = 0;
1183 uint32_t other_connection_count = 0;
1184 uint32_t typed_connection_count = 0;
1186 ostringstream label;
1188 bool have_label = false;
1189 bool each_io_has_one_connection = true;
1191 string connection_name;
1192 string ardour_track_name;
1193 string other_connection_type;
1194 string system_ports;
1197 ostringstream tooltip;
1198 char * tooltip_cstr;
1200 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1201 DataType dt = DataType::AUDIO;
1202 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1203 dt = DataType::MIDI;
1204 // avoid further confusion with Midi-tracks that have a synth.
1205 // Audio-ports may be connected, but button says "Disconnected"
1206 tooltip << _("MIDI ");
1210 io_count = route->n_inputs().n_total();
1211 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1213 io_count = route->n_outputs().n_total();
1214 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1218 for (io_index = 0; io_index < io_count; ++io_index) {
1220 port = route->input()->nth (io_index);
1222 port = route->output()->nth (io_index);
1225 port_connections.clear ();
1226 port->get_connections(port_connections);
1228 //ignore any port connections that don't match our DataType
1229 if (port->type() != dt) {
1230 if (!port_connections.empty()) {
1231 ++typed_connection_count;
1236 io_connection_count = 0;
1238 if (!port_connections.empty()) {
1239 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1241 string& connection_name (*i);
1243 if (connection_name.find("system:") == 0) {
1244 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1247 if (io_connection_count == 0) {
1248 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1250 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1253 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1256 if (connection_name.find("ardour:") == 0) {
1257 if (ardour_track_name.empty()) {
1258 // "ardour:Master/in 1" -> "ardour:Master/"
1259 string::size_type slash = connection_name.find("/");
1260 if (slash != string::npos) {
1261 ardour_track_name = connection_name.substr(0, slash + 1);
1265 if (connection_name.find(ardour_track_name) == 0) {
1266 ++ardour_connection_count;
1268 } else if (!pn.empty()) {
1269 if (system_ports.empty()) {
1272 system_ports += "/" + pn;
1274 if (connection_name.find("system:") == 0) {
1275 ++system_connection_count;
1277 } else if (connection_name.find("system:midi_") == 0) {
1279 // "system:midi_capture_123" -> "123"
1280 system_port = "M " + connection_name.substr(20);
1282 // "system:midi_playback_123" -> "123"
1283 system_port = "M " + connection_name.substr(21);
1286 if (system_ports.empty()) {
1287 system_ports += system_port;
1289 system_ports += "/" + system_port;
1292 ++system_connection_count;
1294 } else if (connection_name.find("system:") == 0) {
1296 // "system:capture_123" -> "123"
1297 system_port = connection_name.substr(15);
1299 // "system:playback_123" -> "123"
1300 system_port = connection_name.substr(16);
1303 if (system_ports.empty()) {
1304 system_ports += system_port;
1306 system_ports += "/" + system_port;
1309 ++system_connection_count;
1311 if (other_connection_type.empty()) {
1312 // "jamin:in 1" -> "jamin:"
1313 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1316 if (connection_name.find(other_connection_type) == 0) {
1317 ++other_connection_count;
1321 ++total_connection_count;
1322 ++io_connection_count;
1326 if (io_connection_count != 1) {
1327 each_io_has_one_connection = false;
1331 if (total_connection_count == 0) {
1332 tooltip << endl << _("Disconnected");
1335 tooltip_cstr = new char[tooltip.str().size() + 1];
1336 strcpy(tooltip_cstr, tooltip.str().c_str());
1339 set_tooltip (&input_button, tooltip_cstr);
1341 set_tooltip (&output_button, tooltip_cstr);
1344 if (each_io_has_one_connection) {
1345 if (total_connection_count == ardour_connection_count) {
1346 // all connections are to the same track in ardour
1347 // "ardour:Master/" -> "Master"
1348 string::size_type slash = ardour_track_name.find("/");
1349 if (slash != string::npos) {
1350 label << ardour_track_name.substr(7, slash - 7);
1354 else if (total_connection_count == system_connection_count) {
1355 // all connections are to system ports
1356 label << system_ports;
1359 else if (total_connection_count == other_connection_count) {
1360 // all connections are to the same external program eg jamin
1361 // "jamin:" -> "jamin"
1362 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1368 if (total_connection_count == 0) {
1372 // Odd configuration
1373 label << "*" << total_connection_count << "*";
1375 if (typed_connection_count > 0) {
1376 label << "\u2295"; // circled plus
1381 input_button.set_text (label.str());
1383 output_button.set_text (label.str());
1388 MixerStrip::update_input_display ()
1390 update_io_button (_route, _width, true);
1391 panners.setup_pan ();
1393 if (has_audio_outputs ()) {
1394 panners.show_all ();
1396 panners.hide_all ();
1402 MixerStrip::update_output_display ()
1404 update_io_button (_route, _width, false);
1405 gpm.setup_meters ();
1406 panners.setup_pan ();
1408 if (has_audio_outputs ()) {
1409 panners.show_all ();
1411 panners.hide_all ();
1416 MixerStrip::fast_update ()
1418 gpm.update_meters ();
1422 MixerStrip::diskstream_changed ()
1424 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1428 MixerStrip::io_changed_proxy ()
1430 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1434 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1436 boost::shared_ptr<Port> a = wa.lock ();
1437 boost::shared_ptr<Port> b = wb.lock ();
1439 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1440 update_input_display ();
1441 set_width_enum (_width, this);
1444 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1445 update_output_display ();
1446 set_width_enum (_width, this);
1451 MixerStrip::setup_comment_button ()
1456 if (_route->comment().empty ()) {
1457 _comment_button.unset_bg (STATE_NORMAL);
1458 _comment_button.set_text (_("Comments"));
1460 _comment_button.modify_bg (STATE_NORMAL, color ());
1461 _comment_button.set_text (_("*Comments*"));
1466 if (_route->comment().empty ()) {
1467 _comment_button.unset_bg (STATE_NORMAL);
1468 _comment_button.set_text (_("Cmt"));
1470 _comment_button.modify_bg (STATE_NORMAL, color ());
1471 _comment_button.set_text (_("*Cmt*"));
1477 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1483 MixerStrip::select_route_group (GdkEventButton *ev)
1485 using namespace Menu_Helpers;
1487 if (ev->button == 1) {
1489 if (group_menu == 0) {
1491 PropertyList* plist = new PropertyList();
1493 plist->add (Properties::gain, true);
1494 plist->add (Properties::mute, true);
1495 plist->add (Properties::solo, true);
1497 group_menu = new RouteGroupMenu (_session, plist);
1501 r.push_back (route ());
1502 group_menu->build (r);
1503 group_menu->menu()->popup (1, ev->time);
1510 MixerStrip::route_group_changed ()
1512 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1514 RouteGroup *rg = _route->route_group();
1517 group_button.set_text (PBD::short_version (rg->name(), 5));
1521 group_button.set_text (_("Grp"));
1524 group_button.set_text (_("~G"));
1531 MixerStrip::route_color_changed ()
1533 name_button.modify_bg (STATE_NORMAL, color());
1534 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1535 reset_strip_style ();
1539 MixerStrip::show_passthru_color ()
1541 reset_strip_style ();
1545 MixerStrip::build_route_ops_menu ()
1547 using namespace Menu_Helpers;
1548 route_ops_menu = new Menu;
1549 route_ops_menu->set_name ("ArdourContextMenu");
1551 MenuList& items = route_ops_menu->items();
1553 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1555 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1557 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1559 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1561 items.push_back (SeparatorElem());
1563 if (!_route->is_master()) {
1564 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1566 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1567 rename_menu_item = &items.back();
1569 items.push_back (SeparatorElem());
1570 items.push_back (CheckMenuElem (_("Active")));
1571 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1572 i->set_active (_route->active());
1573 i->set_sensitive(! _session->transport_rolling());
1574 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1576 items.push_back (SeparatorElem());
1578 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1580 items.push_back (SeparatorElem());
1581 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1582 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1583 denormal_menu_item->set_active (_route->denormal_protection());
1585 if (!Profile->get_sae()) {
1586 items.push_back (SeparatorElem());
1587 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1591 /* note that this relies on selection being shared across editor and
1592 mixer (or global to the backend, in the future), which is the only
1593 sane thing for users anyway.
1596 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1598 Selection& selection (PublicEditor::instance().get_selection());
1599 if (!selection.selected (rtav)) {
1600 selection.set (rtav);
1603 if (!_route->is_master()) {
1604 items.push_back (SeparatorElem());
1605 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1608 items.push_back (SeparatorElem());
1609 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1615 MixerStrip::name_button_button_press (GdkEventButton* ev)
1617 if (ev->button == 3) {
1618 list_route_operations ();
1620 /* do not allow rename if the track is record-enabled */
1621 rename_menu_item->set_sensitive (!_route->record_enabled());
1622 route_ops_menu->popup (1, ev->time);
1631 MixerStrip::name_button_button_release (GdkEventButton* ev)
1633 if (ev->button == 1) {
1634 list_route_operations ();
1636 /* do not allow rename if the track is record-enabled */
1637 rename_menu_item->set_sensitive (!_route->record_enabled());
1638 route_ops_menu->popup (1, ev->time);
1645 MixerStrip::number_button_button_press (GdkEventButton* ev)
1647 if ( ev->button == 3 ) {
1648 list_route_operations ();
1650 /* do not allow rename if the track is record-enabled */
1651 rename_menu_item->set_sensitive (!_route->record_enabled());
1652 route_ops_menu->popup (1, ev->time);
1661 MixerStrip::list_route_operations ()
1663 delete route_ops_menu;
1664 build_route_ops_menu ();
1668 MixerStrip::set_selected (bool yn)
1670 AxisView::set_selected (yn);
1672 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1673 global_frame.set_name ("MixerStripSelectedFrame");
1675 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1676 global_frame.set_name ("MixerStripFrame");
1678 global_frame.queue_draw ();
1681 // processor_box.deselect_all_processors();
1685 MixerStrip::property_changed (const PropertyChange& what_changed)
1687 RouteUI::property_changed (what_changed);
1689 if (what_changed.contains (ARDOUR::Properties::name)) {
1695 MixerStrip::name_changed ()
1699 name_button.set_text (_route->name());
1702 name_button.set_text (PBD::short_version (_route->name(), 5));
1706 set_tooltip (name_button, _route->name());
1708 if (_session->config.get_track_name_number()) {
1709 const int64_t track_number = _route->track_number ();
1710 if (track_number == 0) {
1711 number_label.set_text ("-");
1713 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1716 number_label.set_text ("");
1721 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1723 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1727 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1729 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1733 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1735 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1739 MixerStrip::width_button_pressed (GdkEventButton* ev)
1741 if (ev->button != 1) {
1745 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1748 _mixer.set_strip_width (Narrow, true);
1752 _mixer.set_strip_width (Wide, true);
1758 set_width_enum (Narrow, this);
1761 set_width_enum (Wide, this);
1770 MixerStrip::hide_clicked ()
1772 // LAME fix to reset the button status for when it is redisplayed (part 1)
1773 hide_button.set_sensitive(false);
1776 Hiding(); /* EMIT_SIGNAL */
1778 _mixer.hide_strip (this);
1782 hide_button.set_sensitive(true);
1786 MixerStrip::set_embedded (bool yn)
1792 MixerStrip::map_frozen ()
1794 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1796 boost::shared_ptr<AudioTrack> at = audio_track();
1799 switch (at->freeze_state()) {
1800 case AudioTrack::Frozen:
1801 processor_box.set_sensitive (false);
1802 hide_redirect_editors ();
1805 processor_box.set_sensitive (true);
1806 // XXX need some way, maybe, to retoggle redirect editors
1810 processor_box.set_sensitive (true);
1815 MixerStrip::hide_redirect_editors ()
1817 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1821 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1823 boost::shared_ptr<Processor> processor (p.lock ());
1828 Gtk::Window* w = processor_box.get_processor_ui (processor);
1836 MixerStrip::reset_strip_style ()
1838 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1840 gpm.set_fader_name ("SendStripBase");
1844 if (is_midi_track()) {
1845 if (_route->active()) {
1846 set_name ("MidiTrackStripBase");
1848 set_name ("MidiTrackStripBaseInactive");
1850 gpm.set_fader_name ("MidiTrackFader");
1851 } else if (is_audio_track()) {
1852 if (_route->active()) {
1853 set_name ("AudioTrackStripBase");
1855 set_name ("AudioTrackStripBaseInactive");
1857 gpm.set_fader_name ("AudioTrackFader");
1859 if (_route->active()) {
1860 set_name ("AudioBusStripBase");
1862 set_name ("AudioBusStripBaseInactive");
1864 gpm.set_fader_name ("AudioBusFader");
1866 /* (no MIDI busses yet) */
1873 MixerStrip::engine_stopped ()
1878 MixerStrip::engine_running ()
1883 MixerStrip::meter_point_string (MeterPoint mp)
1896 case MeterPostFader:
1913 return S_("Meter|In");
1917 return S_("Meter|Pr");
1920 case MeterPostFader:
1921 return S_("Meter|Po");
1925 return S_("Meter|O");
1930 return S_("Meter|C");
1939 /** Called when the metering point has changed */
1941 MixerStrip::meter_changed ()
1943 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1944 gpm.setup_meters ();
1945 // reset peak when meter point changes
1946 gpm.reset_peak_display();
1949 /** The bus that we are displaying sends to has changed, or been turned off.
1950 * @param send_to New bus that we are displaying sends to, or 0.
1953 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1955 RouteUI::bus_send_display_changed (send_to);
1958 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1963 revert_to_default_display ();
1966 revert_to_default_display ();
1971 MixerStrip::drop_send ()
1973 boost::shared_ptr<Send> current_send;
1975 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1976 current_send->set_metering (false);
1979 send_gone_connection.disconnect ();
1980 input_button.set_sensitive (true);
1981 output_button.set_sensitive (true);
1982 group_button.set_sensitive (true);
1983 set_invert_sensitive (true);
1984 meter_point_button.set_sensitive (true);
1985 mute_button->set_sensitive (true);
1986 solo_button->set_sensitive (true);
1987 rec_enable_button->set_sensitive (true);
1988 solo_isolated_led->set_sensitive (true);
1989 solo_safe_led->set_sensitive (true);
1990 monitor_input_button->set_sensitive (true);
1991 monitor_disk_button->set_sensitive (true);
1992 _comment_button.set_sensitive (true);
1996 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1998 _current_delivery = d;
1999 DeliveryChanged (_current_delivery);
2003 MixerStrip::show_send (boost::shared_ptr<Send> send)
2009 set_current_delivery (send);
2011 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2012 send->set_metering (true);
2013 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2015 gain_meter().set_controls (_route, send->meter(), send->amp());
2016 gain_meter().setup_meters ();
2018 uint32_t const in = _current_delivery->pans_required();
2019 uint32_t const out = _current_delivery->pan_outs();
2021 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2022 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2023 panner_ui().setup_pan ();
2024 panner_ui().set_send_drawing_mode (true);
2025 panner_ui().show_all ();
2027 input_button.set_sensitive (false);
2028 group_button.set_sensitive (false);
2029 set_invert_sensitive (false);
2030 meter_point_button.set_sensitive (false);
2031 mute_button->set_sensitive (false);
2032 solo_button->set_sensitive (false);
2033 rec_enable_button->set_sensitive (false);
2034 solo_isolated_led->set_sensitive (false);
2035 solo_safe_led->set_sensitive (false);
2036 monitor_input_button->set_sensitive (false);
2037 monitor_disk_button->set_sensitive (false);
2038 _comment_button.set_sensitive (false);
2040 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2041 output_button.set_sensitive (false);
2044 reset_strip_style ();
2048 MixerStrip::revert_to_default_display ()
2052 set_current_delivery (_route->main_outs ());
2054 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2055 gain_meter().setup_meters ();
2057 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2058 update_panner_choices();
2059 panner_ui().setup_pan ();
2060 panner_ui().set_send_drawing_mode (false);
2062 if (has_audio_outputs ()) {
2063 panners.show_all ();
2065 panners.hide_all ();
2068 reset_strip_style ();
2072 MixerStrip::set_button_names ()
2076 mute_button->set_text (_("Mute"));
2077 monitor_input_button->set_text (_("In"));
2078 monitor_disk_button->set_text (_("Disk"));
2080 if (_route && _route->solo_safe()) {
2081 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2083 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2085 if (!Config->get_solo_control_is_listen_control()) {
2086 solo_button->set_text (_("Solo"));
2088 switch (Config->get_listen_position()) {
2089 case AfterFaderListen:
2090 solo_button->set_text (_("AFL"));
2092 case PreFaderListen:
2093 solo_button->set_text (_("PFL"));
2097 solo_isolated_led->set_text (_("Iso"));
2098 solo_safe_led->set_text (S_("SoloLock|Lock"));
2102 mute_button->set_text (S_("Mute|M"));
2103 monitor_input_button->set_text (S_("MonitorInput|I"));
2104 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2106 if (_route && _route->solo_safe()) {
2107 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2109 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2111 if (!Config->get_solo_control_is_listen_control()) {
2112 solo_button->set_text (S_("Solo|S"));
2114 switch (Config->get_listen_position()) {
2115 case AfterFaderListen:
2116 solo_button->set_text (S_("AfterFader|A"));
2118 case PreFaderListen:
2119 solo_button->set_text (S_("Prefader|P"));
2124 solo_isolated_led->set_text (S_("SoloIso|I"));
2125 solo_safe_led->set_text (S_("SoloLock|L"));
2130 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2132 meter_point_button.set_text ("");
2137 MixerStrip::plugin_selector()
2139 return _mixer.plugin_selector();
2143 MixerStrip::hide_things ()
2145 processor_box.hide_things ();
2149 MixerStrip::input_active_button_press (GdkEventButton*)
2151 /* nothing happens on press */
2156 MixerStrip::input_active_button_release (GdkEventButton* ev)
2158 boost::shared_ptr<MidiTrack> mt = midi_track ();
2164 boost::shared_ptr<RouteList> rl (new RouteList);
2166 rl->push_back (route());
2168 _session->set_exclusive_input_active (rl, !mt->input_active(),
2169 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2175 MixerStrip::midi_input_status_changed ()
2177 if (midi_input_enable_button) {
2178 boost::shared_ptr<MidiTrack> mt = midi_track ();
2180 midi_input_enable_button->set_active (mt->input_active ());
2185 MixerStrip::state_id () const
2187 return string_compose ("strip %1", _route->id().to_s());
2191 MixerStrip::parameter_changed (string p)
2193 if (p == _visibility.get_state_name()) {
2194 /* The user has made changes to the mixer strip visibility, so get
2195 our VisibilityGroup to reflect these changes in our widgets.
2197 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2199 else if (p == "track-name-number") {
2204 /** Called to decide whether the solo isolate / solo lock button visibility should
2205 * be overridden from that configured by the user. We do this for the master bus.
2207 * @return optional value that is present if visibility state should be overridden.
2209 boost::optional<bool>
2210 MixerStrip::override_solo_visibility () const
2212 if (_route && _route->is_master ()) {
2213 return boost::optional<bool> (false);
2216 return boost::optional<bool> ();
2220 MixerStrip::add_input_port (DataType t)
2222 _route->input()->add_port ("", this, t);
2226 MixerStrip::add_output_port (DataType t)
2228 _route->output()->add_port ("", this, t);
2232 MixerStrip::route_active_changed ()
2234 reset_strip_style ();
2238 MixerStrip::copy_processors ()
2240 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2244 MixerStrip::cut_processors ()
2246 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2250 MixerStrip::paste_processors ()
2252 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2256 MixerStrip::select_all_processors ()
2258 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2262 MixerStrip::deselect_all_processors ()
2264 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2268 MixerStrip::delete_processors ()
2270 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2274 MixerStrip::toggle_processors ()
2276 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2280 MixerStrip::ab_plugins ()
2282 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2286 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2288 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2291 if (ev->button == 3) {
2292 popup_level_meter_menu (ev);
2300 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2302 using namespace Gtk::Menu_Helpers;
2304 Gtk::Menu* m = manage (new Menu);
2305 MenuList& items = m->items ();
2307 RadioMenuItem::Group group;
2309 _suspend_menu_callbacks = true;
2310 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2311 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2312 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2313 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2314 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2316 if (gpm.meter_channels().n_audio() == 0) {
2317 m->popup (ev->button, ev->time);
2318 _suspend_menu_callbacks = false;
2322 RadioMenuItem::Group tgroup;
2323 items.push_back (SeparatorElem());
2325 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2326 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2327 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2328 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2329 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2330 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2331 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2332 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2333 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2334 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2335 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2338 if (_route->is_master()) {
2341 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2342 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2343 /* non-master bus */
2346 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2353 MeterType cmt = _route->meter_type();
2354 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2356 items.push_back (SeparatorElem());
2357 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2358 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2359 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2360 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2361 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2362 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2364 m->popup (ev->button, ev->time);
2365 _suspend_menu_callbacks = false;
2369 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2370 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2372 using namespace Menu_Helpers;
2374 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2375 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2376 i->set_active (_route->meter_point() == point);
2380 MixerStrip::set_meter_point (MeterPoint p)
2382 if (_suspend_menu_callbacks) return;
2383 _route->set_meter_point (p);
2387 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2388 RadioMenuItem::Group& group, string const & name, MeterType type)
2390 using namespace Menu_Helpers;
2392 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2393 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2394 i->set_active (_route->meter_type() == type);
2398 MixerStrip::set_meter_type (MeterType t)
2400 if (_suspend_menu_callbacks) return;