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 if (!ARDOUR::Profile->get_mixbus()) {
878 citems.push_back (SeparatorElem());
880 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
883 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
884 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
890 citems.push_back (SeparatorElem());
891 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
893 output_menu.popup (1, ev->time);
904 MixerStrip::input_release (GdkEventButton *ev)
906 switch (ev->button) {
909 edit_input_configuration ();
921 MixerStrip::input_press (GdkEventButton *ev)
923 using namespace Menu_Helpers;
925 MenuList& citems = input_menu.items();
926 input_menu.set_name ("ArdourContextMenu");
929 if (!_session->engine().connected()) {
930 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
935 if (_session->actively_recording() && _route->record_enabled())
938 switch (ev->button) {
941 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
945 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
947 citems.push_back (SeparatorElem());
948 uint32_t const n_with_separator = citems.size ();
950 input_menu_bundles.clear ();
952 ARDOUR::BundleList current = _route->input()->bundles_connected ();
954 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
956 /* give user bundles first chance at being in the menu */
958 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
959 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
960 maybe_add_bundle_to_input_menu (*i, current);
964 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
965 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
966 maybe_add_bundle_to_input_menu (*i, current);
970 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
971 RouteList copy = *routes;
972 copy.sort (RouteCompareByName ());
973 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
974 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
977 if (citems.size() == n_with_separator) {
978 /* no routes added; remove the separator */
982 citems.push_back (SeparatorElem());
983 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
986 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
987 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
992 citems.push_back (SeparatorElem());
993 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
995 input_menu.popup (1, ev->time);
1006 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1008 if (ignore_toggle) {
1012 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1014 if (std::find (current.begin(), current.end(), c) == current.end()) {
1015 _route->input()->connect_ports_to_bundle (c, true, this);
1017 _route->input()->disconnect_ports_from_bundle (c, this);
1022 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1024 if (ignore_toggle) {
1028 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1030 if (std::find (current.begin(), current.end(), c) == current.end()) {
1031 _route->output()->connect_ports_to_bundle (c, true, this);
1033 _route->output()->disconnect_ports_from_bundle (c, this);
1038 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1040 using namespace Menu_Helpers;
1042 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1046 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1047 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1051 if (i != input_menu_bundles.end()) {
1055 input_menu_bundles.push_back (b);
1057 MenuList& citems = input_menu.items();
1059 std::string n = b->name ();
1060 replace_all (n, "_", " ");
1062 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1066 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1068 using namespace Menu_Helpers;
1070 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1074 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1075 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1079 if (i != output_menu_bundles.end()) {
1083 output_menu_bundles.push_back (b);
1085 MenuList& citems = output_menu.items();
1087 std::string n = b->name ();
1088 replace_all (n, "_", " ");
1090 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1094 MixerStrip::update_diskstream_display ()
1096 if (is_track() && input_selector) {
1097 input_selector->hide_all ();
1100 route_color_changed ();
1104 MixerStrip::connect_to_pan ()
1106 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1108 panstate_connection.disconnect ();
1109 panstyle_connection.disconnect ();
1111 if (!_route->panner()) {
1115 boost::shared_ptr<Pannable> p = _route->pannable ();
1117 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1118 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1120 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1121 * However, that only works a panner was previously set.
1123 * PannerUI must remain subscribed to _panshell->Changed() in case
1124 * we switch the panner eg. AUX-Send and back
1125 * _route->panner_shell()->Changed() vs _panshell->Changed
1127 if (panners._panner == 0) {
1128 panners.panshell_changed ();
1130 update_panner_choices();
1134 MixerStrip::update_panner_choices ()
1136 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1137 if (!_route->panner_shell()) { return; }
1139 uint32_t in = _route->output()->n_ports().n_audio();
1141 if (_route->panner()) {
1142 in = _route->panner()->in().n_audio();
1145 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1149 * Output port labelling
1150 * =====================
1152 * Case 1: Each output has one connection, all connections are to system:playback_%i
1153 * out 1 -> system:playback_1
1154 * out 2 -> system:playback_2
1155 * out 3 -> system:playback_3
1158 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1159 * out 1 -> ardour:track_x/in 1
1160 * out 2 -> ardour:track_x/in 2
1161 * Display as: track_x
1163 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1164 * out 1 -> program x:foo
1165 * out 2 -> program x:foo
1166 * Display as: program x
1168 * Case 4: No connections (Disconnected)
1171 * Default case (unusual routing):
1172 * Display as: *number of connections*
1176 * .-----------------------------------------------.
1178 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1179 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1180 * '-----------------------------------------------'
1181 * .-----------------------------------------------.
1184 * '-----------------------------------------------'
1188 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1192 boost::shared_ptr<Port> port;
1193 vector<string> port_connections;
1195 uint32_t total_connection_count = 0;
1196 uint32_t io_connection_count = 0;
1197 uint32_t ardour_connection_count = 0;
1198 uint32_t system_connection_count = 0;
1199 uint32_t other_connection_count = 0;
1200 uint32_t typed_connection_count = 0;
1202 ostringstream label;
1204 bool have_label = false;
1205 bool each_io_has_one_connection = true;
1207 string connection_name;
1208 string ardour_track_name;
1209 string other_connection_type;
1210 string system_ports;
1213 ostringstream tooltip;
1214 char * tooltip_cstr;
1216 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1217 DataType dt = DataType::AUDIO;
1218 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1219 dt = DataType::MIDI;
1220 // avoid further confusion with Midi-tracks that have a synth.
1221 // Audio-ports may be connected, but button says "Disconnected"
1222 tooltip << _("MIDI ");
1226 io_count = route->n_inputs().n_total();
1227 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1229 io_count = route->n_outputs().n_total();
1230 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1234 for (io_index = 0; io_index < io_count; ++io_index) {
1236 port = route->input()->nth (io_index);
1238 port = route->output()->nth (io_index);
1241 port_connections.clear ();
1242 port->get_connections(port_connections);
1244 //ignore any port connections that don't match our DataType
1245 if (port->type() != dt) {
1246 if (!port_connections.empty()) {
1247 ++typed_connection_count;
1252 io_connection_count = 0;
1254 if (!port_connections.empty()) {
1255 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1257 string& connection_name (*i);
1259 if (connection_name.find("system:") == 0) {
1260 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1263 if (io_connection_count == 0) {
1264 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1266 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1269 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1272 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1273 if (ardour_track_name.empty()) {
1274 // "ardour:Master/in 1" -> "ardour:Master/"
1275 string::size_type slash = connection_name.find("/");
1276 if (slash != string::npos) {
1277 ardour_track_name = connection_name.substr(0, slash + 1);
1281 if (connection_name.find(ardour_track_name) == 0) {
1282 ++ardour_connection_count;
1284 } else if (!pn.empty()) {
1285 if (system_ports.empty()) {
1288 system_ports += "/" + pn;
1290 if (connection_name.find("system:") == 0) {
1291 ++system_connection_count;
1293 } else if (connection_name.find("system:midi_") == 0) {
1295 // "system:midi_capture_123" -> "123"
1296 system_port = "M " + connection_name.substr(20);
1298 // "system:midi_playback_123" -> "123"
1299 system_port = "M " + connection_name.substr(21);
1302 if (system_ports.empty()) {
1303 system_ports += system_port;
1305 system_ports += "/" + system_port;
1308 ++system_connection_count;
1310 } else if (connection_name.find("system:") == 0) {
1312 // "system:capture_123" -> "123"
1313 system_port = connection_name.substr(15);
1315 // "system:playback_123" -> "123"
1316 system_port = connection_name.substr(16);
1319 if (system_ports.empty()) {
1320 system_ports += system_port;
1322 system_ports += "/" + system_port;
1325 ++system_connection_count;
1327 if (other_connection_type.empty()) {
1328 // "jamin:in 1" -> "jamin:"
1329 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1332 if (connection_name.find(other_connection_type) == 0) {
1333 ++other_connection_count;
1337 ++total_connection_count;
1338 ++io_connection_count;
1342 if (io_connection_count != 1) {
1343 each_io_has_one_connection = false;
1347 if (total_connection_count == 0) {
1348 tooltip << endl << _("Disconnected");
1351 tooltip_cstr = new char[tooltip.str().size() + 1];
1352 strcpy(tooltip_cstr, tooltip.str().c_str());
1355 set_tooltip (&input_button, tooltip_cstr);
1357 set_tooltip (&output_button, tooltip_cstr);
1360 delete [] tooltip_cstr;
1362 if (each_io_has_one_connection) {
1363 if (total_connection_count == ardour_connection_count) {
1364 // all connections are to the same track in ardour
1365 // "ardour:Master/" -> "Master"
1366 string::size_type slash = ardour_track_name.find("/");
1367 if (slash != string::npos) {
1368 label << ardour_track_name.substr(7, slash - 7);
1372 else if (total_connection_count == system_connection_count) {
1373 // all connections are to system ports
1374 label << system_ports;
1377 else if (total_connection_count == other_connection_count) {
1378 // all connections are to the same external program eg jamin
1379 // "jamin:" -> "jamin"
1380 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1386 if (total_connection_count == 0) {
1390 // Odd configuration
1391 label << "*" << total_connection_count << "*";
1393 if (typed_connection_count > 0) {
1394 label << "\u2295"; // circled plus
1399 input_button.set_text (label.str());
1401 output_button.set_text (label.str());
1406 MixerStrip::update_input_display ()
1408 update_io_button (_route, _width, true);
1409 panners.setup_pan ();
1411 if (has_audio_outputs ()) {
1412 panners.show_all ();
1414 panners.hide_all ();
1420 MixerStrip::update_output_display ()
1422 update_io_button (_route, _width, false);
1423 gpm.setup_meters ();
1424 panners.setup_pan ();
1426 if (has_audio_outputs ()) {
1427 panners.show_all ();
1429 panners.hide_all ();
1434 MixerStrip::fast_update ()
1436 gpm.update_meters ();
1440 MixerStrip::diskstream_changed ()
1442 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1446 MixerStrip::io_changed_proxy ()
1448 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1452 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1454 boost::shared_ptr<Port> a = wa.lock ();
1455 boost::shared_ptr<Port> b = wb.lock ();
1457 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1458 update_input_display ();
1459 set_width_enum (_width, this);
1462 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1463 update_output_display ();
1464 set_width_enum (_width, this);
1469 MixerStrip::setup_comment_button ()
1474 if (_route->comment().empty ()) {
1475 _comment_button.unset_bg (STATE_NORMAL);
1476 _comment_button.set_text (_("Comments"));
1478 _comment_button.modify_bg (STATE_NORMAL, color ());
1479 _comment_button.set_text (_("*Comments*"));
1484 if (_route->comment().empty ()) {
1485 _comment_button.unset_bg (STATE_NORMAL);
1486 _comment_button.set_text (_("Cmt"));
1488 _comment_button.modify_bg (STATE_NORMAL, color ());
1489 _comment_button.set_text (_("*Cmt*"));
1495 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1501 MixerStrip::select_route_group (GdkEventButton *ev)
1503 using namespace Menu_Helpers;
1505 if (ev->button == 1) {
1507 if (group_menu == 0) {
1509 PropertyList* plist = new PropertyList();
1511 plist->add (Properties::gain, true);
1512 plist->add (Properties::mute, true);
1513 plist->add (Properties::solo, true);
1515 group_menu = new RouteGroupMenu (_session, plist);
1519 r.push_back (route ());
1520 group_menu->build (r);
1521 group_menu->menu()->popup (1, ev->time);
1528 MixerStrip::route_group_changed ()
1530 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1532 RouteGroup *rg = _route->route_group();
1535 group_button.set_text (PBD::short_version (rg->name(), 5));
1539 group_button.set_text (_("Grp"));
1542 group_button.set_text (_("~G"));
1549 MixerStrip::route_color_changed ()
1551 name_button.modify_bg (STATE_NORMAL, color());
1552 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1553 reset_strip_style ();
1557 MixerStrip::show_passthru_color ()
1559 reset_strip_style ();
1563 MixerStrip::build_route_ops_menu ()
1565 using namespace Menu_Helpers;
1566 route_ops_menu = new Menu;
1567 route_ops_menu->set_name ("ArdourContextMenu");
1569 MenuList& items = route_ops_menu->items();
1571 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1573 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1575 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1577 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1579 items.push_back (SeparatorElem());
1581 if (!_route->is_master()) {
1582 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1584 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1585 rename_menu_item = &items.back();
1587 items.push_back (SeparatorElem());
1588 items.push_back (CheckMenuElem (_("Active")));
1589 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1590 i->set_active (_route->active());
1591 i->set_sensitive(! _session->transport_rolling());
1592 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1594 items.push_back (SeparatorElem());
1595 items.push_back (CheckMenuElem (_("Strict I/O")));
1596 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1597 i->set_active (_route->strict_io());
1598 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1600 items.push_back (SeparatorElem());
1602 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1604 items.push_back (SeparatorElem());
1605 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1606 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1607 denormal_menu_item->set_active (_route->denormal_protection());
1609 items.push_back (SeparatorElem());
1610 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1613 /* note that this relies on selection being shared across editor and
1614 mixer (or global to the backend, in the future), which is the only
1615 sane thing for users anyway.
1618 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1620 Selection& selection (PublicEditor::instance().get_selection());
1621 if (!selection.selected (rtav)) {
1622 selection.set (rtav);
1625 if (!_route->is_master()) {
1626 items.push_back (SeparatorElem());
1627 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1630 items.push_back (SeparatorElem());
1631 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1637 MixerStrip::name_button_button_press (GdkEventButton* ev)
1639 if (ev->button == 3) {
1640 list_route_operations ();
1642 /* do not allow rename if the track is record-enabled */
1643 rename_menu_item->set_sensitive (!_route->record_enabled());
1644 route_ops_menu->popup (1, ev->time);
1653 MixerStrip::name_button_button_release (GdkEventButton* ev)
1655 if (ev->button == 1) {
1656 list_route_operations ();
1658 /* do not allow rename if the track is record-enabled */
1659 rename_menu_item->set_sensitive (!_route->record_enabled());
1660 route_ops_menu->popup (1, ev->time);
1667 MixerStrip::number_button_button_press (GdkEventButton* ev)
1669 if ( ev->button == 3 ) {
1670 list_route_operations ();
1672 /* do not allow rename if the track is record-enabled */
1673 rename_menu_item->set_sensitive (!_route->record_enabled());
1674 route_ops_menu->popup (1, ev->time);
1683 MixerStrip::list_route_operations ()
1685 delete route_ops_menu;
1686 build_route_ops_menu ();
1690 MixerStrip::set_selected (bool yn)
1692 AxisView::set_selected (yn);
1694 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1695 global_frame.set_name ("MixerStripSelectedFrame");
1697 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1698 global_frame.set_name ("MixerStripFrame");
1700 global_frame.queue_draw ();
1703 // processor_box.deselect_all_processors();
1707 MixerStrip::property_changed (const PropertyChange& what_changed)
1709 RouteUI::property_changed (what_changed);
1711 if (what_changed.contains (ARDOUR::Properties::name)) {
1717 MixerStrip::name_changed ()
1721 name_button.set_text (_route->name());
1724 name_button.set_text (PBD::short_version (_route->name(), 5));
1728 set_tooltip (name_button, _route->name());
1730 if (_session->config.get_track_name_number()) {
1731 const int64_t track_number = _route->track_number ();
1732 if (track_number == 0) {
1733 number_label.set_text ("-");
1735 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1738 number_label.set_text ("");
1743 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1745 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1749 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1751 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1755 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1757 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1761 MixerStrip::width_button_pressed (GdkEventButton* ev)
1763 if (ev->button != 1) {
1767 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1770 _mixer.set_strip_width (Narrow, true);
1774 _mixer.set_strip_width (Wide, true);
1780 set_width_enum (Narrow, this);
1783 set_width_enum (Wide, this);
1792 MixerStrip::hide_clicked ()
1794 // LAME fix to reset the button status for when it is redisplayed (part 1)
1795 hide_button.set_sensitive(false);
1798 Hiding(); /* EMIT_SIGNAL */
1800 _mixer.hide_strip (this);
1804 hide_button.set_sensitive(true);
1808 MixerStrip::set_embedded (bool yn)
1814 MixerStrip::map_frozen ()
1816 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1818 boost::shared_ptr<AudioTrack> at = audio_track();
1821 switch (at->freeze_state()) {
1822 case AudioTrack::Frozen:
1823 processor_box.set_sensitive (false);
1824 hide_redirect_editors ();
1827 processor_box.set_sensitive (true);
1828 // XXX need some way, maybe, to retoggle redirect editors
1832 processor_box.set_sensitive (true);
1837 MixerStrip::hide_redirect_editors ()
1839 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1843 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1845 boost::shared_ptr<Processor> processor (p.lock ());
1850 Gtk::Window* w = processor_box.get_processor_ui (processor);
1858 MixerStrip::reset_strip_style ()
1860 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1862 gpm.set_fader_name ("SendStripBase");
1866 if (is_midi_track()) {
1867 if (_route->active()) {
1868 set_name ("MidiTrackStripBase");
1870 set_name ("MidiTrackStripBaseInactive");
1872 gpm.set_fader_name ("MidiTrackFader");
1873 } else if (is_audio_track()) {
1874 if (_route->active()) {
1875 set_name ("AudioTrackStripBase");
1877 set_name ("AudioTrackStripBaseInactive");
1879 gpm.set_fader_name ("AudioTrackFader");
1881 if (_route->active()) {
1882 set_name ("AudioBusStripBase");
1884 set_name ("AudioBusStripBaseInactive");
1886 gpm.set_fader_name ("AudioBusFader");
1888 /* (no MIDI busses yet) */
1895 MixerStrip::engine_stopped ()
1900 MixerStrip::engine_running ()
1905 MixerStrip::meter_point_string (MeterPoint mp)
1918 case MeterPostFader:
1935 return S_("Meter|In");
1939 return S_("Meter|Pr");
1942 case MeterPostFader:
1943 return S_("Meter|Po");
1947 return S_("Meter|O");
1952 return S_("Meter|C");
1961 /** Called when the monitor-section state */
1963 MixerStrip::monitor_changed ()
1965 assert (monitor_section_button);
1966 if (_session->monitor_active()) {
1967 monitor_section_button->set_name ("master monitor section button active");
1969 monitor_section_button->set_name ("master monitor section button normal");
1973 /** Called when the metering point has changed */
1975 MixerStrip::meter_changed ()
1977 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1978 gpm.setup_meters ();
1979 // reset peak when meter point changes
1980 gpm.reset_peak_display();
1983 /** The bus that we are displaying sends to has changed, or been turned off.
1984 * @param send_to New bus that we are displaying sends to, or 0.
1987 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1989 RouteUI::bus_send_display_changed (send_to);
1992 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1997 revert_to_default_display ();
2000 revert_to_default_display ();
2005 MixerStrip::drop_send ()
2007 boost::shared_ptr<Send> current_send;
2009 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2010 current_send->set_metering (false);
2013 send_gone_connection.disconnect ();
2014 input_button.set_sensitive (true);
2015 output_button.set_sensitive (true);
2016 group_button.set_sensitive (true);
2017 set_invert_sensitive (true);
2018 meter_point_button.set_sensitive (true);
2019 mute_button->set_sensitive (true);
2020 solo_button->set_sensitive (true);
2021 rec_enable_button->set_sensitive (true);
2022 solo_isolated_led->set_sensitive (true);
2023 solo_safe_led->set_sensitive (true);
2024 monitor_input_button->set_sensitive (true);
2025 monitor_disk_button->set_sensitive (true);
2026 _comment_button.set_sensitive (true);
2030 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2032 _current_delivery = d;
2033 DeliveryChanged (_current_delivery);
2037 MixerStrip::show_send (boost::shared_ptr<Send> send)
2043 set_current_delivery (send);
2045 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2046 send->set_metering (true);
2047 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2049 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2050 gain_meter().setup_meters ();
2052 uint32_t const in = _current_delivery->pans_required();
2053 uint32_t const out = _current_delivery->pan_outs();
2055 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2056 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2057 panner_ui().setup_pan ();
2058 panner_ui().set_send_drawing_mode (true);
2059 panner_ui().show_all ();
2061 input_button.set_sensitive (false);
2062 group_button.set_sensitive (false);
2063 set_invert_sensitive (false);
2064 meter_point_button.set_sensitive (false);
2065 mute_button->set_sensitive (false);
2066 solo_button->set_sensitive (false);
2067 rec_enable_button->set_sensitive (false);
2068 solo_isolated_led->set_sensitive (false);
2069 solo_safe_led->set_sensitive (false);
2070 monitor_input_button->set_sensitive (false);
2071 monitor_disk_button->set_sensitive (false);
2072 _comment_button.set_sensitive (false);
2074 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2075 output_button.set_sensitive (false);
2078 reset_strip_style ();
2082 MixerStrip::revert_to_default_display ()
2086 set_current_delivery (_route->main_outs ());
2088 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2089 gain_meter().setup_meters ();
2091 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2092 update_panner_choices();
2093 panner_ui().setup_pan ();
2094 panner_ui().set_send_drawing_mode (false);
2096 if (has_audio_outputs ()) {
2097 panners.show_all ();
2099 panners.hide_all ();
2102 reset_strip_style ();
2106 MixerStrip::set_button_names ()
2110 mute_button->set_text (_("Mute"));
2111 monitor_input_button->set_text (_("In"));
2112 monitor_disk_button->set_text (_("Disk"));
2113 if (monitor_section_button) {
2114 monitor_section_button->set_text (_("Mon"));
2117 if (_route && _route->solo_safe()) {
2118 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2120 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2122 if (!Config->get_solo_control_is_listen_control()) {
2123 solo_button->set_text (_("Solo"));
2125 switch (Config->get_listen_position()) {
2126 case AfterFaderListen:
2127 solo_button->set_text (_("AFL"));
2129 case PreFaderListen:
2130 solo_button->set_text (_("PFL"));
2134 solo_isolated_led->set_text (_("Iso"));
2135 solo_safe_led->set_text (S_("SoloLock|Lock"));
2139 mute_button->set_text (S_("Mute|M"));
2140 monitor_input_button->set_text (S_("MonitorInput|I"));
2141 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2142 if (monitor_section_button) {
2143 monitor_section_button->set_text (S_("Mon|O"));
2146 if (_route && _route->solo_safe()) {
2147 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2149 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2151 if (!Config->get_solo_control_is_listen_control()) {
2152 solo_button->set_text (S_("Solo|S"));
2154 switch (Config->get_listen_position()) {
2155 case AfterFaderListen:
2156 solo_button->set_text (S_("AfterFader|A"));
2158 case PreFaderListen:
2159 solo_button->set_text (S_("Prefader|P"));
2164 solo_isolated_led->set_text (S_("SoloIso|I"));
2165 solo_safe_led->set_text (S_("SoloLock|L"));
2170 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2172 meter_point_button.set_text ("");
2177 MixerStrip::plugin_selector()
2179 return _mixer.plugin_selector();
2183 MixerStrip::hide_things ()
2185 processor_box.hide_things ();
2189 MixerStrip::input_active_button_press (GdkEventButton*)
2191 /* nothing happens on press */
2196 MixerStrip::input_active_button_release (GdkEventButton* ev)
2198 boost::shared_ptr<MidiTrack> mt = midi_track ();
2204 boost::shared_ptr<RouteList> rl (new RouteList);
2206 rl->push_back (route());
2208 _session->set_exclusive_input_active (rl, !mt->input_active(),
2209 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2215 MixerStrip::midi_input_status_changed ()
2217 if (midi_input_enable_button) {
2218 boost::shared_ptr<MidiTrack> mt = midi_track ();
2220 midi_input_enable_button->set_active (mt->input_active ());
2225 MixerStrip::state_id () const
2227 return string_compose ("strip %1", _route->id().to_s());
2231 MixerStrip::parameter_changed (string p)
2233 if (p == _visibility.get_state_name()) {
2234 /* The user has made changes to the mixer strip visibility, so get
2235 our VisibilityGroup to reflect these changes in our widgets.
2237 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2239 else if (p == "track-name-number") {
2242 else if (p == "use-monitor-bus") {
2243 if (monitor_section_button) {
2244 if (mute_button->get_parent()) {
2245 mute_button->get_parent()->remove(*mute_button);
2247 if (monitor_section_button->get_parent()) {
2248 monitor_section_button->get_parent()->remove(*monitor_section_button);
2250 if (Config->get_use_monitor_bus ()) {
2251 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2252 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2253 mute_button->show();
2254 monitor_section_button->show();
2256 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2257 mute_button->show();
2263 /** Called to decide whether the solo isolate / solo lock button visibility should
2264 * be overridden from that configured by the user. We do this for the master bus.
2266 * @return optional value that is present if visibility state should be overridden.
2268 boost::optional<bool>
2269 MixerStrip::override_solo_visibility () const
2271 if (_route && _route->is_master ()) {
2272 return boost::optional<bool> (false);
2275 return boost::optional<bool> ();
2279 MixerStrip::add_input_port (DataType t)
2281 _route->input()->add_port ("", this, t);
2285 MixerStrip::add_output_port (DataType t)
2287 _route->output()->add_port ("", this, t);
2291 MixerStrip::route_active_changed ()
2293 reset_strip_style ();
2297 MixerStrip::copy_processors ()
2299 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2303 MixerStrip::cut_processors ()
2305 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2309 MixerStrip::paste_processors ()
2311 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2315 MixerStrip::select_all_processors ()
2317 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2321 MixerStrip::deselect_all_processors ()
2323 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2327 MixerStrip::delete_processors ()
2329 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2333 MixerStrip::toggle_processors ()
2335 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2339 MixerStrip::ab_plugins ()
2341 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2345 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2347 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2350 if (ev->button == 3) {
2351 popup_level_meter_menu (ev);
2359 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2361 using namespace Gtk::Menu_Helpers;
2363 Gtk::Menu* m = manage (new Menu);
2364 MenuList& items = m->items ();
2366 RadioMenuItem::Group group;
2368 _suspend_menu_callbacks = true;
2369 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2370 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2371 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2372 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2373 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2375 if (gpm.meter_channels().n_audio() == 0) {
2376 m->popup (ev->button, ev->time);
2377 _suspend_menu_callbacks = false;
2381 RadioMenuItem::Group tgroup;
2382 items.push_back (SeparatorElem());
2384 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2385 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2386 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2387 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2388 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2389 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2390 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2391 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2392 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2393 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2394 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2397 if (_route->is_master()) {
2400 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2401 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2402 /* non-master bus */
2405 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2412 MeterType cmt = _route->meter_type();
2413 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2415 items.push_back (SeparatorElem());
2416 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2417 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2418 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2419 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2420 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2421 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2423 m->popup (ev->button, ev->time);
2424 _suspend_menu_callbacks = false;
2428 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2429 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2431 using namespace Menu_Helpers;
2433 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2434 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2435 i->set_active (_route->meter_point() == point);
2439 MixerStrip::set_meter_point (MeterPoint p)
2441 if (_suspend_menu_callbacks) return;
2442 _route->set_meter_point (p);
2446 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2447 RadioMenuItem::Group& group, string const & name, MeterType type)
2449 using namespace Menu_Helpers;
2451 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2452 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2453 i->set_active (_route->meter_type() == type);
2457 MixerStrip::set_meter_type (MeterType t)
2459 if (_suspend_menu_callbacks) return;