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_ui.h"
57 #include "ardour_window.h"
58 #include "mixer_strip.h"
61 #include "ardour_button.h"
62 #include "public_editor.h"
64 #include "io_selector.h"
66 #include "gui_thread.h"
67 #include "route_group_menu.h"
68 #include "meter_patterns.h"
72 using namespace ARDOUR;
73 using namespace ARDOUR_UI_UTILS;
76 using namespace Gtkmm2ext;
78 using namespace ArdourMeter;
80 MixerStrip* MixerStrip::_entered_mixer_strip;
82 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
84 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
88 , _mixer_owned (in_mixer)
89 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
92 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
93 , rec_mon_table (2, 2)
94 , solo_iso_table (1, 2)
95 , mute_solo_table (1, 2)
96 , bottom_button_table (1, 3)
97 , meter_point_button (_("pre"))
98 , midi_input_enable_button (0)
99 , _comment_button (_("Comments"))
100 , _visibility (X_("mixer-element-visibility"))
105 /* the editor mixer strip: don't destroy it every time
106 the underlying route goes away.
109 self_destruct = false;
113 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
117 , _mixer_owned (in_mixer)
118 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
121 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
122 , rec_mon_table (2, 2)
123 , solo_iso_table (1, 2)
124 , mute_solo_table (1, 2)
125 , bottom_button_table (1, 3)
126 , meter_point_button (_("pre"))
127 , midi_input_enable_button (0)
128 , _comment_button (_("Comments"))
129 , _visibility (X_("mixer-element-visibility"))
138 _entered_mixer_strip= 0;
141 ignore_comment_edit = false;
142 ignore_toggle = false;
147 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
148 longest_label = "longest label";
150 string t = _("Click to toggle the width of this mixer strip.");
152 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
155 width_button.set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
156 width_button.set_icon (ArdourButton::StripWidth);
157 ARDOUR_UI::instance()->set_tip (width_button, t);
159 hide_button.set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
160 hide_button.set_icon (ArdourButton::CloseCross);
161 ARDOUR_UI::instance()->set_tip (&hide_button, _("Hide this mixer strip"));
163 input_button_box.set_spacing(2);
165 input_button.set_text (_("Input"));
166 input_button.set_name ("mixer strip button");
167 input_button_box.pack_start (input_button, true, true);
169 output_button.set_text (_("Output"));
170 output_button.set_name ("mixer strip button");
172 ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Click to select metering point"), "");
173 meter_point_button.set_name ("mixer strip button");
175 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
177 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
178 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
180 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
182 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
183 solo_isolated_led->show ();
184 solo_isolated_led->set_no_show_all (true);
185 solo_isolated_led->set_name (X_("solo isolate"));
186 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
187 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
188 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
190 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
191 solo_safe_led->show ();
192 solo_safe_led->set_no_show_all (true);
193 solo_safe_led->set_name (X_("solo safe"));
194 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
195 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
196 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
198 solo_safe_led->set_text (S_("SoloLock|Lock"));
199 solo_isolated_led->set_text (_("Iso"));
201 solo_iso_table.set_homogeneous (true);
202 solo_iso_table.set_spacings (2);
203 if (!ARDOUR::Profile->get_trx()) {
204 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
205 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
207 solo_iso_table.show ();
209 rec_mon_table.set_homogeneous (true);
210 rec_mon_table.set_row_spacings (2);
211 rec_mon_table.set_col_spacings (2);
212 if (ARDOUR::Profile->get_mixbus()) {
213 rec_mon_table.resize (1, 3);
214 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
215 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
216 } else if (!ARDOUR::Profile->get_trx()) {
217 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
218 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
220 rec_mon_table.show ();
222 if (solo_isolated_led) {
223 button_size_group->add_widget (*solo_isolated_led);
226 button_size_group->add_widget (*solo_safe_led);
229 if (!ARDOUR::Profile->get_mixbus()) {
230 if (rec_enable_button) {
231 button_size_group->add_widget (*rec_enable_button);
233 if (monitor_disk_button) {
234 button_size_group->add_widget (*monitor_disk_button);
236 if (monitor_input_button) {
237 button_size_group->add_widget (*monitor_input_button);
241 mute_solo_table.set_homogeneous (true);
242 mute_solo_table.set_spacings (2);
244 bottom_button_table.set_spacings (2);
245 bottom_button_table.set_homogeneous (true);
246 bottom_button_table.attach (group_button, 1, 2, 0, 1);
247 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
249 name_button.set_name ("mixer strip button");
250 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
251 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
253 ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
254 group_button.set_name ("mixer strip button");
256 _comment_button.set_name (X_("mixer strip button"));
257 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
259 // TODO implement ArdourKnob::on_size_request properly
260 #define PX_SCALE(px) std::max((float)px, rintf((float)px * ARDOUR_UI::ui_scale))
261 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
263 trim_control.set_tooltip_prefix (_("Trim: "));
264 trim_control.set_name ("trim knob");
265 trim_control.set_no_show_all (true);
266 input_button_box.pack_start (trim_control, false, false);
268 global_vpacker.set_border_width (1);
269 global_vpacker.set_spacing (0);
271 width_button.set_name ("mixer strip button");
272 hide_button.set_name ("mixer strip button");
274 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
275 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
277 // width_hide_box.set_border_width (1);
278 width_hide_box.set_spacing (2);
279 width_hide_box.pack_start (width_button, false, true);
280 width_hide_box.pack_start (number_label, true, true);
281 width_hide_box.pack_end (hide_button, false, true);
283 number_label.set_text ("-");
284 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
285 number_label.set_no_show_all ();
286 number_label.set_name ("tracknumber label");
287 number_label.set_fixed_colors (0x80808080, 0x80808080);
288 number_label.set_alignment (.5, .5);
289 number_label.set_fallthrough_to_parent (true);
291 global_vpacker.set_spacing (2);
292 if (!ARDOUR::Profile->get_trx()) {
293 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
294 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
295 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
296 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (processor_box, true, true);
299 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
300 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
301 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
302 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
305 if (!ARDOUR::Profile->get_trx()) {
306 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
312 global_frame.add (global_vpacker);
313 global_frame.set_shadow_type (Gtk::SHADOW_IN);
314 global_frame.set_name ("BaseFrame");
318 /* force setting of visible selected status */
321 set_selected (false);
326 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
327 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
329 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
330 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
331 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
333 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
334 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
336 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
337 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
338 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
340 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
342 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
343 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
345 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
349 /* start off as a passthru strip. we'll correct this, if necessary,
350 in update_diskstream_display().
353 /* start off as a passthru strip. we'll correct this, if necessary,
354 in update_diskstream_display().
357 if (is_midi_track()) {
358 set_name ("MidiTrackStripBase");
360 set_name ("AudioTrackStripBase");
363 add_events (Gdk::BUTTON_RELEASE_MASK|
364 Gdk::ENTER_NOTIFY_MASK|
365 Gdk::LEAVE_NOTIFY_MASK|
367 Gdk::KEY_RELEASE_MASK);
369 set_flags (get_flags() | Gtk::CAN_FOCUS);
371 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
372 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
375 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
376 must be the same as those used in RCOptionEditor so that the configuration changes
377 are recognised when they occur.
379 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
380 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
381 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
382 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
383 _visibility.add (&output_button, X_("Output"), _("Output"), false);
384 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
386 parameter_changed (X_("mixer-element-visibility"));
387 ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
388 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
389 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
391 //watch for mouse enter/exit so we can do some stuff
392 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
393 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
395 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
398 MixerStrip::~MixerStrip ()
400 CatchDeletion (this);
402 if (this ==_entered_mixer_strip)
403 _entered_mixer_strip = NULL;
407 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
409 _entered_mixer_strip = this;
411 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
412 //because the mixerstrip control is a parent that encompasses the strip
413 deselect_all_processors();
419 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
421 //if we have moved outside our strip, but not into a child view, then deselect ourselves
422 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
423 _entered_mixer_strip= 0;
425 //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
426 gpm.gain_display.set_sensitive(false);
428 gpm.gain_display.set_sensitive(true);
430 //if we leave this mixer strip we need to clear out any selections
431 //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
438 MixerStrip::set_route (boost::shared_ptr<Route> rt)
440 //the rec/monitor stuff only shows up for tracks.
441 //the show_sends only shows up for buses.
442 //remove them all here, and we may add them back later
443 if (show_sends_button->get_parent()) {
444 rec_mon_table.remove (*show_sends_button);
446 if (rec_enable_button->get_parent()) {
447 rec_mon_table.remove (*rec_enable_button);
449 if (monitor_input_button->get_parent()) {
450 rec_mon_table.remove (*monitor_input_button);
452 if (monitor_disk_button->get_parent()) {
453 rec_mon_table.remove (*monitor_disk_button);
455 if (group_button.get_parent()) {
456 bottom_button_table.remove (group_button);
459 RouteUI::set_route (rt);
461 /* ProcessorBox needs access to _route so that it can read
464 processor_box.set_route (rt);
466 revert_to_default_display ();
468 /* unpack these from the parent and stuff them into our own
472 if (gpm.peak_display.get_parent()) {
473 gpm.peak_display.get_parent()->remove (gpm.peak_display);
475 if (gpm.gain_display.get_parent()) {
476 gpm.gain_display.get_parent()->remove (gpm.gain_display);
479 gpm.set_type (rt->meter_type());
481 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
482 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
484 if (solo_button->get_parent()) {
485 mute_solo_table.remove (*solo_button);
488 if (mute_button->get_parent()) {
489 mute_solo_table.remove (*mute_button);
492 if (route()->is_master()) {
493 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
494 solo_button->hide ();
495 mute_button->show ();
496 rec_mon_table.hide ();
497 if (solo_iso_table.get_parent()) {
498 solo_iso_table.get_parent()->remove(solo_iso_table);
501 bottom_button_table.attach (group_button, 1, 2, 0, 1);
502 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
503 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
504 mute_button->show ();
505 solo_button->show ();
506 rec_mon_table.show ();
509 if (_mixer_owned && route()->is_master() ) {
511 HScrollbar scrollbar;
512 Gtk::Requisition requisition(scrollbar.size_request ());
513 int scrollbar_height = requisition.height;
515 spacer = manage (new EventBox);
516 spacer->set_size_request (-1, scrollbar_height+2);
517 global_vpacker.pack_start (*spacer, false, false);
522 monitor_input_button->show ();
523 monitor_disk_button->show ();
525 monitor_input_button->hide();
526 monitor_disk_button->hide ();
529 if (route()->trim() && route()->trim()->active()) {
530 trim_control.show ();
531 trim_control.set_controllable (route()->trim()->gain_control());
533 trim_control.hide ();
534 boost::shared_ptr<Controllable> none;
535 trim_control.set_controllable (none);
538 if (is_midi_track()) {
539 if (midi_input_enable_button == 0) {
540 midi_input_enable_button = manage (new ArdourButton);
541 midi_input_enable_button->set_name ("midi input button");
542 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
543 midi_input_enable_button->set_icon (ArdourButton::DinMidi);
544 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
545 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
546 ARDOUR_UI::instance()->set_tip (midi_input_enable_button, _("Enable/Disable MIDI input"));
548 input_button_box.remove (*midi_input_enable_button);
550 /* get current state */
551 midi_input_status_changed ();
552 input_button_box.pack_start (*midi_input_enable_button, false, false);
554 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
556 if (midi_input_enable_button) {
557 /* removal from the container will delete it */
558 input_button_box.remove (*midi_input_enable_button);
559 midi_input_enable_button = 0;
563 if (is_audio_track()) {
564 boost::shared_ptr<AudioTrack> at = audio_track();
565 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
570 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
571 rec_enable_button->set_sensitive (_session->writable());
572 rec_enable_button->show();
574 if (ARDOUR::Profile->get_mixbus()) {
575 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
576 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
577 } else if (ARDOUR::Profile->get_trx()) {
578 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
580 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
581 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
588 if (!_route->is_master()) {
589 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
590 show_sends_button->show();
594 meter_point_button.set_text (meter_point_string (_route->meter_point()));
596 delete route_ops_menu;
599 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
600 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
601 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
602 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
604 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
606 if (_route->panner_shell()) {
607 update_panner_choices();
608 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
611 if (is_audio_track()) {
612 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
615 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
616 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
618 set_stuff_from_route ();
620 /* now force an update of all the various elements */
622 update_mute_display ();
623 update_solo_display ();
626 route_group_changed ();
629 panners.setup_pan ();
631 if (has_audio_outputs ()) {
637 update_diskstream_display ();
638 update_input_display ();
639 update_output_display ();
641 add_events (Gdk::BUTTON_RELEASE_MASK);
643 processor_box.show ();
645 if (!route()->is_master() && !route()->is_monitor()) {
646 /* we don't allow master or control routes to be hidden */
651 gpm.reset_peak_display ();
652 gpm.gain_display.show ();
653 gpm.peak_display.show ();
656 width_hide_box.show();
658 global_vpacker.show();
659 mute_solo_table.show();
660 bottom_button_table.show();
662 meter_point_button.show();
663 input_button_box.show_all();
664 output_button.show();
666 _comment_button.show();
668 gpm.gain_automation_state_button.show();
670 parameter_changed ("mixer-element-visibility");
676 MixerStrip::set_stuff_from_route ()
678 /* if width is not set, it will be set by the MixerUI or editor */
680 string str = gui_property ("strip-width");
682 set_width_enum (Width (string_2_enum (str, _width)), this);
687 MixerStrip::set_width_enum (Width w, void* owner)
689 /* always set the gpm width again, things may be hidden */
692 panners.set_width (w);
694 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
696 _width_owner = owner;
700 if (_width_owner == this) {
701 set_gui_property ("strip-width", enum_2_string (_width));
706 const float scale = std::max(1.f, ARDOUR_UI::ui_scale);
711 if (show_sends_button) {
712 show_sends_button->set_text (_("Aux"));
715 gpm.gain_automation_style_button.set_text (
716 gpm.astyle_string(gain_automation->automation_style()));
717 gpm.gain_automation_state_button.set_text (
718 gpm.astate_string(gain_automation->automation_state()));
720 if (_route->panner()) {
721 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
722 panners.astyle_string(_route->panner()->automation_style()));
723 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
724 panners.astate_string(_route->panner()->automation_state()));
728 // panners expect an even number of horiz. pixels
729 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
731 set_size_request (width, -1);
737 if (show_sends_button) {
738 show_sends_button->set_text (_("Snd"));
741 gpm.gain_automation_style_button.set_text (
742 gpm.short_astyle_string(gain_automation->automation_style()));
743 gpm.gain_automation_state_button.set_text (
744 gpm.short_astate_string(gain_automation->automation_state()));
745 gain_meter().setup_meters (); // recalc meter width
747 if (_route->panner()) {
748 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
749 panners.short_astyle_string(_route->panner()->automation_style()));
750 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
751 panners.short_astate_string(_route->panner()->automation_state()));
755 // panners expect an even number of horiz. pixels
756 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
758 set_size_request (width, -1);
763 processor_box.set_width (w);
765 update_input_display ();
766 update_output_display ();
767 setup_comment_button ();
768 route_group_changed ();
774 MixerStrip::set_packed (bool yn)
779 set_gui_property ("visible", true);
781 set_gui_property ("visible", false);
786 struct RouteCompareByName {
787 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
788 return a->name().compare (b->name()) < 0;
793 MixerStrip::output_release (GdkEventButton *ev)
795 switch (ev->button) {
797 edit_output_configuration ();
805 MixerStrip::output_press (GdkEventButton *ev)
807 using namespace Menu_Helpers;
808 if (!_session->engine().connected()) {
809 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
814 MenuList& citems = output_menu.items();
815 switch (ev->button) {
818 return false; //wait for the mouse-up to pop the dialog
822 output_menu.set_name ("ArdourContextMenu");
824 output_menu_bundles.clear ();
826 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
828 citems.push_back (SeparatorElem());
829 uint32_t const n_with_separator = citems.size ();
831 ARDOUR::BundleList current = _route->output()->bundles_connected ();
833 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
835 /* give user bundles first chance at being in the menu */
837 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
838 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
839 maybe_add_bundle_to_output_menu (*i, current);
843 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
844 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
845 maybe_add_bundle_to_output_menu (*i, current);
849 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
850 RouteList copy = *routes;
851 copy.sort (RouteCompareByName ());
852 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
853 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
856 if (citems.size() == n_with_separator) {
857 /* no routes added; remove the separator */
861 citems.push_back (SeparatorElem());
863 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
866 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
867 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
872 citems.push_back (SeparatorElem());
873 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
875 output_menu.popup (1, ev->time);
886 MixerStrip::input_release (GdkEventButton *ev)
888 switch (ev->button) {
891 edit_input_configuration ();
903 MixerStrip::input_press (GdkEventButton *ev)
905 using namespace Menu_Helpers;
907 MenuList& citems = input_menu.items();
908 input_menu.set_name ("ArdourContextMenu");
911 if (!_session->engine().connected()) {
912 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
917 if (_session->actively_recording() && _route->record_enabled())
920 switch (ev->button) {
923 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
927 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
929 citems.push_back (SeparatorElem());
930 uint32_t const n_with_separator = citems.size ();
932 input_menu_bundles.clear ();
934 ARDOUR::BundleList current = _route->input()->bundles_connected ();
936 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
938 /* give user bundles first chance at being in the menu */
940 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
941 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
942 maybe_add_bundle_to_input_menu (*i, current);
946 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
947 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
948 maybe_add_bundle_to_input_menu (*i, current);
952 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
953 RouteList copy = *routes;
954 copy.sort (RouteCompareByName ());
955 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
956 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
959 if (citems.size() == n_with_separator) {
960 /* no routes added; remove the separator */
964 citems.push_back (SeparatorElem());
965 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
968 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
969 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
974 citems.push_back (SeparatorElem());
975 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
977 input_menu.popup (1, ev->time);
988 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
994 ARDOUR::BundleList current = _route->input()->bundles_connected ();
996 if (std::find (current.begin(), current.end(), c) == current.end()) {
997 _route->input()->connect_ports_to_bundle (c, true, this);
999 _route->input()->disconnect_ports_from_bundle (c, this);
1004 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1006 if (ignore_toggle) {
1010 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1012 if (std::find (current.begin(), current.end(), c) == current.end()) {
1013 _route->output()->connect_ports_to_bundle (c, true, this);
1015 _route->output()->disconnect_ports_from_bundle (c, this);
1020 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1022 using namespace Menu_Helpers;
1024 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1028 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1029 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1033 if (i != input_menu_bundles.end()) {
1037 input_menu_bundles.push_back (b);
1039 MenuList& citems = input_menu.items();
1041 std::string n = b->name ();
1042 replace_all (n, "_", " ");
1044 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1048 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1050 using namespace Menu_Helpers;
1052 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1056 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1057 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1061 if (i != output_menu_bundles.end()) {
1065 output_menu_bundles.push_back (b);
1067 MenuList& citems = output_menu.items();
1069 std::string n = b->name ();
1070 replace_all (n, "_", " ");
1072 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1076 MixerStrip::update_diskstream_display ()
1078 if (is_track() && input_selector) {
1079 input_selector->hide_all ();
1082 route_color_changed ();
1086 MixerStrip::connect_to_pan ()
1088 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1090 panstate_connection.disconnect ();
1091 panstyle_connection.disconnect ();
1093 if (!_route->panner()) {
1097 boost::shared_ptr<Pannable> p = _route->pannable ();
1099 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1100 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1102 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1103 * However, that only works a panner was previously set.
1105 * PannerUI must remain subscribed to _panshell->Changed() in case
1106 * we switch the panner eg. AUX-Send and back
1107 * _route->panner_shell()->Changed() vs _panshell->Changed
1109 if (panners._panner == 0) {
1110 panners.panshell_changed ();
1112 update_panner_choices();
1116 MixerStrip::update_panner_choices ()
1118 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1119 if (!_route->panner_shell()) { return; }
1121 uint32_t in = _route->output()->n_ports().n_audio();
1123 if (_route->panner()) {
1124 in = _route->panner()->in().n_audio();
1127 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1131 * Output port labelling
1132 * =====================
1134 * Case 1: Each output has one connection, all connections are to system:playback_%i
1135 * out 1 -> system:playback_1
1136 * out 2 -> system:playback_2
1137 * out 3 -> system:playback_3
1140 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1141 * out 1 -> ardour:track_x/in 1
1142 * out 2 -> ardour:track_x/in 2
1143 * Display as: track_x
1145 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1146 * out 1 -> program x:foo
1147 * out 2 -> program x:foo
1148 * Display as: program x
1150 * Case 4: No connections (Disconnected)
1153 * Default case (unusual routing):
1154 * Display as: *number of connections*
1158 * .-----------------------------------------------.
1160 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1161 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1162 * '-----------------------------------------------'
1163 * .-----------------------------------------------.
1166 * '-----------------------------------------------'
1170 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1174 boost::shared_ptr<Port> port;
1175 vector<string> port_connections;
1177 uint32_t total_connection_count = 0;
1178 uint32_t io_connection_count = 0;
1179 uint32_t ardour_connection_count = 0;
1180 uint32_t system_connection_count = 0;
1181 uint32_t other_connection_count = 0;
1183 ostringstream label;
1185 bool have_label = false;
1186 bool each_io_has_one_connection = true;
1188 string connection_name;
1189 string ardour_track_name;
1190 string other_connection_type;
1191 string system_ports;
1194 ostringstream tooltip;
1195 char * tooltip_cstr;
1197 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1198 DataType dt = DataType::AUDIO;
1199 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 )
1200 dt = DataType::MIDI;
1203 io_count = route->n_inputs().n_total();
1204 tooltip << string_compose (_("<b>INPUT</b> to %1"), Glib::Markup::escape_text(route->name()));
1206 io_count = route->n_outputs().n_total();
1207 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(route->name()));
1211 for (io_index = 0; io_index < io_count; ++io_index) {
1213 port = route->input()->nth (io_index);
1215 port = route->output()->nth (io_index);
1218 //ignore any port connections that don't match our DataType
1219 if (port->type() != dt)
1222 port_connections.clear ();
1223 port->get_connections(port_connections);
1224 io_connection_count = 0;
1226 if (!port_connections.empty()) {
1227 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1229 string& connection_name (*i);
1231 if (connection_name.find("system:") == 0) {
1232 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1235 if (io_connection_count == 0) {
1236 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1238 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1241 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1244 if (connection_name.find("ardour:") == 0) {
1245 if (ardour_track_name.empty()) {
1246 // "ardour:Master/in 1" -> "ardour:Master/"
1247 string::size_type slash = connection_name.find("/");
1248 if (slash != string::npos) {
1249 ardour_track_name = connection_name.substr(0, slash + 1);
1253 if (connection_name.find(ardour_track_name) == 0) {
1254 ++ardour_connection_count;
1256 } else if (!pn.empty()) {
1257 if (system_ports.empty()) {
1260 system_ports += "/" + pn;
1262 if (connection_name.find("system:") == 0) {
1263 ++system_connection_count;
1265 } else if (connection_name.find("system:midi_") == 0) {
1267 // "system:midi_capture_123" -> "123"
1268 system_port = "M " + connection_name.substr(20);
1270 // "system:midi_playback_123" -> "123"
1271 system_port = "M " + connection_name.substr(21);
1274 if (system_ports.empty()) {
1275 system_ports += system_port;
1277 system_ports += "/" + system_port;
1280 ++system_connection_count;
1282 } else if (connection_name.find("system:") == 0) {
1284 // "system:capture_123" -> "123"
1285 system_port = connection_name.substr(15);
1287 // "system:playback_123" -> "123"
1288 system_port = connection_name.substr(16);
1291 if (system_ports.empty()) {
1292 system_ports += system_port;
1294 system_ports += "/" + system_port;
1297 ++system_connection_count;
1299 if (other_connection_type.empty()) {
1300 // "jamin:in 1" -> "jamin:"
1301 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1304 if (connection_name.find(other_connection_type) == 0) {
1305 ++other_connection_count;
1309 ++total_connection_count;
1310 ++io_connection_count;
1314 if (io_connection_count != 1) {
1315 each_io_has_one_connection = false;
1319 if (total_connection_count == 0) {
1320 tooltip << endl << _("Disconnected");
1323 tooltip_cstr = new char[tooltip.str().size() + 1];
1324 strcpy(tooltip_cstr, tooltip.str().c_str());
1327 ARDOUR_UI::instance()->set_tip (&input_button, tooltip_cstr, "");
1329 ARDOUR_UI::instance()->set_tip (&output_button, tooltip_cstr, "");
1332 if (each_io_has_one_connection) {
1333 if (total_connection_count == ardour_connection_count) {
1334 // all connections are to the same track in ardour
1335 // "ardour:Master/" -> "Master"
1336 string::size_type slash = ardour_track_name.find("/");
1337 if (slash != string::npos) {
1338 label << ardour_track_name.substr(7, slash - 7);
1342 else if (total_connection_count == system_connection_count) {
1343 // all connections are to system ports
1344 label << system_ports;
1347 else if (total_connection_count == other_connection_count) {
1348 // all connections are to the same external program eg jamin
1349 // "jamin:" -> "jamin"
1350 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1356 if (total_connection_count == 0) {
1360 // Odd configuration
1361 label << "*" << total_connection_count << "*";
1366 input_button.set_text (label.str());
1368 output_button.set_text (label.str());
1373 MixerStrip::update_input_display ()
1375 update_io_button (_route, _width, true);
1376 panners.setup_pan ();
1378 if (has_audio_outputs ()) {
1379 panners.show_all ();
1381 panners.hide_all ();
1387 MixerStrip::update_output_display ()
1389 update_io_button (_route, _width, false);
1390 gpm.setup_meters ();
1391 panners.setup_pan ();
1393 if (has_audio_outputs ()) {
1394 panners.show_all ();
1396 panners.hide_all ();
1401 MixerStrip::fast_update ()
1403 gpm.update_meters ();
1407 MixerStrip::diskstream_changed ()
1409 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1413 MixerStrip::io_changed_proxy ()
1415 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1419 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1421 boost::shared_ptr<Port> a = wa.lock ();
1422 boost::shared_ptr<Port> b = wb.lock ();
1424 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1425 update_input_display ();
1426 set_width_enum (_width, this);
1429 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1430 update_output_display ();
1431 set_width_enum (_width, this);
1436 MixerStrip::setup_comment_button ()
1441 if (_route->comment().empty ()) {
1442 _comment_button.unset_bg (STATE_NORMAL);
1443 _comment_button.set_text (_("Comments"));
1445 _comment_button.modify_bg (STATE_NORMAL, color ());
1446 _comment_button.set_text (_("*Comments*"));
1451 if (_route->comment().empty ()) {
1452 _comment_button.unset_bg (STATE_NORMAL);
1453 _comment_button.set_text (_("Cmt"));
1455 _comment_button.modify_bg (STATE_NORMAL, color ());
1456 _comment_button.set_text (_("*Cmt*"));
1461 ARDOUR_UI::instance()->set_tip (
1462 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1468 MixerStrip::select_route_group (GdkEventButton *ev)
1470 using namespace Menu_Helpers;
1472 if (ev->button == 1) {
1474 if (group_menu == 0) {
1476 PropertyList* plist = new PropertyList();
1478 plist->add (Properties::gain, true);
1479 plist->add (Properties::mute, true);
1480 plist->add (Properties::solo, true);
1482 group_menu = new RouteGroupMenu (_session, plist);
1486 r.push_back (route ());
1487 group_menu->build (r);
1488 group_menu->menu()->popup (1, ev->time);
1495 MixerStrip::route_group_changed ()
1497 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1499 RouteGroup *rg = _route->route_group();
1502 group_button.set_text (PBD::short_version (rg->name(), 5));
1506 group_button.set_text (_("Grp"));
1509 group_button.set_text (_("~G"));
1516 MixerStrip::route_color_changed ()
1518 name_button.modify_bg (STATE_NORMAL, color());
1519 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1520 reset_strip_style ();
1524 MixerStrip::show_passthru_color ()
1526 reset_strip_style ();
1530 MixerStrip::build_route_ops_menu ()
1532 using namespace Menu_Helpers;
1533 route_ops_menu = new Menu;
1534 route_ops_menu->set_name ("ArdourContextMenu");
1536 MenuList& items = route_ops_menu->items();
1538 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1540 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1542 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1544 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1546 items.push_back (SeparatorElem());
1548 if (!_route->is_master()) {
1549 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1551 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1552 rename_menu_item = &items.back();
1554 items.push_back (SeparatorElem());
1555 items.push_back (CheckMenuElem (_("Active")));
1556 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1557 i->set_active (_route->active());
1558 i->set_sensitive(! _session->transport_rolling());
1559 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1561 items.push_back (SeparatorElem());
1563 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1565 items.push_back (SeparatorElem());
1566 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1567 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1568 denormal_menu_item->set_active (_route->denormal_protection());
1570 if (!Profile->get_sae()) {
1571 items.push_back (SeparatorElem());
1572 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1575 items.push_back (SeparatorElem());
1578 /* note that this relies on selection being shared across editor and
1579 mixer (or global to the backend, in the future), which is the only
1580 sane thing for users anyway.
1583 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1585 Selection& selection (PublicEditor::instance().get_selection());
1586 if (!selection.selected (rtav)) {
1587 selection.set (rtav);
1590 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1596 MixerStrip::name_button_button_press (GdkEventButton* ev)
1598 if (ev->button == 3) {
1599 list_route_operations ();
1601 /* do not allow rename if the track is record-enabled */
1602 rename_menu_item->set_sensitive (!_route->record_enabled());
1603 route_ops_menu->popup (1, ev->time);
1612 MixerStrip::name_button_button_release (GdkEventButton* ev)
1614 if (ev->button == 1) {
1615 list_route_operations ();
1617 /* do not allow rename if the track is record-enabled */
1618 rename_menu_item->set_sensitive (!_route->record_enabled());
1619 route_ops_menu->popup (1, ev->time);
1626 MixerStrip::number_button_button_press (GdkEventButton* ev)
1628 if ( ev->button == 3 ) {
1629 list_route_operations ();
1631 /* do not allow rename if the track is record-enabled */
1632 rename_menu_item->set_sensitive (!_route->record_enabled());
1633 route_ops_menu->popup (1, ev->time);
1642 MixerStrip::list_route_operations ()
1644 delete route_ops_menu;
1645 build_route_ops_menu ();
1649 MixerStrip::set_selected (bool yn)
1651 AxisView::set_selected (yn);
1653 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1654 global_frame.set_name ("MixerStripSelectedFrame");
1656 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1657 global_frame.set_name ("MixerStripFrame");
1659 global_frame.queue_draw ();
1662 // processor_box.deselect_all_processors();
1666 MixerStrip::property_changed (const PropertyChange& what_changed)
1668 RouteUI::property_changed (what_changed);
1670 if (what_changed.contains (ARDOUR::Properties::name)) {
1676 MixerStrip::name_changed ()
1680 name_button.set_text (_route->name());
1683 name_button.set_text (PBD::short_version (_route->name(), 5));
1687 ARDOUR_UI::instance()->set_tip (name_button, _route->name());
1689 if (_session->config.get_track_name_number()) {
1690 const int64_t track_number = _route->track_number ();
1691 if (track_number == 0) {
1692 number_label.set_text ("-");
1694 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1697 number_label.set_text ("");
1702 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1704 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1708 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1710 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1714 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1716 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1720 MixerStrip::width_button_pressed (GdkEventButton* ev)
1722 if (ev->button != 1) {
1726 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1729 _mixer.set_strip_width (Narrow, true);
1733 _mixer.set_strip_width (Wide, true);
1739 set_width_enum (Narrow, this);
1742 set_width_enum (Wide, this);
1751 MixerStrip::hide_clicked ()
1753 // LAME fix to reset the button status for when it is redisplayed (part 1)
1754 hide_button.set_sensitive(false);
1757 Hiding(); /* EMIT_SIGNAL */
1759 _mixer.hide_strip (this);
1763 hide_button.set_sensitive(true);
1767 MixerStrip::set_embedded (bool yn)
1773 MixerStrip::map_frozen ()
1775 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1777 boost::shared_ptr<AudioTrack> at = audio_track();
1780 switch (at->freeze_state()) {
1781 case AudioTrack::Frozen:
1782 processor_box.set_sensitive (false);
1783 hide_redirect_editors ();
1786 processor_box.set_sensitive (true);
1787 // XXX need some way, maybe, to retoggle redirect editors
1794 MixerStrip::hide_redirect_editors ()
1796 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1800 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1802 boost::shared_ptr<Processor> processor (p.lock ());
1807 Gtk::Window* w = processor_box.get_processor_ui (processor);
1815 MixerStrip::reset_strip_style ()
1817 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1819 gpm.set_fader_name ("SendStripBase");
1823 if (is_midi_track()) {
1824 if (_route->active()) {
1825 set_name ("MidiTrackStripBase");
1827 set_name ("MidiTrackStripBaseInactive");
1829 gpm.set_fader_name ("MidiTrackFader");
1830 } else if (is_audio_track()) {
1831 if (_route->active()) {
1832 set_name ("AudioTrackStripBase");
1834 set_name ("AudioTrackStripBaseInactive");
1836 gpm.set_fader_name ("AudioTrackFader");
1838 if (_route->active()) {
1839 set_name ("AudioBusStripBase");
1841 set_name ("AudioBusStripBaseInactive");
1843 gpm.set_fader_name ("AudioBusFader");
1845 /* (no MIDI busses yet) */
1852 MixerStrip::engine_stopped ()
1857 MixerStrip::engine_running ()
1862 MixerStrip::meter_point_string (MeterPoint mp)
1875 case MeterPostFader:
1892 return S_("Meter|In");
1896 return S_("Meter|Pr");
1899 case MeterPostFader:
1900 return S_("Meter|Po");
1904 return S_("Meter|O");
1909 return S_("Meter|C");
1918 /** Called when the metering point has changed */
1920 MixerStrip::meter_changed ()
1922 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1923 gpm.setup_meters ();
1924 // reset peak when meter point changes
1925 gpm.reset_peak_display();
1928 /** The bus that we are displaying sends to has changed, or been turned off.
1929 * @param send_to New bus that we are displaying sends to, or 0.
1932 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1934 RouteUI::bus_send_display_changed (send_to);
1937 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1942 revert_to_default_display ();
1945 revert_to_default_display ();
1950 MixerStrip::drop_send ()
1952 boost::shared_ptr<Send> current_send;
1954 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1955 current_send->set_metering (false);
1958 send_gone_connection.disconnect ();
1959 input_button.set_sensitive (true);
1960 output_button.set_sensitive (true);
1961 group_button.set_sensitive (true);
1962 set_invert_sensitive (true);
1963 meter_point_button.set_sensitive (true);
1964 mute_button->set_sensitive (true);
1965 solo_button->set_sensitive (true);
1966 rec_enable_button->set_sensitive (true);
1967 solo_isolated_led->set_sensitive (true);
1968 solo_safe_led->set_sensitive (true);
1969 monitor_input_button->set_sensitive (true);
1970 monitor_disk_button->set_sensitive (true);
1971 _comment_button.set_sensitive (true);
1975 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1977 _current_delivery = d;
1978 DeliveryChanged (_current_delivery);
1982 MixerStrip::show_send (boost::shared_ptr<Send> send)
1988 set_current_delivery (send);
1990 send->meter()->set_type(_route->shared_peak_meter()->get_type());
1991 send->set_metering (true);
1992 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
1994 gain_meter().set_controls (_route, send->meter(), send->amp());
1995 gain_meter().setup_meters ();
1997 uint32_t const in = _current_delivery->pans_required();
1998 uint32_t const out = _current_delivery->pan_outs();
2000 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2001 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2002 panner_ui().setup_pan ();
2003 panner_ui().set_send_drawing_mode (true);
2004 panner_ui().show_all ();
2006 input_button.set_sensitive (false);
2007 group_button.set_sensitive (false);
2008 set_invert_sensitive (false);
2009 meter_point_button.set_sensitive (false);
2010 mute_button->set_sensitive (false);
2011 solo_button->set_sensitive (false);
2012 rec_enable_button->set_sensitive (false);
2013 solo_isolated_led->set_sensitive (false);
2014 solo_safe_led->set_sensitive (false);
2015 monitor_input_button->set_sensitive (false);
2016 monitor_disk_button->set_sensitive (false);
2017 _comment_button.set_sensitive (false);
2019 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2020 output_button.set_sensitive (false);
2023 reset_strip_style ();
2027 MixerStrip::revert_to_default_display ()
2031 set_current_delivery (_route->main_outs ());
2033 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2034 gain_meter().setup_meters ();
2036 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2037 update_panner_choices();
2038 panner_ui().setup_pan ();
2039 panner_ui().set_send_drawing_mode (false);
2041 if (has_audio_outputs ()) {
2042 panners.show_all ();
2044 panners.hide_all ();
2047 reset_strip_style ();
2051 MixerStrip::set_button_names ()
2055 mute_button->set_text (_("Mute"));
2056 monitor_input_button->set_text (_("In"));
2057 monitor_disk_button->set_text (_("Disk"));
2059 if (_route && _route->solo_safe()) {
2060 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2062 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2064 if (!Config->get_solo_control_is_listen_control()) {
2065 solo_button->set_text (_("Solo"));
2067 switch (Config->get_listen_position()) {
2068 case AfterFaderListen:
2069 solo_button->set_text (_("AFL"));
2071 case PreFaderListen:
2072 solo_button->set_text (_("PFL"));
2076 solo_isolated_led->set_text (_("Iso"));
2077 solo_safe_led->set_text (S_("SoloLock|Lock"));
2081 mute_button->set_text (S_("Mute|M"));
2082 monitor_input_button->set_text (S_("MonitorInput|I"));
2083 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2085 if (_route && _route->solo_safe()) {
2086 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2088 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2090 if (!Config->get_solo_control_is_listen_control()) {
2091 solo_button->set_text (S_("Solo|S"));
2093 switch (Config->get_listen_position()) {
2094 case AfterFaderListen:
2095 solo_button->set_text (S_("AfterFader|A"));
2097 case PreFaderListen:
2098 solo_button->set_text (S_("Prefader|P"));
2103 solo_isolated_led->set_text (S_("SoloIso|I"));
2104 solo_safe_led->set_text (S_("SoloLock|L"));
2109 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2111 meter_point_button.set_text ("");
2116 MixerStrip::plugin_selector()
2118 return _mixer.plugin_selector();
2122 MixerStrip::hide_things ()
2124 processor_box.hide_things ();
2128 MixerStrip::input_active_button_press (GdkEventButton*)
2130 /* nothing happens on press */
2135 MixerStrip::input_active_button_release (GdkEventButton* ev)
2137 boost::shared_ptr<MidiTrack> mt = midi_track ();
2143 boost::shared_ptr<RouteList> rl (new RouteList);
2145 rl->push_back (route());
2147 _session->set_exclusive_input_active (rl, !mt->input_active(),
2148 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2154 MixerStrip::midi_input_status_changed ()
2156 if (midi_input_enable_button) {
2157 boost::shared_ptr<MidiTrack> mt = midi_track ();
2159 midi_input_enable_button->set_active (mt->input_active ());
2164 MixerStrip::state_id () const
2166 return string_compose ("strip %1", _route->id().to_s());
2170 MixerStrip::parameter_changed (string p)
2172 if (p == _visibility.get_state_name()) {
2173 /* The user has made changes to the mixer strip visibility, so get
2174 our VisibilityGroup to reflect these changes in our widgets.
2176 _visibility.set_state (ARDOUR_UI::config()->get_mixer_strip_visibility ());
2178 else if (p == "track-name-number") {
2183 /** Called to decide whether the solo isolate / solo lock button visibility should
2184 * be overridden from that configured by the user. We do this for the master bus.
2186 * @return optional value that is present if visibility state should be overridden.
2188 boost::optional<bool>
2189 MixerStrip::override_solo_visibility () const
2191 if (_route && _route->is_master ()) {
2192 return boost::optional<bool> (false);
2195 return boost::optional<bool> ();
2199 MixerStrip::add_input_port (DataType t)
2201 _route->input()->add_port ("", this, t);
2205 MixerStrip::add_output_port (DataType t)
2207 _route->output()->add_port ("", this, t);
2211 MixerStrip::route_active_changed ()
2213 reset_strip_style ();
2217 MixerStrip::copy_processors ()
2219 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2223 MixerStrip::cut_processors ()
2225 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2229 MixerStrip::paste_processors ()
2231 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2235 MixerStrip::select_all_processors ()
2237 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2241 MixerStrip::deselect_all_processors ()
2243 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2247 MixerStrip::delete_processors ()
2249 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2253 MixerStrip::toggle_processors ()
2255 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2259 MixerStrip::ab_plugins ()
2261 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2265 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2267 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2270 if (ev->button == 3) {
2271 popup_level_meter_menu (ev);
2279 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2281 using namespace Gtk::Menu_Helpers;
2283 Gtk::Menu* m = manage (new Menu);
2284 MenuList& items = m->items ();
2286 RadioMenuItem::Group group;
2288 _suspend_menu_callbacks = true;
2289 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2290 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2291 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2292 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2293 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2295 if (gpm.meter_channels().n_audio() == 0) {
2296 m->popup (ev->button, ev->time);
2297 _suspend_menu_callbacks = false;
2301 RadioMenuItem::Group tgroup;
2302 items.push_back (SeparatorElem());
2304 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2305 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2306 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2307 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2308 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2309 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2310 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2311 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2312 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2313 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2316 if (_route->is_master()) {
2319 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2320 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2321 /* non-master bus */
2324 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2331 MeterType cmt = _route->meter_type();
2332 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2334 items.push_back (SeparatorElem());
2335 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2336 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2337 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2338 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2339 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2340 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2342 m->popup (ev->button, ev->time);
2343 _suspend_menu_callbacks = false;
2347 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2348 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2350 using namespace Menu_Helpers;
2352 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2353 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2354 i->set_active (_route->meter_point() == point);
2358 MixerStrip::set_meter_point (MeterPoint p)
2360 if (_suspend_menu_callbacks) return;
2361 _route->set_meter_point (p);
2365 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2366 RadioMenuItem::Group& group, string const & name, MeterType type)
2368 using namespace Menu_Helpers;
2370 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2371 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2372 i->set_active (_route->meter_type() == type);
2376 MixerStrip::set_meter_type (MeterType t)
2378 if (_suspend_menu_callbacks) return;