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 monitor_section_button = manage (new ArdourButton);
505 monitor_section_button->set_name ("master monitor section button");
506 monitor_section_button->set_related_action (act);
507 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
508 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
509 monitor_section_button->show();
510 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
512 parameter_changed ("use-monitor-bus");
514 bottom_button_table.attach (group_button, 1, 2, 0, 1);
515 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
516 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
517 mute_button->show ();
518 solo_button->show ();
519 rec_mon_table.show ();
522 if (_mixer_owned && route()->is_master() ) {
524 HScrollbar scrollbar;
525 Gtk::Requisition requisition(scrollbar.size_request ());
526 int scrollbar_height = requisition.height;
528 spacer = manage (new EventBox);
529 spacer->set_size_request (-1, scrollbar_height+2);
530 global_vpacker.pack_start (*spacer, false, false);
535 monitor_input_button->show ();
536 monitor_disk_button->show ();
538 monitor_input_button->hide();
539 monitor_disk_button->hide ();
542 if (route()->trim() && route()->trim()->active()) {
543 trim_control.show ();
544 trim_control.set_controllable (route()->trim()->gain_control());
546 trim_control.hide ();
547 boost::shared_ptr<Controllable> none;
548 trim_control.set_controllable (none);
551 if (is_midi_track()) {
552 if (midi_input_enable_button == 0) {
553 midi_input_enable_button = manage (new ArdourButton);
554 midi_input_enable_button->set_name ("midi input button");
555 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
556 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
557 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
558 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
559 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
561 input_button_box.remove (*midi_input_enable_button);
563 /* get current state */
564 midi_input_status_changed ();
565 input_button_box.pack_start (*midi_input_enable_button, false, false);
567 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
569 if (midi_input_enable_button) {
570 /* removal from the container will delete it */
571 input_button_box.remove (*midi_input_enable_button);
572 midi_input_enable_button = 0;
576 if (is_audio_track()) {
577 boost::shared_ptr<AudioTrack> at = audio_track();
578 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
583 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
584 rec_enable_button->set_sensitive (_session->writable());
585 rec_enable_button->show();
587 if (ARDOUR::Profile->get_mixbus()) {
588 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
589 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
590 } else if (ARDOUR::Profile->get_trx()) {
591 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
593 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
594 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
601 if (!_route->is_master()) {
602 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
603 show_sends_button->show();
607 meter_point_button.set_text (meter_point_string (_route->meter_point()));
609 delete route_ops_menu;
612 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
613 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
614 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
615 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
617 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
619 if (_route->panner_shell()) {
620 update_panner_choices();
621 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
624 if (is_audio_track()) {
625 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
628 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
629 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
631 set_stuff_from_route ();
633 /* now force an update of all the various elements */
635 update_mute_display ();
636 update_solo_display ();
639 route_group_changed ();
642 panners.setup_pan ();
644 if (has_audio_outputs ()) {
650 update_diskstream_display ();
651 update_input_display ();
652 update_output_display ();
654 add_events (Gdk::BUTTON_RELEASE_MASK);
656 processor_box.show ();
658 if (!route()->is_master() && !route()->is_monitor()) {
659 /* we don't allow master or control routes to be hidden */
664 gpm.reset_peak_display ();
665 gpm.gain_display.show ();
666 gpm.peak_display.show ();
669 width_hide_box.show();
671 global_vpacker.show();
672 mute_solo_table.show();
673 bottom_button_table.show();
675 meter_point_button.show();
676 input_button_box.show_all();
677 output_button.show();
679 _comment_button.show();
681 gpm.gain_automation_state_button.show();
683 parameter_changed ("mixer-element-visibility");
690 MixerStrip::set_stuff_from_route ()
692 /* if width is not set, it will be set by the MixerUI or editor */
694 string str = gui_property ("strip-width");
696 set_width_enum (Width (string_2_enum (str, _width)), this);
701 MixerStrip::set_width_enum (Width w, void* owner)
703 /* always set the gpm width again, things may be hidden */
706 panners.set_width (w);
708 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
710 _width_owner = owner;
714 if (_width_owner == this) {
715 set_gui_property ("strip-width", enum_2_string (_width));
720 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
725 if (show_sends_button) {
726 show_sends_button->set_text (_("Aux"));
729 gpm.gain_automation_style_button.set_text (
730 gpm.astyle_string(gain_automation->automation_style()));
731 gpm.gain_automation_state_button.set_text (
732 gpm.astate_string(gain_automation->automation_state()));
734 if (_route->panner()) {
735 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
736 panners.astyle_string(_route->panner()->automation_style()));
737 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
738 panners.astate_string(_route->panner()->automation_state()));
742 // panners expect an even number of horiz. pixels
743 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
745 set_size_request (width, -1);
751 if (show_sends_button) {
752 show_sends_button->set_text (_("Snd"));
755 gpm.gain_automation_style_button.set_text (
756 gpm.short_astyle_string(gain_automation->automation_style()));
757 gpm.gain_automation_state_button.set_text (
758 gpm.short_astate_string(gain_automation->automation_state()));
759 gain_meter().setup_meters (); // recalc meter width
761 if (_route->panner()) {
762 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
763 panners.short_astyle_string(_route->panner()->automation_style()));
764 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
765 panners.short_astate_string(_route->panner()->automation_state()));
769 // panners expect an even number of horiz. pixels
770 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
772 set_size_request (width, -1);
777 processor_box.set_width (w);
779 update_input_display ();
780 update_output_display ();
781 setup_comment_button ();
782 route_group_changed ();
788 MixerStrip::set_packed (bool yn)
793 set_gui_property ("visible", true);
795 set_gui_property ("visible", false);
800 struct RouteCompareByName {
801 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
802 return a->name().compare (b->name()) < 0;
807 MixerStrip::output_release (GdkEventButton *ev)
809 switch (ev->button) {
811 edit_output_configuration ();
819 MixerStrip::output_press (GdkEventButton *ev)
821 using namespace Menu_Helpers;
822 if (!_session->engine().connected()) {
823 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
828 MenuList& citems = output_menu.items();
829 switch (ev->button) {
832 return false; //wait for the mouse-up to pop the dialog
836 output_menu.set_name ("ArdourContextMenu");
838 output_menu_bundles.clear ();
840 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
842 citems.push_back (SeparatorElem());
843 uint32_t const n_with_separator = citems.size ();
845 ARDOUR::BundleList current = _route->output()->bundles_connected ();
847 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
849 /* give user bundles first chance at being in the menu */
851 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
852 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
853 maybe_add_bundle_to_output_menu (*i, current);
857 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
858 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
859 maybe_add_bundle_to_output_menu (*i, current);
863 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
864 RouteList copy = *routes;
865 copy.sort (RouteCompareByName ());
866 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
867 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
870 if (citems.size() == n_with_separator) {
871 /* no routes added; remove the separator */
875 citems.push_back (SeparatorElem());
877 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
880 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
881 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
886 citems.push_back (SeparatorElem());
887 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
889 output_menu.popup (1, ev->time);
900 MixerStrip::input_release (GdkEventButton *ev)
902 switch (ev->button) {
905 edit_input_configuration ();
917 MixerStrip::input_press (GdkEventButton *ev)
919 using namespace Menu_Helpers;
921 MenuList& citems = input_menu.items();
922 input_menu.set_name ("ArdourContextMenu");
925 if (!_session->engine().connected()) {
926 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
931 if (_session->actively_recording() && _route->record_enabled())
934 switch (ev->button) {
937 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
941 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
943 citems.push_back (SeparatorElem());
944 uint32_t const n_with_separator = citems.size ();
946 input_menu_bundles.clear ();
948 ARDOUR::BundleList current = _route->input()->bundles_connected ();
950 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
952 /* give user bundles first chance at being in the menu */
954 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
955 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
956 maybe_add_bundle_to_input_menu (*i, current);
960 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
961 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
962 maybe_add_bundle_to_input_menu (*i, current);
966 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
967 RouteList copy = *routes;
968 copy.sort (RouteCompareByName ());
969 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
970 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
973 if (citems.size() == n_with_separator) {
974 /* no routes added; remove the separator */
978 citems.push_back (SeparatorElem());
979 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
982 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
983 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
988 citems.push_back (SeparatorElem());
989 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
991 input_menu.popup (1, ev->time);
1002 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1004 if (ignore_toggle) {
1008 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1010 if (std::find (current.begin(), current.end(), c) == current.end()) {
1011 _route->input()->connect_ports_to_bundle (c, true, this);
1013 _route->input()->disconnect_ports_from_bundle (c, this);
1018 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1020 if (ignore_toggle) {
1024 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1026 if (std::find (current.begin(), current.end(), c) == current.end()) {
1027 _route->output()->connect_ports_to_bundle (c, true, this);
1029 _route->output()->disconnect_ports_from_bundle (c, this);
1034 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1036 using namespace Menu_Helpers;
1038 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1042 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1043 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1047 if (i != input_menu_bundles.end()) {
1051 input_menu_bundles.push_back (b);
1053 MenuList& citems = input_menu.items();
1055 std::string n = b->name ();
1056 replace_all (n, "_", " ");
1058 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1062 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1064 using namespace Menu_Helpers;
1066 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1070 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1071 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1075 if (i != output_menu_bundles.end()) {
1079 output_menu_bundles.push_back (b);
1081 MenuList& citems = output_menu.items();
1083 std::string n = b->name ();
1084 replace_all (n, "_", " ");
1086 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1090 MixerStrip::update_diskstream_display ()
1092 if (is_track() && input_selector) {
1093 input_selector->hide_all ();
1096 route_color_changed ();
1100 MixerStrip::connect_to_pan ()
1102 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1104 panstate_connection.disconnect ();
1105 panstyle_connection.disconnect ();
1107 if (!_route->panner()) {
1111 boost::shared_ptr<Pannable> p = _route->pannable ();
1113 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1114 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1116 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1117 * However, that only works a panner was previously set.
1119 * PannerUI must remain subscribed to _panshell->Changed() in case
1120 * we switch the panner eg. AUX-Send and back
1121 * _route->panner_shell()->Changed() vs _panshell->Changed
1123 if (panners._panner == 0) {
1124 panners.panshell_changed ();
1126 update_panner_choices();
1130 MixerStrip::update_panner_choices ()
1132 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1133 if (!_route->panner_shell()) { return; }
1135 uint32_t in = _route->output()->n_ports().n_audio();
1137 if (_route->panner()) {
1138 in = _route->panner()->in().n_audio();
1141 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1145 * Output port labelling
1146 * =====================
1148 * Case 1: Each output has one connection, all connections are to system:playback_%i
1149 * out 1 -> system:playback_1
1150 * out 2 -> system:playback_2
1151 * out 3 -> system:playback_3
1154 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1155 * out 1 -> ardour:track_x/in 1
1156 * out 2 -> ardour:track_x/in 2
1157 * Display as: track_x
1159 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1160 * out 1 -> program x:foo
1161 * out 2 -> program x:foo
1162 * Display as: program x
1164 * Case 4: No connections (Disconnected)
1167 * Default case (unusual routing):
1168 * Display as: *number of connections*
1172 * .-----------------------------------------------.
1174 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1175 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1176 * '-----------------------------------------------'
1177 * .-----------------------------------------------.
1180 * '-----------------------------------------------'
1184 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1188 boost::shared_ptr<Port> port;
1189 vector<string> port_connections;
1191 uint32_t total_connection_count = 0;
1192 uint32_t io_connection_count = 0;
1193 uint32_t ardour_connection_count = 0;
1194 uint32_t system_connection_count = 0;
1195 uint32_t other_connection_count = 0;
1196 uint32_t typed_connection_count = 0;
1198 ostringstream label;
1200 bool have_label = false;
1201 bool each_io_has_one_connection = true;
1203 string connection_name;
1204 string ardour_track_name;
1205 string other_connection_type;
1206 string system_ports;
1209 ostringstream tooltip;
1210 char * tooltip_cstr;
1212 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1213 DataType dt = DataType::AUDIO;
1214 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1215 dt = DataType::MIDI;
1216 // avoid further confusion with Midi-tracks that have a synth.
1217 // Audio-ports may be connected, but button says "Disconnected"
1218 tooltip << _("MIDI ");
1222 io_count = route->n_inputs().n_total();
1223 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1225 io_count = route->n_outputs().n_total();
1226 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1230 for (io_index = 0; io_index < io_count; ++io_index) {
1232 port = route->input()->nth (io_index);
1234 port = route->output()->nth (io_index);
1237 port_connections.clear ();
1238 port->get_connections(port_connections);
1240 //ignore any port connections that don't match our DataType
1241 if (port->type() != dt) {
1242 if (!port_connections.empty()) {
1243 ++typed_connection_count;
1248 io_connection_count = 0;
1250 if (!port_connections.empty()) {
1251 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1253 string& connection_name (*i);
1255 if (connection_name.find("system:") == 0) {
1256 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1259 if (io_connection_count == 0) {
1260 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1262 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1265 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1268 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1269 if (ardour_track_name.empty()) {
1270 // "ardour:Master/in 1" -> "ardour:Master/"
1271 string::size_type slash = connection_name.find("/");
1272 if (slash != string::npos) {
1273 ardour_track_name = connection_name.substr(0, slash + 1);
1277 if (connection_name.find(ardour_track_name) == 0) {
1278 ++ardour_connection_count;
1280 } else if (!pn.empty()) {
1281 if (system_ports.empty()) {
1284 system_ports += "/" + pn;
1286 if (connection_name.find("system:") == 0) {
1287 ++system_connection_count;
1289 } else if (connection_name.find("system:midi_") == 0) {
1291 // "system:midi_capture_123" -> "123"
1292 system_port = "M " + connection_name.substr(20);
1294 // "system:midi_playback_123" -> "123"
1295 system_port = "M " + connection_name.substr(21);
1298 if (system_ports.empty()) {
1299 system_ports += system_port;
1301 system_ports += "/" + system_port;
1304 ++system_connection_count;
1306 } else if (connection_name.find("system:") == 0) {
1308 // "system:capture_123" -> "123"
1309 system_port = connection_name.substr(15);
1311 // "system:playback_123" -> "123"
1312 system_port = connection_name.substr(16);
1315 if (system_ports.empty()) {
1316 system_ports += system_port;
1318 system_ports += "/" + system_port;
1321 ++system_connection_count;
1323 if (other_connection_type.empty()) {
1324 // "jamin:in 1" -> "jamin:"
1325 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1328 if (connection_name.find(other_connection_type) == 0) {
1329 ++other_connection_count;
1333 ++total_connection_count;
1334 ++io_connection_count;
1338 if (io_connection_count != 1) {
1339 each_io_has_one_connection = false;
1343 if (total_connection_count == 0) {
1344 tooltip << endl << _("Disconnected");
1347 tooltip_cstr = new char[tooltip.str().size() + 1];
1348 strcpy(tooltip_cstr, tooltip.str().c_str());
1351 set_tooltip (&input_button, tooltip_cstr);
1353 set_tooltip (&output_button, tooltip_cstr);
1356 if (each_io_has_one_connection) {
1357 if (total_connection_count == ardour_connection_count) {
1358 // all connections are to the same track in ardour
1359 // "ardour:Master/" -> "Master"
1360 string::size_type slash = ardour_track_name.find("/");
1361 if (slash != string::npos) {
1362 label << ardour_track_name.substr(7, slash - 7);
1366 else if (total_connection_count == system_connection_count) {
1367 // all connections are to system ports
1368 label << system_ports;
1371 else if (total_connection_count == other_connection_count) {
1372 // all connections are to the same external program eg jamin
1373 // "jamin:" -> "jamin"
1374 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1380 if (total_connection_count == 0) {
1384 // Odd configuration
1385 label << "*" << total_connection_count << "*";
1387 if (typed_connection_count > 0) {
1388 label << "\u2295"; // circled plus
1393 input_button.set_text (label.str());
1395 output_button.set_text (label.str());
1400 MixerStrip::update_input_display ()
1402 update_io_button (_route, _width, true);
1403 panners.setup_pan ();
1405 if (has_audio_outputs ()) {
1406 panners.show_all ();
1408 panners.hide_all ();
1414 MixerStrip::update_output_display ()
1416 update_io_button (_route, _width, false);
1417 gpm.setup_meters ();
1418 panners.setup_pan ();
1420 if (has_audio_outputs ()) {
1421 panners.show_all ();
1423 panners.hide_all ();
1428 MixerStrip::fast_update ()
1430 gpm.update_meters ();
1434 MixerStrip::diskstream_changed ()
1436 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1440 MixerStrip::io_changed_proxy ()
1442 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1446 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1448 boost::shared_ptr<Port> a = wa.lock ();
1449 boost::shared_ptr<Port> b = wb.lock ();
1451 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1452 update_input_display ();
1453 set_width_enum (_width, this);
1456 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1457 update_output_display ();
1458 set_width_enum (_width, this);
1463 MixerStrip::setup_comment_button ()
1468 if (_route->comment().empty ()) {
1469 _comment_button.unset_bg (STATE_NORMAL);
1470 _comment_button.set_text (_("Comments"));
1472 _comment_button.modify_bg (STATE_NORMAL, color ());
1473 _comment_button.set_text (_("*Comments*"));
1478 if (_route->comment().empty ()) {
1479 _comment_button.unset_bg (STATE_NORMAL);
1480 _comment_button.set_text (_("Cmt"));
1482 _comment_button.modify_bg (STATE_NORMAL, color ());
1483 _comment_button.set_text (_("*Cmt*"));
1489 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1495 MixerStrip::select_route_group (GdkEventButton *ev)
1497 using namespace Menu_Helpers;
1499 if (ev->button == 1) {
1501 if (group_menu == 0) {
1503 PropertyList* plist = new PropertyList();
1505 plist->add (Properties::gain, true);
1506 plist->add (Properties::mute, true);
1507 plist->add (Properties::solo, true);
1509 group_menu = new RouteGroupMenu (_session, plist);
1513 r.push_back (route ());
1514 group_menu->build (r);
1515 group_menu->menu()->popup (1, ev->time);
1522 MixerStrip::route_group_changed ()
1524 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1526 RouteGroup *rg = _route->route_group();
1529 group_button.set_text (PBD::short_version (rg->name(), 5));
1533 group_button.set_text (_("Grp"));
1536 group_button.set_text (_("~G"));
1543 MixerStrip::route_color_changed ()
1545 name_button.modify_bg (STATE_NORMAL, color());
1546 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1547 reset_strip_style ();
1551 MixerStrip::show_passthru_color ()
1553 reset_strip_style ();
1557 MixerStrip::build_route_ops_menu ()
1559 using namespace Menu_Helpers;
1560 route_ops_menu = new Menu;
1561 route_ops_menu->set_name ("ArdourContextMenu");
1563 MenuList& items = route_ops_menu->items();
1565 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1567 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1569 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1571 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1573 items.push_back (SeparatorElem());
1575 if (!_route->is_master()) {
1576 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1578 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1579 rename_menu_item = &items.back();
1581 items.push_back (SeparatorElem());
1582 items.push_back (CheckMenuElem (_("Active")));
1583 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1584 i->set_active (_route->active());
1585 i->set_sensitive(! _session->transport_rolling());
1586 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1588 items.push_back (SeparatorElem());
1590 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1592 items.push_back (SeparatorElem());
1593 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1594 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1595 denormal_menu_item->set_active (_route->denormal_protection());
1597 if (!Profile->get_sae()) {
1598 items.push_back (SeparatorElem());
1599 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 metering point has changed */
1953 MixerStrip::meter_changed ()
1955 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1956 gpm.setup_meters ();
1957 // reset peak when meter point changes
1958 gpm.reset_peak_display();
1961 /** The bus that we are displaying sends to has changed, or been turned off.
1962 * @param send_to New bus that we are displaying sends to, or 0.
1965 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1967 RouteUI::bus_send_display_changed (send_to);
1970 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1975 revert_to_default_display ();
1978 revert_to_default_display ();
1983 MixerStrip::drop_send ()
1985 boost::shared_ptr<Send> current_send;
1987 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1988 current_send->set_metering (false);
1991 send_gone_connection.disconnect ();
1992 input_button.set_sensitive (true);
1993 output_button.set_sensitive (true);
1994 group_button.set_sensitive (true);
1995 set_invert_sensitive (true);
1996 meter_point_button.set_sensitive (true);
1997 mute_button->set_sensitive (true);
1998 solo_button->set_sensitive (true);
1999 rec_enable_button->set_sensitive (true);
2000 solo_isolated_led->set_sensitive (true);
2001 solo_safe_led->set_sensitive (true);
2002 monitor_input_button->set_sensitive (true);
2003 monitor_disk_button->set_sensitive (true);
2004 _comment_button.set_sensitive (true);
2008 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2010 _current_delivery = d;
2011 DeliveryChanged (_current_delivery);
2015 MixerStrip::show_send (boost::shared_ptr<Send> send)
2021 set_current_delivery (send);
2023 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2024 send->set_metering (true);
2025 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2027 gain_meter().set_controls (_route, send->meter(), send->amp());
2028 gain_meter().setup_meters ();
2030 uint32_t const in = _current_delivery->pans_required();
2031 uint32_t const out = _current_delivery->pan_outs();
2033 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2034 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2035 panner_ui().setup_pan ();
2036 panner_ui().set_send_drawing_mode (true);
2037 panner_ui().show_all ();
2039 input_button.set_sensitive (false);
2040 group_button.set_sensitive (false);
2041 set_invert_sensitive (false);
2042 meter_point_button.set_sensitive (false);
2043 mute_button->set_sensitive (false);
2044 solo_button->set_sensitive (false);
2045 rec_enable_button->set_sensitive (false);
2046 solo_isolated_led->set_sensitive (false);
2047 solo_safe_led->set_sensitive (false);
2048 monitor_input_button->set_sensitive (false);
2049 monitor_disk_button->set_sensitive (false);
2050 _comment_button.set_sensitive (false);
2052 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2053 output_button.set_sensitive (false);
2056 reset_strip_style ();
2060 MixerStrip::revert_to_default_display ()
2064 set_current_delivery (_route->main_outs ());
2066 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2067 gain_meter().setup_meters ();
2069 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2070 update_panner_choices();
2071 panner_ui().setup_pan ();
2072 panner_ui().set_send_drawing_mode (false);
2074 if (has_audio_outputs ()) {
2075 panners.show_all ();
2077 panners.hide_all ();
2080 reset_strip_style ();
2084 MixerStrip::set_button_names ()
2088 mute_button->set_text (_("Mute"));
2089 monitor_input_button->set_text (_("In"));
2090 monitor_disk_button->set_text (_("Disk"));
2091 if (monitor_section_button) {
2092 monitor_section_button->set_text (_("Mon"));
2095 if (_route && _route->solo_safe()) {
2096 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2098 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2100 if (!Config->get_solo_control_is_listen_control()) {
2101 solo_button->set_text (_("Solo"));
2103 switch (Config->get_listen_position()) {
2104 case AfterFaderListen:
2105 solo_button->set_text (_("AFL"));
2107 case PreFaderListen:
2108 solo_button->set_text (_("PFL"));
2112 solo_isolated_led->set_text (_("Iso"));
2113 solo_safe_led->set_text (S_("SoloLock|Lock"));
2117 mute_button->set_text (S_("Mute|M"));
2118 monitor_input_button->set_text (S_("MonitorInput|I"));
2119 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2120 if (monitor_section_button) {
2121 monitor_section_button->set_text (S_("Mon|O"));
2124 if (_route && _route->solo_safe()) {
2125 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2127 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2129 if (!Config->get_solo_control_is_listen_control()) {
2130 solo_button->set_text (S_("Solo|S"));
2132 switch (Config->get_listen_position()) {
2133 case AfterFaderListen:
2134 solo_button->set_text (S_("AfterFader|A"));
2136 case PreFaderListen:
2137 solo_button->set_text (S_("Prefader|P"));
2142 solo_isolated_led->set_text (S_("SoloIso|I"));
2143 solo_safe_led->set_text (S_("SoloLock|L"));
2148 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2150 meter_point_button.set_text ("");
2155 MixerStrip::plugin_selector()
2157 return _mixer.plugin_selector();
2161 MixerStrip::hide_things ()
2163 processor_box.hide_things ();
2167 MixerStrip::input_active_button_press (GdkEventButton*)
2169 /* nothing happens on press */
2174 MixerStrip::input_active_button_release (GdkEventButton* ev)
2176 boost::shared_ptr<MidiTrack> mt = midi_track ();
2182 boost::shared_ptr<RouteList> rl (new RouteList);
2184 rl->push_back (route());
2186 _session->set_exclusive_input_active (rl, !mt->input_active(),
2187 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2193 MixerStrip::midi_input_status_changed ()
2195 if (midi_input_enable_button) {
2196 boost::shared_ptr<MidiTrack> mt = midi_track ();
2198 midi_input_enable_button->set_active (mt->input_active ());
2203 MixerStrip::state_id () const
2205 return string_compose ("strip %1", _route->id().to_s());
2209 MixerStrip::parameter_changed (string p)
2211 if (p == _visibility.get_state_name()) {
2212 /* The user has made changes to the mixer strip visibility, so get
2213 our VisibilityGroup to reflect these changes in our widgets.
2215 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2217 else if (p == "track-name-number") {
2220 else if (p == "use-monitor-bus") {
2221 if (monitor_section_button) {
2222 if (mute_button->get_parent()) {
2223 mute_button->get_parent()->remove(*mute_button);
2225 if (monitor_section_button->get_parent()) {
2226 monitor_section_button->get_parent()->remove(*monitor_section_button);
2228 if (_session->monitor_out()) {
2229 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2230 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2231 mute_button->show();
2232 monitor_section_button->show();
2234 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2235 mute_button->show();
2241 /** Called to decide whether the solo isolate / solo lock button visibility should
2242 * be overridden from that configured by the user. We do this for the master bus.
2244 * @return optional value that is present if visibility state should be overridden.
2246 boost::optional<bool>
2247 MixerStrip::override_solo_visibility () const
2249 if (_route && _route->is_master ()) {
2250 return boost::optional<bool> (false);
2253 return boost::optional<bool> ();
2257 MixerStrip::add_input_port (DataType t)
2259 _route->input()->add_port ("", this, t);
2263 MixerStrip::add_output_port (DataType t)
2265 _route->output()->add_port ("", this, t);
2269 MixerStrip::route_active_changed ()
2271 reset_strip_style ();
2275 MixerStrip::copy_processors ()
2277 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2281 MixerStrip::cut_processors ()
2283 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2287 MixerStrip::paste_processors ()
2289 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2293 MixerStrip::select_all_processors ()
2295 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2299 MixerStrip::deselect_all_processors ()
2301 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2305 MixerStrip::delete_processors ()
2307 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2311 MixerStrip::toggle_processors ()
2313 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2317 MixerStrip::ab_plugins ()
2319 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2323 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2325 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2328 if (ev->button == 3) {
2329 popup_level_meter_menu (ev);
2337 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2339 using namespace Gtk::Menu_Helpers;
2341 Gtk::Menu* m = manage (new Menu);
2342 MenuList& items = m->items ();
2344 RadioMenuItem::Group group;
2346 _suspend_menu_callbacks = true;
2347 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2348 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2349 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2350 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2351 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2353 if (gpm.meter_channels().n_audio() == 0) {
2354 m->popup (ev->button, ev->time);
2355 _suspend_menu_callbacks = false;
2359 RadioMenuItem::Group tgroup;
2360 items.push_back (SeparatorElem());
2362 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2363 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2364 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2365 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2366 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2367 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2368 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2369 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2370 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2371 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2372 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2375 if (_route->is_master()) {
2378 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2379 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2380 /* non-master bus */
2383 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2390 MeterType cmt = _route->meter_type();
2391 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2393 items.push_back (SeparatorElem());
2394 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2395 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2396 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2397 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2398 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2399 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2401 m->popup (ev->button, ev->time);
2402 _suspend_menu_callbacks = false;
2406 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2407 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2409 using namespace Menu_Helpers;
2411 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2412 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2413 i->set_active (_route->meter_point() == point);
2417 MixerStrip::set_meter_point (MeterPoint p)
2419 if (_suspend_menu_callbacks) return;
2420 _route->set_meter_point (p);
2424 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2425 RadioMenuItem::Group& group, string const & name, MeterType type)
2427 using namespace Menu_Helpers;
2429 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2430 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2431 i->set_active (_route->meter_type() == type);
2435 MixerStrip::set_meter_type (MeterType t)
2437 if (_suspend_menu_callbacks) return;