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 items.push_back (SeparatorElem());
1600 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1603 /* note that this relies on selection being shared across editor and
1604 mixer (or global to the backend, in the future), which is the only
1605 sane thing for users anyway.
1608 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1610 Selection& selection (PublicEditor::instance().get_selection());
1611 if (!selection.selected (rtav)) {
1612 selection.set (rtav);
1615 if (!_route->is_master()) {
1616 items.push_back (SeparatorElem());
1617 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1620 items.push_back (SeparatorElem());
1621 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1627 MixerStrip::name_button_button_press (GdkEventButton* ev)
1629 if (ev->button == 3) {
1630 list_route_operations ();
1632 /* do not allow rename if the track is record-enabled */
1633 rename_menu_item->set_sensitive (!_route->record_enabled());
1634 route_ops_menu->popup (1, ev->time);
1643 MixerStrip::name_button_button_release (GdkEventButton* ev)
1645 if (ev->button == 1) {
1646 list_route_operations ();
1648 /* do not allow rename if the track is record-enabled */
1649 rename_menu_item->set_sensitive (!_route->record_enabled());
1650 route_ops_menu->popup (1, ev->time);
1657 MixerStrip::number_button_button_press (GdkEventButton* ev)
1659 if ( ev->button == 3 ) {
1660 list_route_operations ();
1662 /* do not allow rename if the track is record-enabled */
1663 rename_menu_item->set_sensitive (!_route->record_enabled());
1664 route_ops_menu->popup (1, ev->time);
1673 MixerStrip::list_route_operations ()
1675 delete route_ops_menu;
1676 build_route_ops_menu ();
1680 MixerStrip::set_selected (bool yn)
1682 AxisView::set_selected (yn);
1684 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1685 global_frame.set_name ("MixerStripSelectedFrame");
1687 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1688 global_frame.set_name ("MixerStripFrame");
1690 global_frame.queue_draw ();
1693 // processor_box.deselect_all_processors();
1697 MixerStrip::property_changed (const PropertyChange& what_changed)
1699 RouteUI::property_changed (what_changed);
1701 if (what_changed.contains (ARDOUR::Properties::name)) {
1707 MixerStrip::name_changed ()
1711 name_button.set_text (_route->name());
1714 name_button.set_text (PBD::short_version (_route->name(), 5));
1718 set_tooltip (name_button, _route->name());
1720 if (_session->config.get_track_name_number()) {
1721 const int64_t track_number = _route->track_number ();
1722 if (track_number == 0) {
1723 number_label.set_text ("-");
1725 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1728 number_label.set_text ("");
1733 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1735 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1739 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1741 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1745 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1747 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1751 MixerStrip::width_button_pressed (GdkEventButton* ev)
1753 if (ev->button != 1) {
1757 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1760 _mixer.set_strip_width (Narrow, true);
1764 _mixer.set_strip_width (Wide, true);
1770 set_width_enum (Narrow, this);
1773 set_width_enum (Wide, this);
1782 MixerStrip::hide_clicked ()
1784 // LAME fix to reset the button status for when it is redisplayed (part 1)
1785 hide_button.set_sensitive(false);
1788 Hiding(); /* EMIT_SIGNAL */
1790 _mixer.hide_strip (this);
1794 hide_button.set_sensitive(true);
1798 MixerStrip::set_embedded (bool yn)
1804 MixerStrip::map_frozen ()
1806 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1808 boost::shared_ptr<AudioTrack> at = audio_track();
1811 switch (at->freeze_state()) {
1812 case AudioTrack::Frozen:
1813 processor_box.set_sensitive (false);
1814 hide_redirect_editors ();
1817 processor_box.set_sensitive (true);
1818 // XXX need some way, maybe, to retoggle redirect editors
1822 processor_box.set_sensitive (true);
1827 MixerStrip::hide_redirect_editors ()
1829 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1833 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1835 boost::shared_ptr<Processor> processor (p.lock ());
1840 Gtk::Window* w = processor_box.get_processor_ui (processor);
1848 MixerStrip::reset_strip_style ()
1850 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1852 gpm.set_fader_name ("SendStripBase");
1856 if (is_midi_track()) {
1857 if (_route->active()) {
1858 set_name ("MidiTrackStripBase");
1860 set_name ("MidiTrackStripBaseInactive");
1862 gpm.set_fader_name ("MidiTrackFader");
1863 } else if (is_audio_track()) {
1864 if (_route->active()) {
1865 set_name ("AudioTrackStripBase");
1867 set_name ("AudioTrackStripBaseInactive");
1869 gpm.set_fader_name ("AudioTrackFader");
1871 if (_route->active()) {
1872 set_name ("AudioBusStripBase");
1874 set_name ("AudioBusStripBaseInactive");
1876 gpm.set_fader_name ("AudioBusFader");
1878 /* (no MIDI busses yet) */
1885 MixerStrip::engine_stopped ()
1890 MixerStrip::engine_running ()
1895 MixerStrip::meter_point_string (MeterPoint mp)
1908 case MeterPostFader:
1925 return S_("Meter|In");
1929 return S_("Meter|Pr");
1932 case MeterPostFader:
1933 return S_("Meter|Po");
1937 return S_("Meter|O");
1942 return S_("Meter|C");
1951 /** Called when the monitor-section state */
1953 MixerStrip::monitor_changed ()
1955 assert (monitor_section_button);
1956 if (_session->monitor_active()) {
1957 monitor_section_button->set_name ("master monitor section button active");
1959 monitor_section_button->set_name ("master monitor section button normal");
1963 /** Called when the metering point has changed */
1965 MixerStrip::meter_changed ()
1967 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1968 gpm.setup_meters ();
1969 // reset peak when meter point changes
1970 gpm.reset_peak_display();
1973 /** The bus that we are displaying sends to has changed, or been turned off.
1974 * @param send_to New bus that we are displaying sends to, or 0.
1977 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1979 RouteUI::bus_send_display_changed (send_to);
1982 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1987 revert_to_default_display ();
1990 revert_to_default_display ();
1995 MixerStrip::drop_send ()
1997 boost::shared_ptr<Send> current_send;
1999 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2000 current_send->set_metering (false);
2003 send_gone_connection.disconnect ();
2004 input_button.set_sensitive (true);
2005 output_button.set_sensitive (true);
2006 group_button.set_sensitive (true);
2007 set_invert_sensitive (true);
2008 meter_point_button.set_sensitive (true);
2009 mute_button->set_sensitive (true);
2010 solo_button->set_sensitive (true);
2011 rec_enable_button->set_sensitive (true);
2012 solo_isolated_led->set_sensitive (true);
2013 solo_safe_led->set_sensitive (true);
2014 monitor_input_button->set_sensitive (true);
2015 monitor_disk_button->set_sensitive (true);
2016 _comment_button.set_sensitive (true);
2020 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2022 _current_delivery = d;
2023 DeliveryChanged (_current_delivery);
2027 MixerStrip::show_send (boost::shared_ptr<Send> send)
2033 set_current_delivery (send);
2035 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2036 send->set_metering (true);
2037 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2039 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2040 gain_meter().setup_meters ();
2042 uint32_t const in = _current_delivery->pans_required();
2043 uint32_t const out = _current_delivery->pan_outs();
2045 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2046 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2047 panner_ui().setup_pan ();
2048 panner_ui().set_send_drawing_mode (true);
2049 panner_ui().show_all ();
2051 input_button.set_sensitive (false);
2052 group_button.set_sensitive (false);
2053 set_invert_sensitive (false);
2054 meter_point_button.set_sensitive (false);
2055 mute_button->set_sensitive (false);
2056 solo_button->set_sensitive (false);
2057 rec_enable_button->set_sensitive (false);
2058 solo_isolated_led->set_sensitive (false);
2059 solo_safe_led->set_sensitive (false);
2060 monitor_input_button->set_sensitive (false);
2061 monitor_disk_button->set_sensitive (false);
2062 _comment_button.set_sensitive (false);
2064 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2065 output_button.set_sensitive (false);
2068 reset_strip_style ();
2072 MixerStrip::revert_to_default_display ()
2076 set_current_delivery (_route->main_outs ());
2078 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2079 gain_meter().setup_meters ();
2081 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2082 update_panner_choices();
2083 panner_ui().setup_pan ();
2084 panner_ui().set_send_drawing_mode (false);
2086 if (has_audio_outputs ()) {
2087 panners.show_all ();
2089 panners.hide_all ();
2092 reset_strip_style ();
2096 MixerStrip::set_button_names ()
2100 mute_button->set_text (_("Mute"));
2101 monitor_input_button->set_text (_("In"));
2102 monitor_disk_button->set_text (_("Disk"));
2103 if (monitor_section_button) {
2104 monitor_section_button->set_text (_("Mon"));
2107 if (_route && _route->solo_safe()) {
2108 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2110 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2112 if (!Config->get_solo_control_is_listen_control()) {
2113 solo_button->set_text (_("Solo"));
2115 switch (Config->get_listen_position()) {
2116 case AfterFaderListen:
2117 solo_button->set_text (_("AFL"));
2119 case PreFaderListen:
2120 solo_button->set_text (_("PFL"));
2124 solo_isolated_led->set_text (_("Iso"));
2125 solo_safe_led->set_text (S_("SoloLock|Lock"));
2129 mute_button->set_text (S_("Mute|M"));
2130 monitor_input_button->set_text (S_("MonitorInput|I"));
2131 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2132 if (monitor_section_button) {
2133 monitor_section_button->set_text (S_("Mon|O"));
2136 if (_route && _route->solo_safe()) {
2137 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2139 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2141 if (!Config->get_solo_control_is_listen_control()) {
2142 solo_button->set_text (S_("Solo|S"));
2144 switch (Config->get_listen_position()) {
2145 case AfterFaderListen:
2146 solo_button->set_text (S_("AfterFader|A"));
2148 case PreFaderListen:
2149 solo_button->set_text (S_("Prefader|P"));
2154 solo_isolated_led->set_text (S_("SoloIso|I"));
2155 solo_safe_led->set_text (S_("SoloLock|L"));
2160 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2162 meter_point_button.set_text ("");
2167 MixerStrip::plugin_selector()
2169 return _mixer.plugin_selector();
2173 MixerStrip::hide_things ()
2175 processor_box.hide_things ();
2179 MixerStrip::input_active_button_press (GdkEventButton*)
2181 /* nothing happens on press */
2186 MixerStrip::input_active_button_release (GdkEventButton* ev)
2188 boost::shared_ptr<MidiTrack> mt = midi_track ();
2194 boost::shared_ptr<RouteList> rl (new RouteList);
2196 rl->push_back (route());
2198 _session->set_exclusive_input_active (rl, !mt->input_active(),
2199 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2205 MixerStrip::midi_input_status_changed ()
2207 if (midi_input_enable_button) {
2208 boost::shared_ptr<MidiTrack> mt = midi_track ();
2210 midi_input_enable_button->set_active (mt->input_active ());
2215 MixerStrip::state_id () const
2217 return string_compose ("strip %1", _route->id().to_s());
2221 MixerStrip::parameter_changed (string p)
2223 if (p == _visibility.get_state_name()) {
2224 /* The user has made changes to the mixer strip visibility, so get
2225 our VisibilityGroup to reflect these changes in our widgets.
2227 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2229 else if (p == "track-name-number") {
2232 else if (p == "use-monitor-bus") {
2233 if (monitor_section_button) {
2234 if (mute_button->get_parent()) {
2235 mute_button->get_parent()->remove(*mute_button);
2237 if (monitor_section_button->get_parent()) {
2238 monitor_section_button->get_parent()->remove(*monitor_section_button);
2240 if (Config->get_use_monitor_bus ()) {
2241 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2242 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2243 mute_button->show();
2244 monitor_section_button->show();
2246 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2247 mute_button->show();
2253 /** Called to decide whether the solo isolate / solo lock button visibility should
2254 * be overridden from that configured by the user. We do this for the master bus.
2256 * @return optional value that is present if visibility state should be overridden.
2258 boost::optional<bool>
2259 MixerStrip::override_solo_visibility () const
2261 if (_route && _route->is_master ()) {
2262 return boost::optional<bool> (false);
2265 return boost::optional<bool> ();
2269 MixerStrip::add_input_port (DataType t)
2271 _route->input()->add_port ("", this, t);
2275 MixerStrip::add_output_port (DataType t)
2277 _route->output()->add_port ("", this, t);
2281 MixerStrip::route_active_changed ()
2283 reset_strip_style ();
2287 MixerStrip::copy_processors ()
2289 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2293 MixerStrip::cut_processors ()
2295 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2299 MixerStrip::paste_processors ()
2301 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2305 MixerStrip::select_all_processors ()
2307 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2311 MixerStrip::deselect_all_processors ()
2313 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2317 MixerStrip::delete_processors ()
2319 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2323 MixerStrip::toggle_processors ()
2325 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2329 MixerStrip::ab_plugins ()
2331 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2335 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2337 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2340 if (ev->button == 3) {
2341 popup_level_meter_menu (ev);
2349 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2351 using namespace Gtk::Menu_Helpers;
2353 Gtk::Menu* m = manage (new Menu);
2354 MenuList& items = m->items ();
2356 RadioMenuItem::Group group;
2358 _suspend_menu_callbacks = true;
2359 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2360 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2361 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2362 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2363 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2365 if (gpm.meter_channels().n_audio() == 0) {
2366 m->popup (ev->button, ev->time);
2367 _suspend_menu_callbacks = false;
2371 RadioMenuItem::Group tgroup;
2372 items.push_back (SeparatorElem());
2374 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2375 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2376 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2377 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2378 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2379 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2380 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2381 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2382 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2383 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2384 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2387 if (_route->is_master()) {
2390 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2391 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2392 /* non-master bus */
2395 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2402 MeterType cmt = _route->meter_type();
2403 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2405 items.push_back (SeparatorElem());
2406 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2407 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2408 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2409 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2410 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2411 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2413 m->popup (ev->button, ev->time);
2414 _suspend_menu_callbacks = false;
2418 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2419 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2421 using namespace Menu_Helpers;
2423 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2424 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2425 i->set_active (_route->meter_point() == point);
2429 MixerStrip::set_meter_point (MeterPoint p)
2431 if (_suspend_menu_callbacks) return;
2432 _route->set_meter_point (p);
2436 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2437 RadioMenuItem::Group& group, string const & name, MeterType type)
2439 using namespace Menu_Helpers;
2441 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2442 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2443 i->set_active (_route->meter_type() == type);
2447 MixerStrip::set_meter_type (MeterType t)
2449 if (_suspend_menu_callbacks) return;