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 , monitor_section_button (0)
100 , midi_input_enable_button (0)
101 , _comment_button (_("Comments"))
102 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
103 , _visibility (X_("mixer-element-visibility"))
108 /* the editor mixer strip: don't destroy it every time
109 the underlying route goes away.
112 self_destruct = false;
116 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
120 , _mixer_owned (in_mixer)
121 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
124 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
125 , rec_mon_table (2, 2)
126 , solo_iso_table (1, 2)
127 , mute_solo_table (1, 2)
128 , bottom_button_table (1, 3)
129 , meter_point_button (_("pre"))
130 , monitor_section_button (0)
131 , midi_input_enable_button (0)
132 , _comment_button (_("Comments"))
133 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
134 , _visibility (X_("mixer-element-visibility"))
143 _entered_mixer_strip= 0;
146 ignore_comment_edit = false;
147 ignore_toggle = false;
152 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
153 longest_label = "longest label";
155 string t = _("Click to toggle the width of this mixer strip.");
157 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
160 width_button.set_icon (ArdourIcon::StripWidth);
161 set_tooltip (width_button, t);
163 hide_button.set_icon (ArdourIcon::CloseCross);
164 set_tooltip (&hide_button, _("Hide this mixer strip"));
166 input_button_box.set_spacing(2);
168 input_button.set_text (_("Input"));
169 input_button.set_name ("mixer strip button");
170 input_button_box.pack_start (input_button, true, true);
172 output_button.set_text (_("Output"));
173 output_button.set_name ("mixer strip button");
175 set_tooltip (&meter_point_button, _("Click to select metering point"));
176 meter_point_button.set_name ("mixer strip button");
178 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
180 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
181 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
183 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
185 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
186 solo_isolated_led->show ();
187 solo_isolated_led->set_no_show_all (true);
188 solo_isolated_led->set_name (X_("solo isolate"));
189 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
190 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
191 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
193 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
194 solo_safe_led->show ();
195 solo_safe_led->set_no_show_all (true);
196 solo_safe_led->set_name (X_("solo safe"));
197 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
198 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
199 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
201 solo_safe_led->set_text (S_("SoloLock|Lock"));
202 solo_isolated_led->set_text (_("Iso"));
204 solo_iso_table.set_homogeneous (true);
205 solo_iso_table.set_spacings (2);
206 if (!ARDOUR::Profile->get_trx()) {
207 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
208 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
210 solo_iso_table.show ();
212 rec_mon_table.set_homogeneous (true);
213 rec_mon_table.set_row_spacings (2);
214 rec_mon_table.set_col_spacings (2);
215 if (ARDOUR::Profile->get_mixbus()) {
216 rec_mon_table.resize (1, 3);
217 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
218 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
219 } else if (!ARDOUR::Profile->get_trx()) {
220 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
221 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
223 rec_mon_table.show ();
225 if (solo_isolated_led) {
226 button_size_group->add_widget (*solo_isolated_led);
229 button_size_group->add_widget (*solo_safe_led);
232 if (!ARDOUR::Profile->get_mixbus()) {
233 if (rec_enable_button) {
234 button_size_group->add_widget (*rec_enable_button);
236 if (monitor_disk_button) {
237 button_size_group->add_widget (*monitor_disk_button);
239 if (monitor_input_button) {
240 button_size_group->add_widget (*monitor_input_button);
244 mute_solo_table.set_homogeneous (true);
245 mute_solo_table.set_spacings (2);
247 bottom_button_table.set_spacings (2);
248 bottom_button_table.set_homogeneous (true);
249 bottom_button_table.attach (group_button, 1, 2, 0, 1);
250 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
252 name_button.set_name ("mixer strip button");
253 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
254 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
256 set_tooltip (&group_button, _("Mix group"));
257 group_button.set_name ("mixer strip button");
259 _comment_button.set_name (X_("mixer strip button"));
260 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
262 // TODO implement ArdourKnob::on_size_request properly
263 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
264 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
266 trim_control.set_tooltip_prefix (_("Trim: "));
267 trim_control.set_name ("trim knob");
268 trim_control.set_no_show_all (true);
269 input_button_box.pack_start (trim_control, false, false);
271 global_vpacker.set_border_width (1);
272 global_vpacker.set_spacing (0);
274 width_button.set_name ("mixer strip button");
275 hide_button.set_name ("mixer strip button");
277 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
278 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
280 // width_hide_box.set_border_width (1);
281 width_hide_box.set_spacing (2);
282 width_hide_box.pack_start (width_button, false, true);
283 width_hide_box.pack_start (number_label, true, true);
284 width_hide_box.pack_end (hide_button, false, true);
286 number_label.set_text ("-");
287 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
288 number_label.set_no_show_all ();
289 number_label.set_name ("tracknumber label");
290 number_label.set_fixed_colors (0x80808080, 0x80808080);
291 number_label.set_alignment (.5, .5);
292 number_label.set_fallthrough_to_parent (true);
294 global_vpacker.set_spacing (2);
295 if (!ARDOUR::Profile->get_trx()) {
296 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
298 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
299 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
300 global_vpacker.pack_start (processor_box, true, true);
302 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
305 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
306 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
308 if (!ARDOUR::Profile->get_trx()) {
309 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
315 global_frame.add (global_vpacker);
316 global_frame.set_shadow_type (Gtk::SHADOW_IN);
317 global_frame.set_name ("BaseFrame");
321 /* force setting of visible selected status */
324 set_selected (false);
329 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
330 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
332 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
333 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
334 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
336 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
337 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
339 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
340 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
341 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
343 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
345 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
346 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
348 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
352 /* start off as a passthru strip. we'll correct this, if necessary,
353 in update_diskstream_display().
356 /* start off as a passthru strip. we'll correct this, if necessary,
357 in update_diskstream_display().
360 if (is_midi_track()) {
361 set_name ("MidiTrackStripBase");
363 set_name ("AudioTrackStripBase");
366 add_events (Gdk::BUTTON_RELEASE_MASK|
367 Gdk::ENTER_NOTIFY_MASK|
368 Gdk::LEAVE_NOTIFY_MASK|
370 Gdk::KEY_RELEASE_MASK);
372 set_flags (get_flags() | Gtk::CAN_FOCUS);
374 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
375 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
378 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
379 must be the same as those used in RCOptionEditor so that the configuration changes
380 are recognised when they occur.
382 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
383 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
384 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
385 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
386 _visibility.add (&output_button, X_("Output"), _("Output"), false);
387 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
389 parameter_changed (X_("mixer-element-visibility"));
390 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
391 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
392 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
394 //watch for mouse enter/exit so we can do some stuff
395 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
396 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
398 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
401 MixerStrip::~MixerStrip ()
403 CatchDeletion (this);
405 if (this ==_entered_mixer_strip)
406 _entered_mixer_strip = NULL;
410 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
412 _entered_mixer_strip = this;
414 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
415 //because the mixerstrip control is a parent that encompasses the strip
416 deselect_all_processors();
422 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
424 //if we have moved outside our strip, but not into a child view, then deselect ourselves
425 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
426 _entered_mixer_strip= 0;
428 //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
429 gpm.gain_display.set_sensitive(false);
431 gpm.gain_display.set_sensitive(true);
433 //if we leave this mixer strip we need to clear out any selections
434 //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
441 MixerStrip::set_route (boost::shared_ptr<Route> rt)
443 //the rec/monitor stuff only shows up for tracks.
444 //the show_sends only shows up for buses.
445 //remove them all here, and we may add them back later
446 if (show_sends_button->get_parent()) {
447 rec_mon_table.remove (*show_sends_button);
449 if (rec_enable_button->get_parent()) {
450 rec_mon_table.remove (*rec_enable_button);
452 if (monitor_input_button->get_parent()) {
453 rec_mon_table.remove (*monitor_input_button);
455 if (monitor_disk_button->get_parent()) {
456 rec_mon_table.remove (*monitor_disk_button);
458 if (group_button.get_parent()) {
459 bottom_button_table.remove (group_button);
462 RouteUI::set_route (rt);
464 /* ProcessorBox needs access to _route so that it can read
467 processor_box.set_route (rt);
469 revert_to_default_display ();
471 /* unpack these from the parent and stuff them into our own
475 if (gpm.peak_display.get_parent()) {
476 gpm.peak_display.get_parent()->remove (gpm.peak_display);
478 if (gpm.gain_display.get_parent()) {
479 gpm.gain_display.get_parent()->remove (gpm.gain_display);
482 gpm.set_type (rt->meter_type());
484 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
485 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
487 if (solo_button->get_parent()) {
488 mute_solo_table.remove (*solo_button);
491 if (mute_button->get_parent()) {
492 mute_solo_table.remove (*mute_button);
495 if (route()->is_master()) {
496 solo_button->hide ();
497 mute_button->show ();
498 rec_mon_table.hide ();
499 if (solo_iso_table.get_parent()) {
500 solo_iso_table.get_parent()->remove(solo_iso_table);
502 if (monitor_section_button == 0) {
503 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
504 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
506 monitor_section_button = manage (new ArdourButton);
508 monitor_section_button->set_related_action (act);
509 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
510 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
511 monitor_section_button->show();
512 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
514 parameter_changed ("use-monitor-bus");
516 bottom_button_table.attach (group_button, 1, 2, 0, 1);
517 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
518 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
519 mute_button->show ();
520 solo_button->show ();
521 rec_mon_table.show ();
524 if (_mixer_owned && route()->is_master() ) {
526 HScrollbar scrollbar;
527 Gtk::Requisition requisition(scrollbar.size_request ());
528 int scrollbar_height = requisition.height;
530 spacer = manage (new EventBox);
531 spacer->set_size_request (-1, scrollbar_height+2);
532 global_vpacker.pack_start (*spacer, false, false);
537 monitor_input_button->show ();
538 monitor_disk_button->show ();
540 monitor_input_button->hide();
541 monitor_disk_button->hide ();
544 if (route()->trim() && route()->trim()->active()) {
545 trim_control.show ();
546 trim_control.set_controllable (route()->trim()->gain_control());
548 trim_control.hide ();
549 boost::shared_ptr<Controllable> none;
550 trim_control.set_controllable (none);
553 if (is_midi_track()) {
554 if (midi_input_enable_button == 0) {
555 midi_input_enable_button = manage (new ArdourButton);
556 midi_input_enable_button->set_name ("midi input button");
557 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
558 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
559 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
560 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
561 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
563 input_button_box.remove (*midi_input_enable_button);
565 /* get current state */
566 midi_input_status_changed ();
567 input_button_box.pack_start (*midi_input_enable_button, false, false);
569 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
571 if (midi_input_enable_button) {
572 /* removal from the container will delete it */
573 input_button_box.remove (*midi_input_enable_button);
574 midi_input_enable_button = 0;
578 if (is_audio_track()) {
579 boost::shared_ptr<AudioTrack> at = audio_track();
580 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
585 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
586 rec_enable_button->set_sensitive (_session->writable());
587 rec_enable_button->show();
589 if (ARDOUR::Profile->get_mixbus()) {
590 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
591 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
592 } else if (ARDOUR::Profile->get_trx()) {
593 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
595 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
596 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
603 if (!_route->is_master()) {
604 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
605 show_sends_button->show();
609 meter_point_button.set_text (meter_point_string (_route->meter_point()));
611 delete route_ops_menu;
614 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
615 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
616 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
617 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
619 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
621 if (_route->panner_shell()) {
622 update_panner_choices();
623 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
626 if (is_audio_track()) {
627 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
630 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
631 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
633 set_stuff_from_route ();
635 /* now force an update of all the various elements */
637 update_mute_display ();
638 update_solo_display ();
641 route_group_changed ();
644 panners.setup_pan ();
646 if (has_audio_outputs ()) {
652 update_diskstream_display ();
653 update_input_display ();
654 update_output_display ();
656 add_events (Gdk::BUTTON_RELEASE_MASK);
658 processor_box.show ();
660 if (!route()->is_master() && !route()->is_monitor()) {
661 /* we don't allow master or control routes to be hidden */
666 gpm.reset_peak_display ();
667 gpm.gain_display.show ();
668 gpm.peak_display.show ();
671 width_hide_box.show();
673 global_vpacker.show();
674 mute_solo_table.show();
675 bottom_button_table.show();
677 meter_point_button.show();
678 input_button_box.show_all();
679 output_button.show();
681 _comment_button.show();
683 gpm.gain_automation_state_button.show();
685 parameter_changed ("mixer-element-visibility");
692 MixerStrip::set_stuff_from_route ()
694 /* if width is not set, it will be set by the MixerUI or editor */
696 string str = gui_property ("strip-width");
698 set_width_enum (Width (string_2_enum (str, _width)), this);
703 MixerStrip::set_width_enum (Width w, void* owner)
705 /* always set the gpm width again, things may be hidden */
708 panners.set_width (w);
710 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
712 _width_owner = owner;
716 if (_width_owner == this) {
717 set_gui_property ("strip-width", enum_2_string (_width));
722 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
727 if (show_sends_button) {
728 show_sends_button->set_text (_("Aux"));
731 gpm.gain_automation_style_button.set_text (
732 gpm.astyle_string(gain_automation->automation_style()));
733 gpm.gain_automation_state_button.set_text (
734 gpm.astate_string(gain_automation->automation_state()));
736 if (_route->panner()) {
737 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
738 panners.astyle_string(_route->panner()->automation_style()));
739 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
740 panners.astate_string(_route->panner()->automation_state()));
744 // panners expect an even number of horiz. pixels
745 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
747 set_size_request (width, -1);
753 if (show_sends_button) {
754 show_sends_button->set_text (_("Snd"));
757 gpm.gain_automation_style_button.set_text (
758 gpm.short_astyle_string(gain_automation->automation_style()));
759 gpm.gain_automation_state_button.set_text (
760 gpm.short_astate_string(gain_automation->automation_state()));
761 gain_meter().setup_meters (); // recalc meter width
763 if (_route->panner()) {
764 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
765 panners.short_astyle_string(_route->panner()->automation_style()));
766 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
767 panners.short_astate_string(_route->panner()->automation_state()));
771 // panners expect an even number of horiz. pixels
772 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
774 set_size_request (width, -1);
779 processor_box.set_width (w);
781 update_input_display ();
782 update_output_display ();
783 setup_comment_button ();
784 route_group_changed ();
790 MixerStrip::set_packed (bool yn)
795 set_gui_property ("visible", true);
797 set_gui_property ("visible", false);
802 struct RouteCompareByName {
803 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
804 return a->name().compare (b->name()) < 0;
809 MixerStrip::output_release (GdkEventButton *ev)
811 switch (ev->button) {
813 edit_output_configuration ();
821 MixerStrip::output_press (GdkEventButton *ev)
823 using namespace Menu_Helpers;
824 if (!_session->engine().connected()) {
825 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
830 MenuList& citems = output_menu.items();
831 switch (ev->button) {
834 return false; //wait for the mouse-up to pop the dialog
838 output_menu.set_name ("ArdourContextMenu");
840 output_menu_bundles.clear ();
842 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
844 citems.push_back (SeparatorElem());
845 uint32_t const n_with_separator = citems.size ();
847 ARDOUR::BundleList current = _route->output()->bundles_connected ();
849 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
851 /* give user bundles first chance at being in the menu */
853 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
854 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
855 maybe_add_bundle_to_output_menu (*i, current);
859 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
860 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
861 maybe_add_bundle_to_output_menu (*i, current);
865 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
866 RouteList copy = *routes;
867 copy.sort (RouteCompareByName ());
868 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
869 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
872 if (citems.size() == n_with_separator) {
873 /* no routes added; remove the separator */
877 citems.push_back (SeparatorElem());
879 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
882 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
883 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
888 citems.push_back (SeparatorElem());
889 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
891 output_menu.popup (1, ev->time);
902 MixerStrip::input_release (GdkEventButton *ev)
904 switch (ev->button) {
907 edit_input_configuration ();
919 MixerStrip::input_press (GdkEventButton *ev)
921 using namespace Menu_Helpers;
923 MenuList& citems = input_menu.items();
924 input_menu.set_name ("ArdourContextMenu");
927 if (!_session->engine().connected()) {
928 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
933 if (_session->actively_recording() && _route->record_enabled())
936 switch (ev->button) {
939 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
943 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
945 citems.push_back (SeparatorElem());
946 uint32_t const n_with_separator = citems.size ();
948 input_menu_bundles.clear ();
950 ARDOUR::BundleList current = _route->input()->bundles_connected ();
952 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
954 /* give user bundles first chance at being in the menu */
956 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
957 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
958 maybe_add_bundle_to_input_menu (*i, current);
962 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
963 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
964 maybe_add_bundle_to_input_menu (*i, current);
968 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
969 RouteList copy = *routes;
970 copy.sort (RouteCompareByName ());
971 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
972 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
975 if (citems.size() == n_with_separator) {
976 /* no routes added; remove the separator */
980 citems.push_back (SeparatorElem());
981 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
984 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
985 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
990 citems.push_back (SeparatorElem());
991 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
993 input_menu.popup (1, ev->time);
1004 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1006 if (ignore_toggle) {
1010 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1012 if (std::find (current.begin(), current.end(), c) == current.end()) {
1013 _route->input()->connect_ports_to_bundle (c, true, this);
1015 _route->input()->disconnect_ports_from_bundle (c, this);
1020 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1022 if (ignore_toggle) {
1026 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1028 if (std::find (current.begin(), current.end(), c) == current.end()) {
1029 _route->output()->connect_ports_to_bundle (c, true, this);
1031 _route->output()->disconnect_ports_from_bundle (c, this);
1036 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1038 using namespace Menu_Helpers;
1040 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1044 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1045 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1049 if (i != input_menu_bundles.end()) {
1053 input_menu_bundles.push_back (b);
1055 MenuList& citems = input_menu.items();
1057 std::string n = b->name ();
1058 replace_all (n, "_", " ");
1060 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1064 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1066 using namespace Menu_Helpers;
1068 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1072 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1073 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1077 if (i != output_menu_bundles.end()) {
1081 output_menu_bundles.push_back (b);
1083 MenuList& citems = output_menu.items();
1085 std::string n = b->name ();
1086 replace_all (n, "_", " ");
1088 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1092 MixerStrip::update_diskstream_display ()
1094 if (is_track() && input_selector) {
1095 input_selector->hide_all ();
1098 route_color_changed ();
1102 MixerStrip::connect_to_pan ()
1104 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1106 panstate_connection.disconnect ();
1107 panstyle_connection.disconnect ();
1109 if (!_route->panner()) {
1113 boost::shared_ptr<Pannable> p = _route->pannable ();
1115 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1116 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1118 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1119 * However, that only works a panner was previously set.
1121 * PannerUI must remain subscribed to _panshell->Changed() in case
1122 * we switch the panner eg. AUX-Send and back
1123 * _route->panner_shell()->Changed() vs _panshell->Changed
1125 if (panners._panner == 0) {
1126 panners.panshell_changed ();
1128 update_panner_choices();
1132 MixerStrip::update_panner_choices ()
1134 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1135 if (!_route->panner_shell()) { return; }
1137 uint32_t in = _route->output()->n_ports().n_audio();
1139 if (_route->panner()) {
1140 in = _route->panner()->in().n_audio();
1143 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1147 * Output port labelling
1148 * =====================
1150 * Case 1: Each output has one connection, all connections are to system:playback_%i
1151 * out 1 -> system:playback_1
1152 * out 2 -> system:playback_2
1153 * out 3 -> system:playback_3
1156 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1157 * out 1 -> ardour:track_x/in 1
1158 * out 2 -> ardour:track_x/in 2
1159 * Display as: track_x
1161 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1162 * out 1 -> program x:foo
1163 * out 2 -> program x:foo
1164 * Display as: program x
1166 * Case 4: No connections (Disconnected)
1169 * Default case (unusual routing):
1170 * Display as: *number of connections*
1174 * .-----------------------------------------------.
1176 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1177 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1178 * '-----------------------------------------------'
1179 * .-----------------------------------------------.
1182 * '-----------------------------------------------'
1186 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1190 boost::shared_ptr<Port> port;
1191 vector<string> port_connections;
1193 uint32_t total_connection_count = 0;
1194 uint32_t io_connection_count = 0;
1195 uint32_t ardour_connection_count = 0;
1196 uint32_t system_connection_count = 0;
1197 uint32_t other_connection_count = 0;
1198 uint32_t typed_connection_count = 0;
1200 ostringstream label;
1202 bool have_label = false;
1203 bool each_io_has_one_connection = true;
1205 string connection_name;
1206 string ardour_track_name;
1207 string other_connection_type;
1208 string system_ports;
1211 ostringstream tooltip;
1212 char * tooltip_cstr;
1214 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1215 DataType dt = DataType::AUDIO;
1216 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1217 dt = DataType::MIDI;
1218 // avoid further confusion with Midi-tracks that have a synth.
1219 // Audio-ports may be connected, but button says "Disconnected"
1220 tooltip << _("MIDI ");
1224 io_count = route->n_inputs().n_total();
1225 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1227 io_count = route->n_outputs().n_total();
1228 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1232 for (io_index = 0; io_index < io_count; ++io_index) {
1234 port = route->input()->nth (io_index);
1236 port = route->output()->nth (io_index);
1239 port_connections.clear ();
1240 port->get_connections(port_connections);
1242 //ignore any port connections that don't match our DataType
1243 if (port->type() != dt) {
1244 if (!port_connections.empty()) {
1245 ++typed_connection_count;
1250 io_connection_count = 0;
1252 if (!port_connections.empty()) {
1253 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1255 string& connection_name (*i);
1257 if (connection_name.find("system:") == 0) {
1258 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1261 if (io_connection_count == 0) {
1262 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1264 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1267 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1270 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1271 if (ardour_track_name.empty()) {
1272 // "ardour:Master/in 1" -> "ardour:Master/"
1273 string::size_type slash = connection_name.find("/");
1274 if (slash != string::npos) {
1275 ardour_track_name = connection_name.substr(0, slash + 1);
1279 if (connection_name.find(ardour_track_name) == 0) {
1280 ++ardour_connection_count;
1282 } else if (!pn.empty()) {
1283 if (system_ports.empty()) {
1286 system_ports += "/" + pn;
1288 if (connection_name.find("system:") == 0) {
1289 ++system_connection_count;
1291 } else if (connection_name.find("system:midi_") == 0) {
1293 // "system:midi_capture_123" -> "123"
1294 system_port = "M " + connection_name.substr(20);
1296 // "system:midi_playback_123" -> "123"
1297 system_port = "M " + connection_name.substr(21);
1300 if (system_ports.empty()) {
1301 system_ports += system_port;
1303 system_ports += "/" + system_port;
1306 ++system_connection_count;
1308 } else if (connection_name.find("system:") == 0) {
1310 // "system:capture_123" -> "123"
1311 system_port = connection_name.substr(15);
1313 // "system:playback_123" -> "123"
1314 system_port = connection_name.substr(16);
1317 if (system_ports.empty()) {
1318 system_ports += system_port;
1320 system_ports += "/" + system_port;
1323 ++system_connection_count;
1325 if (other_connection_type.empty()) {
1326 // "jamin:in 1" -> "jamin:"
1327 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1330 if (connection_name.find(other_connection_type) == 0) {
1331 ++other_connection_count;
1335 ++total_connection_count;
1336 ++io_connection_count;
1340 if (io_connection_count != 1) {
1341 each_io_has_one_connection = false;
1345 if (total_connection_count == 0) {
1346 tooltip << endl << _("Disconnected");
1349 tooltip_cstr = new char[tooltip.str().size() + 1];
1350 strcpy(tooltip_cstr, tooltip.str().c_str());
1353 set_tooltip (&input_button, tooltip_cstr);
1355 set_tooltip (&output_button, tooltip_cstr);
1358 if (each_io_has_one_connection) {
1359 if (total_connection_count == ardour_connection_count) {
1360 // all connections are to the same track in ardour
1361 // "ardour:Master/" -> "Master"
1362 string::size_type slash = ardour_track_name.find("/");
1363 if (slash != string::npos) {
1364 label << ardour_track_name.substr(7, slash - 7);
1368 else if (total_connection_count == system_connection_count) {
1369 // all connections are to system ports
1370 label << system_ports;
1373 else if (total_connection_count == other_connection_count) {
1374 // all connections are to the same external program eg jamin
1375 // "jamin:" -> "jamin"
1376 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1382 if (total_connection_count == 0) {
1386 // Odd configuration
1387 label << "*" << total_connection_count << "*";
1389 if (typed_connection_count > 0) {
1390 label << "\u2295"; // circled plus
1395 input_button.set_text (label.str());
1397 output_button.set_text (label.str());
1402 MixerStrip::update_input_display ()
1404 update_io_button (_route, _width, true);
1405 panners.setup_pan ();
1407 if (has_audio_outputs ()) {
1408 panners.show_all ();
1410 panners.hide_all ();
1416 MixerStrip::update_output_display ()
1418 update_io_button (_route, _width, false);
1419 gpm.setup_meters ();
1420 panners.setup_pan ();
1422 if (has_audio_outputs ()) {
1423 panners.show_all ();
1425 panners.hide_all ();
1430 MixerStrip::fast_update ()
1432 gpm.update_meters ();
1436 MixerStrip::diskstream_changed ()
1438 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1442 MixerStrip::io_changed_proxy ()
1444 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1448 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1450 boost::shared_ptr<Port> a = wa.lock ();
1451 boost::shared_ptr<Port> b = wb.lock ();
1453 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1454 update_input_display ();
1455 set_width_enum (_width, this);
1458 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1459 update_output_display ();
1460 set_width_enum (_width, this);
1465 MixerStrip::setup_comment_button ()
1470 if (_route->comment().empty ()) {
1471 _comment_button.unset_bg (STATE_NORMAL);
1472 _comment_button.set_text (_("Comments"));
1474 _comment_button.modify_bg (STATE_NORMAL, color ());
1475 _comment_button.set_text (_("*Comments*"));
1480 if (_route->comment().empty ()) {
1481 _comment_button.unset_bg (STATE_NORMAL);
1482 _comment_button.set_text (_("Cmt"));
1484 _comment_button.modify_bg (STATE_NORMAL, color ());
1485 _comment_button.set_text (_("*Cmt*"));
1491 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1497 MixerStrip::select_route_group (GdkEventButton *ev)
1499 using namespace Menu_Helpers;
1501 if (ev->button == 1) {
1503 if (group_menu == 0) {
1505 PropertyList* plist = new PropertyList();
1507 plist->add (Properties::gain, true);
1508 plist->add (Properties::mute, true);
1509 plist->add (Properties::solo, true);
1511 group_menu = new RouteGroupMenu (_session, plist);
1515 r.push_back (route ());
1516 group_menu->build (r);
1517 group_menu->menu()->popup (1, ev->time);
1524 MixerStrip::route_group_changed ()
1526 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1528 RouteGroup *rg = _route->route_group();
1531 group_button.set_text (PBD::short_version (rg->name(), 5));
1535 group_button.set_text (_("Grp"));
1538 group_button.set_text (_("~G"));
1545 MixerStrip::route_color_changed ()
1547 name_button.modify_bg (STATE_NORMAL, color());
1548 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1549 reset_strip_style ();
1553 MixerStrip::show_passthru_color ()
1555 reset_strip_style ();
1559 MixerStrip::build_route_ops_menu ()
1561 using namespace Menu_Helpers;
1562 route_ops_menu = new Menu;
1563 route_ops_menu->set_name ("ArdourContextMenu");
1565 MenuList& items = route_ops_menu->items();
1567 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1569 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1571 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1573 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1575 items.push_back (SeparatorElem());
1577 if (!_route->is_master()) {
1578 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1580 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1581 rename_menu_item = &items.back();
1583 items.push_back (SeparatorElem());
1584 items.push_back (CheckMenuElem (_("Active")));
1585 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1586 i->set_active (_route->active());
1587 i->set_sensitive(! _session->transport_rolling());
1588 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1590 items.push_back (SeparatorElem());
1592 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1594 items.push_back (SeparatorElem());
1595 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1596 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1597 denormal_menu_item->set_active (_route->denormal_protection());
1599 if (!Profile->get_sae()) {
1600 items.push_back (SeparatorElem());
1601 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1605 /* note that this relies on selection being shared across editor and
1606 mixer (or global to the backend, in the future), which is the only
1607 sane thing for users anyway.
1610 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1612 Selection& selection (PublicEditor::instance().get_selection());
1613 if (!selection.selected (rtav)) {
1614 selection.set (rtav);
1617 if (!_route->is_master()) {
1618 items.push_back (SeparatorElem());
1619 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1622 items.push_back (SeparatorElem());
1623 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1629 MixerStrip::name_button_button_press (GdkEventButton* ev)
1631 if (ev->button == 3) {
1632 list_route_operations ();
1634 /* do not allow rename if the track is record-enabled */
1635 rename_menu_item->set_sensitive (!_route->record_enabled());
1636 route_ops_menu->popup (1, ev->time);
1645 MixerStrip::name_button_button_release (GdkEventButton* ev)
1647 if (ev->button == 1) {
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);
1659 MixerStrip::number_button_button_press (GdkEventButton* ev)
1661 if ( ev->button == 3 ) {
1662 list_route_operations ();
1664 /* do not allow rename if the track is record-enabled */
1665 rename_menu_item->set_sensitive (!_route->record_enabled());
1666 route_ops_menu->popup (1, ev->time);
1675 MixerStrip::list_route_operations ()
1677 delete route_ops_menu;
1678 build_route_ops_menu ();
1682 MixerStrip::set_selected (bool yn)
1684 AxisView::set_selected (yn);
1686 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1687 global_frame.set_name ("MixerStripSelectedFrame");
1689 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1690 global_frame.set_name ("MixerStripFrame");
1692 global_frame.queue_draw ();
1695 // processor_box.deselect_all_processors();
1699 MixerStrip::property_changed (const PropertyChange& what_changed)
1701 RouteUI::property_changed (what_changed);
1703 if (what_changed.contains (ARDOUR::Properties::name)) {
1709 MixerStrip::name_changed ()
1713 name_button.set_text (_route->name());
1716 name_button.set_text (PBD::short_version (_route->name(), 5));
1720 set_tooltip (name_button, _route->name());
1722 if (_session->config.get_track_name_number()) {
1723 const int64_t track_number = _route->track_number ();
1724 if (track_number == 0) {
1725 number_label.set_text ("-");
1727 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1730 number_label.set_text ("");
1735 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1737 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1741 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1743 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1747 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1749 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1753 MixerStrip::width_button_pressed (GdkEventButton* ev)
1755 if (ev->button != 1) {
1759 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1762 _mixer.set_strip_width (Narrow, true);
1766 _mixer.set_strip_width (Wide, true);
1772 set_width_enum (Narrow, this);
1775 set_width_enum (Wide, this);
1784 MixerStrip::hide_clicked ()
1786 // LAME fix to reset the button status for when it is redisplayed (part 1)
1787 hide_button.set_sensitive(false);
1790 Hiding(); /* EMIT_SIGNAL */
1792 _mixer.hide_strip (this);
1796 hide_button.set_sensitive(true);
1800 MixerStrip::set_embedded (bool yn)
1806 MixerStrip::map_frozen ()
1808 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1810 boost::shared_ptr<AudioTrack> at = audio_track();
1813 switch (at->freeze_state()) {
1814 case AudioTrack::Frozen:
1815 processor_box.set_sensitive (false);
1816 hide_redirect_editors ();
1819 processor_box.set_sensitive (true);
1820 // XXX need some way, maybe, to retoggle redirect editors
1824 processor_box.set_sensitive (true);
1829 MixerStrip::hide_redirect_editors ()
1831 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1835 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1837 boost::shared_ptr<Processor> processor (p.lock ());
1842 Gtk::Window* w = processor_box.get_processor_ui (processor);
1850 MixerStrip::reset_strip_style ()
1852 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1854 gpm.set_fader_name ("SendStripBase");
1858 if (is_midi_track()) {
1859 if (_route->active()) {
1860 set_name ("MidiTrackStripBase");
1862 set_name ("MidiTrackStripBaseInactive");
1864 gpm.set_fader_name ("MidiTrackFader");
1865 } else if (is_audio_track()) {
1866 if (_route->active()) {
1867 set_name ("AudioTrackStripBase");
1869 set_name ("AudioTrackStripBaseInactive");
1871 gpm.set_fader_name ("AudioTrackFader");
1873 if (_route->active()) {
1874 set_name ("AudioBusStripBase");
1876 set_name ("AudioBusStripBaseInactive");
1878 gpm.set_fader_name ("AudioBusFader");
1880 /* (no MIDI busses yet) */
1887 MixerStrip::engine_stopped ()
1892 MixerStrip::engine_running ()
1897 MixerStrip::meter_point_string (MeterPoint mp)
1910 case MeterPostFader:
1927 return S_("Meter|In");
1931 return S_("Meter|Pr");
1934 case MeterPostFader:
1935 return S_("Meter|Po");
1939 return S_("Meter|O");
1944 return S_("Meter|C");
1953 /** Called when the monitor-section state */
1955 MixerStrip::monitor_changed ()
1957 assert (monitor_section_button);
1958 if (_session->monitor_active()) {
1959 monitor_section_button->set_name ("master monitor section button active");
1961 monitor_section_button->set_name ("master monitor section button normal");
1965 /** Called when the metering point has changed */
1967 MixerStrip::meter_changed ()
1969 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1970 gpm.setup_meters ();
1971 // reset peak when meter point changes
1972 gpm.reset_peak_display();
1975 /** The bus that we are displaying sends to has changed, or been turned off.
1976 * @param send_to New bus that we are displaying sends to, or 0.
1979 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1981 RouteUI::bus_send_display_changed (send_to);
1984 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1989 revert_to_default_display ();
1992 revert_to_default_display ();
1997 MixerStrip::drop_send ()
1999 boost::shared_ptr<Send> current_send;
2001 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2002 current_send->set_metering (false);
2005 send_gone_connection.disconnect ();
2006 input_button.set_sensitive (true);
2007 output_button.set_sensitive (true);
2008 group_button.set_sensitive (true);
2009 set_invert_sensitive (true);
2010 meter_point_button.set_sensitive (true);
2011 mute_button->set_sensitive (true);
2012 solo_button->set_sensitive (true);
2013 rec_enable_button->set_sensitive (true);
2014 solo_isolated_led->set_sensitive (true);
2015 solo_safe_led->set_sensitive (true);
2016 monitor_input_button->set_sensitive (true);
2017 monitor_disk_button->set_sensitive (true);
2018 _comment_button.set_sensitive (true);
2022 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2024 _current_delivery = d;
2025 DeliveryChanged (_current_delivery);
2029 MixerStrip::show_send (boost::shared_ptr<Send> send)
2035 set_current_delivery (send);
2037 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2038 send->set_metering (true);
2039 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2041 gain_meter().set_controls (_route, send->meter(), send->amp());
2042 gain_meter().setup_meters ();
2044 uint32_t const in = _current_delivery->pans_required();
2045 uint32_t const out = _current_delivery->pan_outs();
2047 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2048 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2049 panner_ui().setup_pan ();
2050 panner_ui().set_send_drawing_mode (true);
2051 panner_ui().show_all ();
2053 input_button.set_sensitive (false);
2054 group_button.set_sensitive (false);
2055 set_invert_sensitive (false);
2056 meter_point_button.set_sensitive (false);
2057 mute_button->set_sensitive (false);
2058 solo_button->set_sensitive (false);
2059 rec_enable_button->set_sensitive (false);
2060 solo_isolated_led->set_sensitive (false);
2061 solo_safe_led->set_sensitive (false);
2062 monitor_input_button->set_sensitive (false);
2063 monitor_disk_button->set_sensitive (false);
2064 _comment_button.set_sensitive (false);
2066 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2067 output_button.set_sensitive (false);
2070 reset_strip_style ();
2074 MixerStrip::revert_to_default_display ()
2078 set_current_delivery (_route->main_outs ());
2080 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2081 gain_meter().setup_meters ();
2083 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2084 update_panner_choices();
2085 panner_ui().setup_pan ();
2086 panner_ui().set_send_drawing_mode (false);
2088 if (has_audio_outputs ()) {
2089 panners.show_all ();
2091 panners.hide_all ();
2094 reset_strip_style ();
2098 MixerStrip::set_button_names ()
2102 mute_button->set_text (_("Mute"));
2103 monitor_input_button->set_text (_("In"));
2104 monitor_disk_button->set_text (_("Disk"));
2105 if (monitor_section_button) {
2106 monitor_section_button->set_text (_("Mon"));
2109 if (_route && _route->solo_safe()) {
2110 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2112 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2114 if (!Config->get_solo_control_is_listen_control()) {
2115 solo_button->set_text (_("Solo"));
2117 switch (Config->get_listen_position()) {
2118 case AfterFaderListen:
2119 solo_button->set_text (_("AFL"));
2121 case PreFaderListen:
2122 solo_button->set_text (_("PFL"));
2126 solo_isolated_led->set_text (_("Iso"));
2127 solo_safe_led->set_text (S_("SoloLock|Lock"));
2131 mute_button->set_text (S_("Mute|M"));
2132 monitor_input_button->set_text (S_("MonitorInput|I"));
2133 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2134 if (monitor_section_button) {
2135 monitor_section_button->set_text (S_("Mon|O"));
2138 if (_route && _route->solo_safe()) {
2139 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2141 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2143 if (!Config->get_solo_control_is_listen_control()) {
2144 solo_button->set_text (S_("Solo|S"));
2146 switch (Config->get_listen_position()) {
2147 case AfterFaderListen:
2148 solo_button->set_text (S_("AfterFader|A"));
2150 case PreFaderListen:
2151 solo_button->set_text (S_("Prefader|P"));
2156 solo_isolated_led->set_text (S_("SoloIso|I"));
2157 solo_safe_led->set_text (S_("SoloLock|L"));
2162 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2164 meter_point_button.set_text ("");
2169 MixerStrip::plugin_selector()
2171 return _mixer.plugin_selector();
2175 MixerStrip::hide_things ()
2177 processor_box.hide_things ();
2181 MixerStrip::input_active_button_press (GdkEventButton*)
2183 /* nothing happens on press */
2188 MixerStrip::input_active_button_release (GdkEventButton* ev)
2190 boost::shared_ptr<MidiTrack> mt = midi_track ();
2196 boost::shared_ptr<RouteList> rl (new RouteList);
2198 rl->push_back (route());
2200 _session->set_exclusive_input_active (rl, !mt->input_active(),
2201 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2207 MixerStrip::midi_input_status_changed ()
2209 if (midi_input_enable_button) {
2210 boost::shared_ptr<MidiTrack> mt = midi_track ();
2212 midi_input_enable_button->set_active (mt->input_active ());
2217 MixerStrip::state_id () const
2219 return string_compose ("strip %1", _route->id().to_s());
2223 MixerStrip::parameter_changed (string p)
2225 if (p == _visibility.get_state_name()) {
2226 /* The user has made changes to the mixer strip visibility, so get
2227 our VisibilityGroup to reflect these changes in our widgets.
2229 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2231 else if (p == "track-name-number") {
2234 else if (p == "use-monitor-bus") {
2235 if (monitor_section_button) {
2236 if (mute_button->get_parent()) {
2237 mute_button->get_parent()->remove(*mute_button);
2239 if (monitor_section_button->get_parent()) {
2240 monitor_section_button->get_parent()->remove(*monitor_section_button);
2242 if (Config->get_use_monitor_bus ()) {
2243 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2244 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2245 mute_button->show();
2246 monitor_section_button->show();
2248 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2249 mute_button->show();
2255 /** Called to decide whether the solo isolate / solo lock button visibility should
2256 * be overridden from that configured by the user. We do this for the master bus.
2258 * @return optional value that is present if visibility state should be overridden.
2260 boost::optional<bool>
2261 MixerStrip::override_solo_visibility () const
2263 if (_route && _route->is_master ()) {
2264 return boost::optional<bool> (false);
2267 return boost::optional<bool> ();
2271 MixerStrip::add_input_port (DataType t)
2273 _route->input()->add_port ("", this, t);
2277 MixerStrip::add_output_port (DataType t)
2279 _route->output()->add_port ("", this, t);
2283 MixerStrip::route_active_changed ()
2285 reset_strip_style ();
2289 MixerStrip::copy_processors ()
2291 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2295 MixerStrip::cut_processors ()
2297 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2301 MixerStrip::paste_processors ()
2303 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2307 MixerStrip::select_all_processors ()
2309 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2313 MixerStrip::deselect_all_processors ()
2315 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2319 MixerStrip::delete_processors ()
2321 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2325 MixerStrip::toggle_processors ()
2327 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2331 MixerStrip::ab_plugins ()
2333 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2337 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2339 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2342 if (ev->button == 3) {
2343 popup_level_meter_menu (ev);
2351 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2353 using namespace Gtk::Menu_Helpers;
2355 Gtk::Menu* m = manage (new Menu);
2356 MenuList& items = m->items ();
2358 RadioMenuItem::Group group;
2360 _suspend_menu_callbacks = true;
2361 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2362 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2363 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2364 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2365 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2367 if (gpm.meter_channels().n_audio() == 0) {
2368 m->popup (ev->button, ev->time);
2369 _suspend_menu_callbacks = false;
2373 RadioMenuItem::Group tgroup;
2374 items.push_back (SeparatorElem());
2376 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2377 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2378 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2379 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2380 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2381 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2382 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2383 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2384 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2385 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2386 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2389 if (_route->is_master()) {
2392 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2393 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2394 /* non-master bus */
2397 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2404 MeterType cmt = _route->meter_type();
2405 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2407 items.push_back (SeparatorElem());
2408 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2409 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2410 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2411 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2412 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2413 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2415 m->popup (ev->button, ev->time);
2416 _suspend_menu_callbacks = false;
2420 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2421 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2423 using namespace Menu_Helpers;
2425 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2426 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2427 i->set_active (_route->meter_point() == point);
2431 MixerStrip::set_meter_point (MeterPoint p)
2433 if (_suspend_menu_callbacks) return;
2434 _route->set_meter_point (p);
2438 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2439 RadioMenuItem::Group& group, string const & name, MeterType type)
2441 using namespace Menu_Helpers;
2443 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2444 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2445 i->set_active (_route->meter_type() == type);
2449 MixerStrip::set_meter_type (MeterType t)
2451 if (_suspend_menu_callbacks) return;