2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <sigc++/bind.h>
25 #include "pbd/convert.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/replace_all.h"
28 #include "pbd/stacktrace.h"
30 #include <gtkmm2ext/gtk_ui.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/choice.h>
33 #include <gtkmm2ext/doi.h>
34 #include <gtkmm2ext/slider_controller.h>
35 #include <gtkmm2ext/bindable_button.h>
37 #include "ardour/amp.h"
38 #include "ardour/audio_track.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/internal_send.h"
41 #include "ardour/meter.h"
42 #include "ardour/midi_track.h"
43 #include "ardour/pannable.h"
44 #include "ardour/panner.h"
45 #include "ardour/panner_shell.h"
46 #include "ardour/panner_manager.h"
47 #include "ardour/port.h"
48 #include "ardour/profile.h"
49 #include "ardour/route.h"
50 #include "ardour/route_group.h"
51 #include "ardour/send.h"
52 #include "ardour/session.h"
53 #include "ardour/types.h"
54 #include "ardour/user_bundle.h"
56 #include "ardour_window.h"
57 #include "mixer_strip.h"
60 #include "ardour_button.h"
61 #include "public_editor.h"
63 #include "io_selector.h"
65 #include "gui_thread.h"
66 #include "route_group_menu.h"
67 #include "meter_patterns.h"
69 #include "ui_config.h"
73 using namespace ARDOUR;
74 using namespace ARDOUR_UI_UTILS;
77 using namespace Gtkmm2ext;
79 using namespace ArdourMeter;
81 MixerStrip* MixerStrip::_entered_mixer_strip;
83 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
85 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
89 , _mixer_owned (in_mixer)
90 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
93 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
94 , rec_mon_table (2, 2)
95 , solo_iso_table (1, 2)
96 , mute_solo_table (1, 2)
97 , bottom_button_table (1, 3)
98 , meter_point_button (_("pre"))
99 , monitor_section_button (0)
100 , midi_input_enable_button (0)
101 , _comment_button (_("Comments"))
102 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
103 , _visibility (X_("mixer-element-visibility"))
108 /* the editor mixer strip: don't destroy it every time
109 the underlying route goes away.
112 self_destruct = false;
116 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
120 , _mixer_owned (in_mixer)
121 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
124 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
125 , rec_mon_table (2, 2)
126 , solo_iso_table (1, 2)
127 , mute_solo_table (1, 2)
128 , bottom_button_table (1, 3)
129 , meter_point_button (_("pre"))
130 , monitor_section_button (0)
131 , midi_input_enable_button (0)
132 , _comment_button (_("Comments"))
133 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
134 , _visibility (X_("mixer-element-visibility"))
143 _entered_mixer_strip= 0;
146 ignore_comment_edit = false;
147 ignore_toggle = false;
152 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
153 longest_label = "longest label";
155 string t = _("Click to toggle the width of this mixer strip.");
157 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
160 width_button.set_icon (ArdourIcon::StripWidth);
161 set_tooltip (width_button, t);
163 hide_button.set_icon (ArdourIcon::CloseCross);
164 set_tooltip (&hide_button, _("Hide this mixer strip"));
166 input_button_box.set_spacing(2);
168 input_button.set_text (_("Input"));
169 input_button.set_name ("mixer strip button");
170 input_button_box.pack_start (input_button, true, true);
172 output_button.set_text (_("Output"));
173 output_button.set_name ("mixer strip button");
175 set_tooltip (&meter_point_button, _("Click to select metering point"));
176 meter_point_button.set_name ("mixer strip button");
178 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
180 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
181 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
183 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
185 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
186 solo_isolated_led->show ();
187 solo_isolated_led->set_no_show_all (true);
188 solo_isolated_led->set_name (X_("solo isolate"));
189 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
190 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
191 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
193 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
194 solo_safe_led->show ();
195 solo_safe_led->set_no_show_all (true);
196 solo_safe_led->set_name (X_("solo safe"));
197 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
198 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
199 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
201 solo_safe_led->set_text (S_("SoloLock|Lock"));
202 solo_isolated_led->set_text (_("Iso"));
204 solo_iso_table.set_homogeneous (true);
205 solo_iso_table.set_spacings (2);
206 if (!ARDOUR::Profile->get_trx()) {
207 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
208 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
210 solo_iso_table.show ();
212 rec_mon_table.set_homogeneous (true);
213 rec_mon_table.set_row_spacings (2);
214 rec_mon_table.set_col_spacings (2);
215 if (ARDOUR::Profile->get_mixbus()) {
216 rec_mon_table.resize (1, 3);
217 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
218 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
219 } else if (!ARDOUR::Profile->get_trx()) {
220 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
221 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
223 rec_mon_table.show ();
225 if (solo_isolated_led) {
226 button_size_group->add_widget (*solo_isolated_led);
229 button_size_group->add_widget (*solo_safe_led);
232 if (!ARDOUR::Profile->get_mixbus()) {
233 if (rec_enable_button) {
234 button_size_group->add_widget (*rec_enable_button);
236 if (monitor_disk_button) {
237 button_size_group->add_widget (*monitor_disk_button);
239 if (monitor_input_button) {
240 button_size_group->add_widget (*monitor_input_button);
244 mute_solo_table.set_homogeneous (true);
245 mute_solo_table.set_spacings (2);
247 bottom_button_table.set_spacings (2);
248 bottom_button_table.set_homogeneous (true);
249 bottom_button_table.attach (group_button, 1, 2, 0, 1);
250 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
252 name_button.set_name ("mixer strip button");
253 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
254 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
256 set_tooltip (&group_button, _("Mix group"));
257 group_button.set_name ("mixer strip button");
259 _comment_button.set_name (X_("mixer strip button"));
260 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
262 // TODO implement ArdourKnob::on_size_request properly
263 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
264 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
266 trim_control.set_tooltip_prefix (_("Trim: "));
267 trim_control.set_name ("trim knob");
268 trim_control.set_no_show_all (true);
269 input_button_box.pack_start (trim_control, false, false);
271 global_vpacker.set_border_width (1);
272 global_vpacker.set_spacing (0);
274 width_button.set_name ("mixer strip button");
275 hide_button.set_name ("mixer strip button");
277 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
278 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
280 // width_hide_box.set_border_width (1);
281 width_hide_box.set_spacing (2);
282 width_hide_box.pack_start (width_button, false, true);
283 width_hide_box.pack_start (number_label, true, true);
284 width_hide_box.pack_end (hide_button, false, true);
286 number_label.set_text ("-");
287 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
288 number_label.set_no_show_all ();
289 number_label.set_name ("tracknumber label");
290 number_label.set_fixed_colors (0x80808080, 0x80808080);
291 number_label.set_alignment (.5, .5);
292 number_label.set_fallthrough_to_parent (true);
294 global_vpacker.set_spacing (2);
295 if (!ARDOUR::Profile->get_trx()) {
296 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
298 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
299 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
300 global_vpacker.pack_start (processor_box, true, true);
302 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
305 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
306 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
308 if (!ARDOUR::Profile->get_trx()) {
309 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
315 global_frame.add (global_vpacker);
316 global_frame.set_shadow_type (Gtk::SHADOW_IN);
317 global_frame.set_name ("BaseFrame");
321 /* force setting of visible selected status */
324 set_selected (false);
329 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
330 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
332 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
333 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
334 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
336 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
337 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
339 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
340 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
341 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
343 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
345 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
346 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
348 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
352 /* start off as a passthru strip. we'll correct this, if necessary,
353 in update_diskstream_display().
356 /* start off as a passthru strip. we'll correct this, if necessary,
357 in update_diskstream_display().
360 if (is_midi_track()) {
361 set_name ("MidiTrackStripBase");
363 set_name ("AudioTrackStripBase");
366 add_events (Gdk::BUTTON_RELEASE_MASK|
367 Gdk::ENTER_NOTIFY_MASK|
368 Gdk::LEAVE_NOTIFY_MASK|
370 Gdk::KEY_RELEASE_MASK);
372 set_flags (get_flags() | Gtk::CAN_FOCUS);
374 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
375 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
378 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
379 must be the same as those used in RCOptionEditor so that the configuration changes
380 are recognised when they occur.
382 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
383 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
384 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
385 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
386 _visibility.add (&output_button, X_("Output"), _("Output"), false);
387 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
389 parameter_changed (X_("mixer-element-visibility"));
390 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
391 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
392 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
394 //watch for mouse enter/exit so we can do some stuff
395 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
396 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
398 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
401 MixerStrip::~MixerStrip ()
403 CatchDeletion (this);
405 if (this ==_entered_mixer_strip)
406 _entered_mixer_strip = NULL;
410 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
412 _entered_mixer_strip = this;
414 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
415 //because the mixerstrip control is a parent that encompasses the strip
416 deselect_all_processors();
422 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
424 //if we have moved outside our strip, but not into a child view, then deselect ourselves
425 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
426 _entered_mixer_strip= 0;
428 //clear keyboard focus in the gain display. this is cheesy but fixes a longstanding "bug" where the user starts typing in the gain entry, and leaves it active, thereby prohibiting other keybindings from working
429 gpm.gain_display.set_sensitive(false);
431 gpm.gain_display.set_sensitive(true);
433 //if we leave this mixer strip we need to clear out any selections
434 //processor_box.processor_display.select_none(); //but this doesn't work, because it gets triggered when (for example) you open the menu or start a drag
441 MixerStrip::set_route (boost::shared_ptr<Route> rt)
443 //the rec/monitor stuff only shows up for tracks.
444 //the show_sends only shows up for buses.
445 //remove them all here, and we may add them back later
446 if (show_sends_button->get_parent()) {
447 rec_mon_table.remove (*show_sends_button);
449 if (rec_enable_button->get_parent()) {
450 rec_mon_table.remove (*rec_enable_button);
452 if (monitor_input_button->get_parent()) {
453 rec_mon_table.remove (*monitor_input_button);
455 if (monitor_disk_button->get_parent()) {
456 rec_mon_table.remove (*monitor_disk_button);
458 if (group_button.get_parent()) {
459 bottom_button_table.remove (group_button);
462 RouteUI::set_route (rt);
464 /* ProcessorBox needs access to _route so that it can read
467 processor_box.set_route (rt);
469 revert_to_default_display ();
471 /* unpack these from the parent and stuff them into our own
475 if (gpm.peak_display.get_parent()) {
476 gpm.peak_display.get_parent()->remove (gpm.peak_display);
478 if (gpm.gain_display.get_parent()) {
479 gpm.gain_display.get_parent()->remove (gpm.gain_display);
482 gpm.set_type (rt->meter_type());
484 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
485 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
487 if (solo_button->get_parent()) {
488 mute_solo_table.remove (*solo_button);
491 if (mute_button->get_parent()) {
492 mute_solo_table.remove (*mute_button);
495 if (route()->is_master()) {
496 solo_button->hide ();
497 mute_button->show ();
498 rec_mon_table.hide ();
499 if (solo_iso_table.get_parent()) {
500 solo_iso_table.get_parent()->remove(solo_iso_table);
502 if (monitor_section_button == 0) {
503 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
504 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
506 monitor_section_button = manage (new ArdourButton);
508 monitor_section_button->set_related_action (act);
509 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
510 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
511 monitor_section_button->show();
512 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
514 parameter_changed ("use-monitor-bus");
516 bottom_button_table.attach (group_button, 1, 2, 0, 1);
517 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
518 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
519 mute_button->show ();
520 solo_button->show ();
521 rec_mon_table.show ();
524 if (_mixer_owned && route()->is_master() ) {
526 HScrollbar scrollbar;
527 Gtk::Requisition requisition(scrollbar.size_request ());
528 int scrollbar_height = requisition.height;
530 spacer = manage (new EventBox);
531 spacer->set_size_request (-1, scrollbar_height+2);
532 global_vpacker.pack_start (*spacer, false, false);
537 monitor_input_button->show ();
538 monitor_disk_button->show ();
540 monitor_input_button->hide();
541 monitor_disk_button->hide ();
544 if (route()->trim() && route()->trim()->active()) {
545 trim_control.show ();
546 trim_control.set_controllable (route()->trim()->gain_control());
548 trim_control.hide ();
549 boost::shared_ptr<Controllable> none;
550 trim_control.set_controllable (none);
553 if (is_midi_track()) {
554 if (midi_input_enable_button == 0) {
555 midi_input_enable_button = manage (new ArdourButton);
556 midi_input_enable_button->set_name ("midi input button");
557 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
558 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
559 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
560 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
561 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
563 input_button_box.remove (*midi_input_enable_button);
565 /* get current state */
566 midi_input_status_changed ();
567 input_button_box.pack_start (*midi_input_enable_button, false, false);
569 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
571 if (midi_input_enable_button) {
572 /* removal from the container will delete it */
573 input_button_box.remove (*midi_input_enable_button);
574 midi_input_enable_button = 0;
578 if (is_audio_track()) {
579 boost::shared_ptr<AudioTrack> at = audio_track();
580 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
585 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
586 rec_enable_button->set_sensitive (_session->writable());
587 rec_enable_button->show();
589 if (ARDOUR::Profile->get_mixbus()) {
590 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
591 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
592 } else if (ARDOUR::Profile->get_trx()) {
593 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
595 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
596 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
603 if (!_route->is_master()) {
604 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
605 show_sends_button->show();
609 meter_point_button.set_text (meter_point_string (_route->meter_point()));
611 delete route_ops_menu;
614 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
615 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
616 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
617 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
619 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
621 if (_route->panner_shell()) {
622 update_panner_choices();
623 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
626 if (is_audio_track()) {
627 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
630 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
631 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
633 set_stuff_from_route ();
635 /* now force an update of all the various elements */
637 update_mute_display ();
638 update_solo_display ();
641 route_group_changed ();
644 panners.setup_pan ();
646 if (has_audio_outputs ()) {
652 update_diskstream_display ();
653 update_input_display ();
654 update_output_display ();
656 add_events (Gdk::BUTTON_RELEASE_MASK);
658 processor_box.show ();
660 if (!route()->is_master() && !route()->is_monitor()) {
661 /* we don't allow master or control routes to be hidden */
666 gpm.reset_peak_display ();
667 gpm.gain_display.show ();
668 gpm.peak_display.show ();
671 width_hide_box.show();
673 global_vpacker.show();
674 mute_solo_table.show();
675 bottom_button_table.show();
677 meter_point_button.show();
678 input_button_box.show_all();
679 output_button.show();
681 _comment_button.show();
683 gpm.gain_automation_state_button.show();
685 parameter_changed ("mixer-element-visibility");
692 MixerStrip::set_stuff_from_route ()
694 /* if width is not set, it will be set by the MixerUI or editor */
696 string str = gui_property ("strip-width");
698 set_width_enum (Width (string_2_enum (str, _width)), this);
703 MixerStrip::set_width_enum (Width w, void* owner)
705 /* always set the gpm width again, things may be hidden */
708 panners.set_width (w);
710 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
712 _width_owner = owner;
716 if (_width_owner == this) {
717 set_gui_property ("strip-width", enum_2_string (_width));
722 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
727 if (show_sends_button) {
728 show_sends_button->set_text (_("Aux"));
731 gpm.gain_automation_style_button.set_text (
732 gpm.astyle_string(gain_automation->automation_style()));
733 gpm.gain_automation_state_button.set_text (
734 gpm.astate_string(gain_automation->automation_state()));
736 if (_route->panner()) {
737 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
738 panners.astyle_string(_route->panner()->automation_style()));
739 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
740 panners.astate_string(_route->panner()->automation_state()));
744 // panners expect an even number of horiz. pixels
745 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
747 set_size_request (width, -1);
753 if (show_sends_button) {
754 show_sends_button->set_text (_("Snd"));
757 gpm.gain_automation_style_button.set_text (
758 gpm.short_astyle_string(gain_automation->automation_style()));
759 gpm.gain_automation_state_button.set_text (
760 gpm.short_astate_string(gain_automation->automation_state()));
761 gain_meter().setup_meters (); // recalc meter width
763 if (_route->panner()) {
764 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
765 panners.short_astyle_string(_route->panner()->automation_style()));
766 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
767 panners.short_astate_string(_route->panner()->automation_state()));
771 // panners expect an even number of horiz. pixels
772 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
774 set_size_request (width, -1);
779 processor_box.set_width (w);
781 update_input_display ();
782 update_output_display ();
783 setup_comment_button ();
784 route_group_changed ();
790 MixerStrip::set_packed (bool yn)
795 set_gui_property ("visible", true);
797 set_gui_property ("visible", false);
802 struct RouteCompareByName {
803 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
804 return a->name().compare (b->name()) < 0;
809 MixerStrip::output_release (GdkEventButton *ev)
811 switch (ev->button) {
813 edit_output_configuration ();
821 MixerStrip::output_press (GdkEventButton *ev)
823 using namespace Menu_Helpers;
824 if (!_session->engine().connected()) {
825 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
830 MenuList& citems = output_menu.items();
831 switch (ev->button) {
834 return false; //wait for the mouse-up to pop the dialog
838 output_menu.set_name ("ArdourContextMenu");
840 output_menu_bundles.clear ();
842 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
844 citems.push_back (SeparatorElem());
845 uint32_t const n_with_separator = citems.size ();
847 ARDOUR::BundleList current = _route->output()->bundles_connected ();
849 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
851 /* give user bundles first chance at being in the menu */
853 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
854 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
855 maybe_add_bundle_to_output_menu (*i, current);
859 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
860 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
861 maybe_add_bundle_to_output_menu (*i, current);
865 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
866 RouteList copy = *routes;
867 copy.sort (RouteCompareByName ());
868 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
869 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
872 if (citems.size() == n_with_separator) {
873 /* no routes added; remove the separator */
877 citems.push_back (SeparatorElem());
879 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
882 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
883 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
888 citems.push_back (SeparatorElem());
889 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
891 output_menu.popup (1, ev->time);
902 MixerStrip::input_release (GdkEventButton *ev)
904 switch (ev->button) {
907 edit_input_configuration ();
919 MixerStrip::input_press (GdkEventButton *ev)
921 using namespace Menu_Helpers;
923 MenuList& citems = input_menu.items();
924 input_menu.set_name ("ArdourContextMenu");
927 if (!_session->engine().connected()) {
928 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
933 if (_session->actively_recording() && _route->record_enabled())
936 switch (ev->button) {
939 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
943 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
945 citems.push_back (SeparatorElem());
946 uint32_t const n_with_separator = citems.size ();
948 input_menu_bundles.clear ();
950 ARDOUR::BundleList current = _route->input()->bundles_connected ();
952 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
954 /* give user bundles first chance at being in the menu */
956 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
957 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
958 maybe_add_bundle_to_input_menu (*i, current);
962 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
963 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
964 maybe_add_bundle_to_input_menu (*i, current);
968 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
969 RouteList copy = *routes;
970 copy.sort (RouteCompareByName ());
971 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
972 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
975 if (citems.size() == n_with_separator) {
976 /* no routes added; remove the separator */
980 citems.push_back (SeparatorElem());
981 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
984 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
985 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
990 citems.push_back (SeparatorElem());
991 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
993 input_menu.popup (1, ev->time);
1004 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1006 if (ignore_toggle) {
1010 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1012 if (std::find (current.begin(), current.end(), c) == current.end()) {
1013 _route->input()->connect_ports_to_bundle (c, true, this);
1015 _route->input()->disconnect_ports_from_bundle (c, this);
1020 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1022 if (ignore_toggle) {
1026 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1028 if (std::find (current.begin(), current.end(), c) == current.end()) {
1029 _route->output()->connect_ports_to_bundle (c, true, this);
1031 _route->output()->disconnect_ports_from_bundle (c, this);
1036 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1038 using namespace Menu_Helpers;
1040 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1044 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1045 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1049 if (i != input_menu_bundles.end()) {
1053 input_menu_bundles.push_back (b);
1055 MenuList& citems = input_menu.items();
1057 std::string n = b->name ();
1058 replace_all (n, "_", " ");
1060 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1064 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1066 using namespace Menu_Helpers;
1068 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1072 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1073 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1077 if (i != output_menu_bundles.end()) {
1081 output_menu_bundles.push_back (b);
1083 MenuList& citems = output_menu.items();
1085 std::string n = b->name ();
1086 replace_all (n, "_", " ");
1088 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1092 MixerStrip::update_diskstream_display ()
1094 if (is_track() && input_selector) {
1095 input_selector->hide_all ();
1098 route_color_changed ();
1102 MixerStrip::connect_to_pan ()
1104 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1106 panstate_connection.disconnect ();
1107 panstyle_connection.disconnect ();
1109 if (!_route->panner()) {
1113 boost::shared_ptr<Pannable> p = _route->pannable ();
1115 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1116 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1118 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1119 * However, that only works a panner was previously set.
1121 * PannerUI must remain subscribed to _panshell->Changed() in case
1122 * we switch the panner eg. AUX-Send and back
1123 * _route->panner_shell()->Changed() vs _panshell->Changed
1125 if (panners._panner == 0) {
1126 panners.panshell_changed ();
1128 update_panner_choices();
1132 MixerStrip::update_panner_choices ()
1134 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1135 if (!_route->panner_shell()) { return; }
1137 uint32_t in = _route->output()->n_ports().n_audio();
1139 if (_route->panner()) {
1140 in = _route->panner()->in().n_audio();
1143 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1147 * Output port labelling
1148 * =====================
1150 * Case 1: Each output has one connection, all connections are to system:playback_%i
1151 * out 1 -> system:playback_1
1152 * out 2 -> system:playback_2
1153 * out 3 -> system:playback_3
1156 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1157 * out 1 -> ardour:track_x/in 1
1158 * out 2 -> ardour:track_x/in 2
1159 * Display as: track_x
1161 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1162 * out 1 -> program x:foo
1163 * out 2 -> program x:foo
1164 * Display as: program x
1166 * Case 4: No connections (Disconnected)
1169 * Default case (unusual routing):
1170 * Display as: *number of connections*
1174 * .-----------------------------------------------.
1176 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1177 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1178 * '-----------------------------------------------'
1179 * .-----------------------------------------------.
1182 * '-----------------------------------------------'
1186 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1190 boost::shared_ptr<Port> port;
1191 vector<string> port_connections;
1193 uint32_t total_connection_count = 0;
1194 uint32_t io_connection_count = 0;
1195 uint32_t ardour_connection_count = 0;
1196 uint32_t system_connection_count = 0;
1197 uint32_t other_connection_count = 0;
1198 uint32_t typed_connection_count = 0;
1200 ostringstream label;
1202 bool have_label = false;
1203 bool each_io_has_one_connection = true;
1205 string connection_name;
1206 string ardour_track_name;
1207 string other_connection_type;
1208 string system_ports;
1211 ostringstream tooltip;
1212 char * tooltip_cstr;
1214 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1215 DataType dt = DataType::AUDIO;
1216 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1217 dt = DataType::MIDI;
1218 // avoid further confusion with Midi-tracks that have a synth.
1219 // Audio-ports may be connected, but button says "Disconnected"
1220 tooltip << _("MIDI ");
1224 io_count = route->n_inputs().n_total();
1225 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1227 io_count = route->n_outputs().n_total();
1228 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1232 for (io_index = 0; io_index < io_count; ++io_index) {
1234 port = route->input()->nth (io_index);
1236 port = route->output()->nth (io_index);
1239 port_connections.clear ();
1240 port->get_connections(port_connections);
1242 //ignore any port connections that don't match our DataType
1243 if (port->type() != dt) {
1244 if (!port_connections.empty()) {
1245 ++typed_connection_count;
1250 io_connection_count = 0;
1252 if (!port_connections.empty()) {
1253 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1255 string& connection_name (*i);
1257 if (connection_name.find("system:") == 0) {
1258 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1261 if (io_connection_count == 0) {
1262 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1264 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1267 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1270 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1271 if (ardour_track_name.empty()) {
1272 // "ardour:Master/in 1" -> "ardour:Master/"
1273 string::size_type slash = connection_name.find("/");
1274 if (slash != string::npos) {
1275 ardour_track_name = connection_name.substr(0, slash + 1);
1279 if (connection_name.find(ardour_track_name) == 0) {
1280 ++ardour_connection_count;
1282 } else if (!pn.empty()) {
1283 if (system_ports.empty()) {
1286 system_ports += "/" + pn;
1288 if (connection_name.find("system:") == 0) {
1289 ++system_connection_count;
1291 } else if (connection_name.find("system:midi_") == 0) {
1293 // "system:midi_capture_123" -> "123"
1294 system_port = "M " + connection_name.substr(20);
1296 // "system:midi_playback_123" -> "123"
1297 system_port = "M " + connection_name.substr(21);
1300 if (system_ports.empty()) {
1301 system_ports += system_port;
1303 system_ports += "/" + system_port;
1306 ++system_connection_count;
1308 } else if (connection_name.find("system:") == 0) {
1310 // "system:capture_123" -> "123"
1311 system_port = connection_name.substr(15);
1313 // "system:playback_123" -> "123"
1314 system_port = connection_name.substr(16);
1317 if (system_ports.empty()) {
1318 system_ports += system_port;
1320 system_ports += "/" + system_port;
1323 ++system_connection_count;
1325 if (other_connection_type.empty()) {
1326 // "jamin:in 1" -> "jamin:"
1327 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1330 if (connection_name.find(other_connection_type) == 0) {
1331 ++other_connection_count;
1335 ++total_connection_count;
1336 ++io_connection_count;
1340 if (io_connection_count != 1) {
1341 each_io_has_one_connection = false;
1345 if (total_connection_count == 0) {
1346 tooltip << endl << _("Disconnected");
1349 tooltip_cstr = new char[tooltip.str().size() + 1];
1350 strcpy(tooltip_cstr, tooltip.str().c_str());
1353 set_tooltip (&input_button, tooltip_cstr);
1355 set_tooltip (&output_button, tooltip_cstr);
1358 delete [] tooltip_cstr;
1360 if (each_io_has_one_connection) {
1361 if (total_connection_count == ardour_connection_count) {
1362 // all connections are to the same track in ardour
1363 // "ardour:Master/" -> "Master"
1364 string::size_type slash = ardour_track_name.find("/");
1365 if (slash != string::npos) {
1366 label << ardour_track_name.substr(7, slash - 7);
1370 else if (total_connection_count == system_connection_count) {
1371 // all connections are to system ports
1372 label << system_ports;
1375 else if (total_connection_count == other_connection_count) {
1376 // all connections are to the same external program eg jamin
1377 // "jamin:" -> "jamin"
1378 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1384 if (total_connection_count == 0) {
1388 // Odd configuration
1389 label << "*" << total_connection_count << "*";
1391 if (typed_connection_count > 0) {
1392 label << "\u2295"; // circled plus
1397 input_button.set_text (label.str());
1399 output_button.set_text (label.str());
1404 MixerStrip::update_input_display ()
1406 update_io_button (_route, _width, true);
1407 panners.setup_pan ();
1409 if (has_audio_outputs ()) {
1410 panners.show_all ();
1412 panners.hide_all ();
1418 MixerStrip::update_output_display ()
1420 update_io_button (_route, _width, false);
1421 gpm.setup_meters ();
1422 panners.setup_pan ();
1424 if (has_audio_outputs ()) {
1425 panners.show_all ();
1427 panners.hide_all ();
1432 MixerStrip::fast_update ()
1434 gpm.update_meters ();
1438 MixerStrip::diskstream_changed ()
1440 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1444 MixerStrip::io_changed_proxy ()
1446 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1450 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1452 boost::shared_ptr<Port> a = wa.lock ();
1453 boost::shared_ptr<Port> b = wb.lock ();
1455 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1456 update_input_display ();
1457 set_width_enum (_width, this);
1460 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1461 update_output_display ();
1462 set_width_enum (_width, this);
1467 MixerStrip::setup_comment_button ()
1472 if (_route->comment().empty ()) {
1473 _comment_button.unset_bg (STATE_NORMAL);
1474 _comment_button.set_text (_("Comments"));
1476 _comment_button.modify_bg (STATE_NORMAL, color ());
1477 _comment_button.set_text (_("*Comments*"));
1482 if (_route->comment().empty ()) {
1483 _comment_button.unset_bg (STATE_NORMAL);
1484 _comment_button.set_text (_("Cmt"));
1486 _comment_button.modify_bg (STATE_NORMAL, color ());
1487 _comment_button.set_text (_("*Cmt*"));
1493 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1499 MixerStrip::select_route_group (GdkEventButton *ev)
1501 using namespace Menu_Helpers;
1503 if (ev->button == 1) {
1505 if (group_menu == 0) {
1507 PropertyList* plist = new PropertyList();
1509 plist->add (Properties::gain, true);
1510 plist->add (Properties::mute, true);
1511 plist->add (Properties::solo, true);
1513 group_menu = new RouteGroupMenu (_session, plist);
1517 r.push_back (route ());
1518 group_menu->build (r);
1519 group_menu->menu()->popup (1, ev->time);
1526 MixerStrip::route_group_changed ()
1528 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1530 RouteGroup *rg = _route->route_group();
1533 group_button.set_text (PBD::short_version (rg->name(), 5));
1537 group_button.set_text (_("Grp"));
1540 group_button.set_text (_("~G"));
1547 MixerStrip::route_color_changed ()
1549 name_button.modify_bg (STATE_NORMAL, color());
1550 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1551 reset_strip_style ();
1555 MixerStrip::show_passthru_color ()
1557 reset_strip_style ();
1561 MixerStrip::build_route_ops_menu ()
1563 using namespace Menu_Helpers;
1564 route_ops_menu = new Menu;
1565 route_ops_menu->set_name ("ArdourContextMenu");
1567 MenuList& items = route_ops_menu->items();
1569 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1571 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1573 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1575 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1577 items.push_back (SeparatorElem());
1579 if (!_route->is_master()) {
1580 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1582 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1583 rename_menu_item = &items.back();
1585 items.push_back (SeparatorElem());
1586 items.push_back (CheckMenuElem (_("Active")));
1587 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1588 i->set_active (_route->active());
1589 i->set_sensitive(! _session->transport_rolling());
1590 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1592 items.push_back (SeparatorElem());
1593 items.push_back (CheckMenuElem (_("Strict I/O")));
1594 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1595 i->set_active (_route->strict_io());
1596 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1598 items.push_back (SeparatorElem());
1600 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1602 items.push_back (SeparatorElem());
1603 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1604 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1605 denormal_menu_item->set_active (_route->denormal_protection());
1607 items.push_back (SeparatorElem());
1608 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1611 /* note that this relies on selection being shared across editor and
1612 mixer (or global to the backend, in the future), which is the only
1613 sane thing for users anyway.
1616 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1618 Selection& selection (PublicEditor::instance().get_selection());
1619 if (!selection.selected (rtav)) {
1620 selection.set (rtav);
1623 if (!_route->is_master()) {
1624 items.push_back (SeparatorElem());
1625 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1628 items.push_back (SeparatorElem());
1629 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1635 MixerStrip::name_button_button_press (GdkEventButton* ev)
1637 if (ev->button == 3) {
1638 list_route_operations ();
1640 /* do not allow rename if the track is record-enabled */
1641 rename_menu_item->set_sensitive (!_route->record_enabled());
1642 route_ops_menu->popup (1, ev->time);
1651 MixerStrip::name_button_button_release (GdkEventButton* ev)
1653 if (ev->button == 1) {
1654 list_route_operations ();
1656 /* do not allow rename if the track is record-enabled */
1657 rename_menu_item->set_sensitive (!_route->record_enabled());
1658 route_ops_menu->popup (1, ev->time);
1665 MixerStrip::number_button_button_press (GdkEventButton* ev)
1667 if ( ev->button == 3 ) {
1668 list_route_operations ();
1670 /* do not allow rename if the track is record-enabled */
1671 rename_menu_item->set_sensitive (!_route->record_enabled());
1672 route_ops_menu->popup (1, ev->time);
1681 MixerStrip::list_route_operations ()
1683 delete route_ops_menu;
1684 build_route_ops_menu ();
1688 MixerStrip::set_selected (bool yn)
1690 AxisView::set_selected (yn);
1692 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1693 global_frame.set_name ("MixerStripSelectedFrame");
1695 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1696 global_frame.set_name ("MixerStripFrame");
1698 global_frame.queue_draw ();
1701 // processor_box.deselect_all_processors();
1705 MixerStrip::property_changed (const PropertyChange& what_changed)
1707 RouteUI::property_changed (what_changed);
1709 if (what_changed.contains (ARDOUR::Properties::name)) {
1715 MixerStrip::name_changed ()
1719 name_button.set_text (_route->name());
1722 name_button.set_text (PBD::short_version (_route->name(), 5));
1726 set_tooltip (name_button, _route->name());
1728 if (_session->config.get_track_name_number()) {
1729 const int64_t track_number = _route->track_number ();
1730 if (track_number == 0) {
1731 number_label.set_text ("-");
1733 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1736 number_label.set_text ("");
1741 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1743 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1747 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1749 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1753 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1755 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1759 MixerStrip::width_button_pressed (GdkEventButton* ev)
1761 if (ev->button != 1) {
1765 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1768 _mixer.set_strip_width (Narrow, true);
1772 _mixer.set_strip_width (Wide, true);
1778 set_width_enum (Narrow, this);
1781 set_width_enum (Wide, this);
1790 MixerStrip::hide_clicked ()
1792 // LAME fix to reset the button status for when it is redisplayed (part 1)
1793 hide_button.set_sensitive(false);
1796 Hiding(); /* EMIT_SIGNAL */
1798 _mixer.hide_strip (this);
1802 hide_button.set_sensitive(true);
1806 MixerStrip::set_embedded (bool yn)
1812 MixerStrip::map_frozen ()
1814 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1816 boost::shared_ptr<AudioTrack> at = audio_track();
1819 switch (at->freeze_state()) {
1820 case AudioTrack::Frozen:
1821 processor_box.set_sensitive (false);
1822 hide_redirect_editors ();
1825 processor_box.set_sensitive (true);
1826 // XXX need some way, maybe, to retoggle redirect editors
1830 processor_box.set_sensitive (true);
1835 MixerStrip::hide_redirect_editors ()
1837 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1841 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1843 boost::shared_ptr<Processor> processor (p.lock ());
1848 Gtk::Window* w = processor_box.get_processor_ui (processor);
1856 MixerStrip::reset_strip_style ()
1858 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1860 gpm.set_fader_name ("SendStripBase");
1864 if (is_midi_track()) {
1865 if (_route->active()) {
1866 set_name ("MidiTrackStripBase");
1868 set_name ("MidiTrackStripBaseInactive");
1870 gpm.set_fader_name ("MidiTrackFader");
1871 } else if (is_audio_track()) {
1872 if (_route->active()) {
1873 set_name ("AudioTrackStripBase");
1875 set_name ("AudioTrackStripBaseInactive");
1877 gpm.set_fader_name ("AudioTrackFader");
1879 if (_route->active()) {
1880 set_name ("AudioBusStripBase");
1882 set_name ("AudioBusStripBaseInactive");
1884 gpm.set_fader_name ("AudioBusFader");
1886 /* (no MIDI busses yet) */
1893 MixerStrip::engine_stopped ()
1898 MixerStrip::engine_running ()
1903 MixerStrip::meter_point_string (MeterPoint mp)
1916 case MeterPostFader:
1933 return S_("Meter|In");
1937 return S_("Meter|Pr");
1940 case MeterPostFader:
1941 return S_("Meter|Po");
1945 return S_("Meter|O");
1950 return S_("Meter|C");
1959 /** Called when the monitor-section state */
1961 MixerStrip::monitor_changed ()
1963 assert (monitor_section_button);
1964 if (_session->monitor_active()) {
1965 monitor_section_button->set_name ("master monitor section button active");
1967 monitor_section_button->set_name ("master monitor section button normal");
1971 /** Called when the metering point has changed */
1973 MixerStrip::meter_changed ()
1975 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1976 gpm.setup_meters ();
1977 // reset peak when meter point changes
1978 gpm.reset_peak_display();
1981 /** The bus that we are displaying sends to has changed, or been turned off.
1982 * @param send_to New bus that we are displaying sends to, or 0.
1985 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1987 RouteUI::bus_send_display_changed (send_to);
1990 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1995 revert_to_default_display ();
1998 revert_to_default_display ();
2003 MixerStrip::drop_send ()
2005 boost::shared_ptr<Send> current_send;
2007 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2008 current_send->set_metering (false);
2011 send_gone_connection.disconnect ();
2012 input_button.set_sensitive (true);
2013 output_button.set_sensitive (true);
2014 group_button.set_sensitive (true);
2015 set_invert_sensitive (true);
2016 meter_point_button.set_sensitive (true);
2017 mute_button->set_sensitive (true);
2018 solo_button->set_sensitive (true);
2019 rec_enable_button->set_sensitive (true);
2020 solo_isolated_led->set_sensitive (true);
2021 solo_safe_led->set_sensitive (true);
2022 monitor_input_button->set_sensitive (true);
2023 monitor_disk_button->set_sensitive (true);
2024 _comment_button.set_sensitive (true);
2028 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2030 _current_delivery = d;
2031 DeliveryChanged (_current_delivery);
2035 MixerStrip::show_send (boost::shared_ptr<Send> send)
2041 set_current_delivery (send);
2043 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2044 send->set_metering (true);
2045 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2047 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2048 gain_meter().setup_meters ();
2050 uint32_t const in = _current_delivery->pans_required();
2051 uint32_t const out = _current_delivery->pan_outs();
2053 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2054 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2055 panner_ui().setup_pan ();
2056 panner_ui().set_send_drawing_mode (true);
2057 panner_ui().show_all ();
2059 input_button.set_sensitive (false);
2060 group_button.set_sensitive (false);
2061 set_invert_sensitive (false);
2062 meter_point_button.set_sensitive (false);
2063 mute_button->set_sensitive (false);
2064 solo_button->set_sensitive (false);
2065 rec_enable_button->set_sensitive (false);
2066 solo_isolated_led->set_sensitive (false);
2067 solo_safe_led->set_sensitive (false);
2068 monitor_input_button->set_sensitive (false);
2069 monitor_disk_button->set_sensitive (false);
2070 _comment_button.set_sensitive (false);
2072 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2073 output_button.set_sensitive (false);
2076 reset_strip_style ();
2080 MixerStrip::revert_to_default_display ()
2084 set_current_delivery (_route->main_outs ());
2086 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2087 gain_meter().setup_meters ();
2089 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2090 update_panner_choices();
2091 panner_ui().setup_pan ();
2092 panner_ui().set_send_drawing_mode (false);
2094 if (has_audio_outputs ()) {
2095 panners.show_all ();
2097 panners.hide_all ();
2100 reset_strip_style ();
2104 MixerStrip::set_button_names ()
2108 mute_button->set_text (_("Mute"));
2109 monitor_input_button->set_text (_("In"));
2110 monitor_disk_button->set_text (_("Disk"));
2111 if (monitor_section_button) {
2112 monitor_section_button->set_text (_("Mon"));
2115 if (_route && _route->solo_safe()) {
2116 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2118 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2120 if (!Config->get_solo_control_is_listen_control()) {
2121 solo_button->set_text (_("Solo"));
2123 switch (Config->get_listen_position()) {
2124 case AfterFaderListen:
2125 solo_button->set_text (_("AFL"));
2127 case PreFaderListen:
2128 solo_button->set_text (_("PFL"));
2132 solo_isolated_led->set_text (_("Iso"));
2133 solo_safe_led->set_text (S_("SoloLock|Lock"));
2137 mute_button->set_text (S_("Mute|M"));
2138 monitor_input_button->set_text (S_("MonitorInput|I"));
2139 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2140 if (monitor_section_button) {
2141 monitor_section_button->set_text (S_("Mon|O"));
2144 if (_route && _route->solo_safe()) {
2145 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2147 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2149 if (!Config->get_solo_control_is_listen_control()) {
2150 solo_button->set_text (S_("Solo|S"));
2152 switch (Config->get_listen_position()) {
2153 case AfterFaderListen:
2154 solo_button->set_text (S_("AfterFader|A"));
2156 case PreFaderListen:
2157 solo_button->set_text (S_("Prefader|P"));
2162 solo_isolated_led->set_text (S_("SoloIso|I"));
2163 solo_safe_led->set_text (S_("SoloLock|L"));
2168 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2170 meter_point_button.set_text ("");
2175 MixerStrip::plugin_selector()
2177 return _mixer.plugin_selector();
2181 MixerStrip::hide_things ()
2183 processor_box.hide_things ();
2187 MixerStrip::input_active_button_press (GdkEventButton*)
2189 /* nothing happens on press */
2194 MixerStrip::input_active_button_release (GdkEventButton* ev)
2196 boost::shared_ptr<MidiTrack> mt = midi_track ();
2202 boost::shared_ptr<RouteList> rl (new RouteList);
2204 rl->push_back (route());
2206 _session->set_exclusive_input_active (rl, !mt->input_active(),
2207 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2213 MixerStrip::midi_input_status_changed ()
2215 if (midi_input_enable_button) {
2216 boost::shared_ptr<MidiTrack> mt = midi_track ();
2218 midi_input_enable_button->set_active (mt->input_active ());
2223 MixerStrip::state_id () const
2225 return string_compose ("strip %1", _route->id().to_s());
2229 MixerStrip::parameter_changed (string p)
2231 if (p == _visibility.get_state_name()) {
2232 /* The user has made changes to the mixer strip visibility, so get
2233 our VisibilityGroup to reflect these changes in our widgets.
2235 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2237 else if (p == "track-name-number") {
2240 else if (p == "use-monitor-bus") {
2241 if (monitor_section_button) {
2242 if (mute_button->get_parent()) {
2243 mute_button->get_parent()->remove(*mute_button);
2245 if (monitor_section_button->get_parent()) {
2246 monitor_section_button->get_parent()->remove(*monitor_section_button);
2248 if (Config->get_use_monitor_bus ()) {
2249 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2250 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2251 mute_button->show();
2252 monitor_section_button->show();
2254 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2255 mute_button->show();
2261 /** Called to decide whether the solo isolate / solo lock button visibility should
2262 * be overridden from that configured by the user. We do this for the master bus.
2264 * @return optional value that is present if visibility state should be overridden.
2266 boost::optional<bool>
2267 MixerStrip::override_solo_visibility () const
2269 if (_route && _route->is_master ()) {
2270 return boost::optional<bool> (false);
2273 return boost::optional<bool> ();
2277 MixerStrip::add_input_port (DataType t)
2279 _route->input()->add_port ("", this, t);
2283 MixerStrip::add_output_port (DataType t)
2285 _route->output()->add_port ("", this, t);
2289 MixerStrip::route_active_changed ()
2291 reset_strip_style ();
2295 MixerStrip::copy_processors ()
2297 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2301 MixerStrip::cut_processors ()
2303 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2307 MixerStrip::paste_processors ()
2309 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2313 MixerStrip::select_all_processors ()
2315 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2319 MixerStrip::deselect_all_processors ()
2321 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2325 MixerStrip::delete_processors ()
2327 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2331 MixerStrip::toggle_processors ()
2333 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2337 MixerStrip::ab_plugins ()
2339 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2343 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2345 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2348 if (ev->button == 3) {
2349 popup_level_meter_menu (ev);
2357 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2359 using namespace Gtk::Menu_Helpers;
2361 Gtk::Menu* m = manage (new Menu);
2362 MenuList& items = m->items ();
2364 RadioMenuItem::Group group;
2366 _suspend_menu_callbacks = true;
2367 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2368 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2369 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2370 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2371 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2373 if (gpm.meter_channels().n_audio() == 0) {
2374 m->popup (ev->button, ev->time);
2375 _suspend_menu_callbacks = false;
2379 RadioMenuItem::Group tgroup;
2380 items.push_back (SeparatorElem());
2382 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2383 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2384 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2385 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2386 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2387 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2388 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2389 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2390 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2391 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2392 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2395 if (_route->is_master()) {
2398 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2399 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2400 /* non-master bus */
2403 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2410 MeterType cmt = _route->meter_type();
2411 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2413 items.push_back (SeparatorElem());
2414 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2415 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2416 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2417 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2418 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2419 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2421 m->popup (ev->button, ev->time);
2422 _suspend_menu_callbacks = false;
2426 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2427 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2429 using namespace Menu_Helpers;
2431 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2432 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2433 i->set_active (_route->meter_point() == point);
2437 MixerStrip::set_meter_point (MeterPoint p)
2439 if (_suspend_menu_callbacks) return;
2440 _route->set_meter_point (p);
2444 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2445 RadioMenuItem::Group& group, string const & name, MeterType type)
2447 using namespace Menu_Helpers;
2449 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2450 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2451 i->set_active (_route->meter_type() == type);
2455 MixerStrip::set_meter_type (MeterType t)
2457 if (_suspend_menu_callbacks) return;