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 , trim_control (ArdourKnob::default_elements, true)
101 , _visibility (X_("mixer-element-visibility"))
106 /* the editor mixer strip: don't destroy it every time
107 the underlying route goes away.
110 self_destruct = false;
114 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
118 , _mixer_owned (in_mixer)
119 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
122 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
123 , rec_mon_table (2, 2)
124 , solo_iso_table (1, 2)
125 , mute_solo_table (1, 2)
126 , bottom_button_table (1, 3)
127 , meter_point_button (_("pre"))
128 , midi_input_enable_button (0)
129 , _comment_button (_("Comments"))
130 , trim_control (ArdourKnob::default_elements, true)
131 , _visibility (X_("mixer-element-visibility"))
140 _entered_mixer_strip= 0;
143 ignore_comment_edit = false;
144 ignore_toggle = false;
149 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
150 longest_label = "longest label";
152 string t = _("Click to toggle the width of this mixer strip.");
154 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
157 width_button.set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
158 width_button.set_icon (ArdourButton::StripWidth);
159 ARDOUR_UI::instance()->set_tip (width_button, t);
161 hide_button.set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
162 hide_button.set_icon (ArdourButton::CloseCross);
163 ARDOUR_UI::instance()->set_tip (&hide_button, _("Hide this mixer strip"));
165 input_button_box.set_spacing(2);
167 input_button.set_text (_("Input"));
168 input_button.set_name ("mixer strip button");
169 input_button_box.pack_start (input_button, true, true);
171 output_button.set_text (_("Output"));
172 output_button.set_name ("mixer strip button");
174 ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Click to select metering point"), "");
175 meter_point_button.set_name ("mixer strip button");
177 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
179 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
180 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
182 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
184 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
185 solo_isolated_led->show ();
186 solo_isolated_led->set_no_show_all (true);
187 solo_isolated_led->set_name (X_("solo isolate"));
188 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
189 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
190 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
192 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
193 solo_safe_led->show ();
194 solo_safe_led->set_no_show_all (true);
195 solo_safe_led->set_name (X_("solo safe"));
196 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
197 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
198 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
200 solo_safe_led->set_text (S_("SoloLock|Lock"));
201 solo_isolated_led->set_text (_("Iso"));
203 solo_iso_table.set_homogeneous (true);
204 solo_iso_table.set_spacings (2);
205 if (!ARDOUR::Profile->get_trx()) {
206 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
207 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
209 solo_iso_table.show ();
211 rec_mon_table.set_homogeneous (true);
212 rec_mon_table.set_row_spacings (2);
213 rec_mon_table.set_col_spacings (2);
214 if (ARDOUR::Profile->get_mixbus()) {
215 rec_mon_table.resize (1, 3);
216 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
217 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
218 } else if (!ARDOUR::Profile->get_trx()) {
219 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
220 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
222 rec_mon_table.show ();
224 if (solo_isolated_led) {
225 button_size_group->add_widget (*solo_isolated_led);
228 button_size_group->add_widget (*solo_safe_led);
231 if (!ARDOUR::Profile->get_mixbus()) {
232 if (rec_enable_button) {
233 button_size_group->add_widget (*rec_enable_button);
235 if (monitor_disk_button) {
236 button_size_group->add_widget (*monitor_disk_button);
238 if (monitor_input_button) {
239 button_size_group->add_widget (*monitor_input_button);
243 mute_solo_table.set_homogeneous (true);
244 mute_solo_table.set_spacings (2);
246 bottom_button_table.set_spacings (2);
247 bottom_button_table.set_homogeneous (true);
248 bottom_button_table.attach (group_button, 1, 2, 0, 1);
249 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
251 name_button.set_name ("mixer strip button");
252 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
253 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
255 ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
256 group_button.set_name ("mixer strip button");
258 _comment_button.set_name (X_("mixer strip button"));
259 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
261 // TODO implement ArdourKnob::on_size_request properly
262 #define PX_SCALE(px) std::max((float)px, rintf((float)px * ARDOUR_UI::ui_scale))
263 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
265 trim_control.set_tooltip_prefix (_("Trim: "));
266 trim_control.set_name ("trim knob");
267 trim_control.set_no_show_all (true);
268 input_button_box.pack_start (trim_control, false, false);
270 global_vpacker.set_border_width (1);
271 global_vpacker.set_spacing (0);
273 width_button.set_name ("mixer strip button");
274 hide_button.set_name ("mixer strip button");
276 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
277 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
279 // width_hide_box.set_border_width (1);
280 width_hide_box.set_spacing (2);
281 width_hide_box.pack_start (width_button, false, true);
282 width_hide_box.pack_start (number_label, true, true);
283 width_hide_box.pack_end (hide_button, false, true);
285 number_label.set_text ("-");
286 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
287 number_label.set_no_show_all ();
288 number_label.set_name ("tracknumber label");
289 number_label.set_fixed_colors (0x80808080, 0x80808080);
290 number_label.set_alignment (.5, .5);
291 number_label.set_fallthrough_to_parent (true);
293 global_vpacker.set_spacing (2);
294 if (!ARDOUR::Profile->get_trx()) {
295 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
296 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
298 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
299 global_vpacker.pack_start (processor_box, true, true);
301 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
302 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
305 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
306 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
307 if (!ARDOUR::Profile->get_trx()) {
308 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
311 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
314 global_frame.add (global_vpacker);
315 global_frame.set_shadow_type (Gtk::SHADOW_IN);
316 global_frame.set_name ("BaseFrame");
320 /* force setting of visible selected status */
323 set_selected (false);
328 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
329 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
331 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
332 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
333 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
335 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
336 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
338 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
339 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
340 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
342 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
344 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
345 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
347 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
351 /* start off as a passthru strip. we'll correct this, if necessary,
352 in update_diskstream_display().
355 /* start off as a passthru strip. we'll correct this, if necessary,
356 in update_diskstream_display().
359 if (is_midi_track()) {
360 set_name ("MidiTrackStripBase");
362 set_name ("AudioTrackStripBase");
365 add_events (Gdk::BUTTON_RELEASE_MASK|
366 Gdk::ENTER_NOTIFY_MASK|
367 Gdk::LEAVE_NOTIFY_MASK|
369 Gdk::KEY_RELEASE_MASK);
371 set_flags (get_flags() | Gtk::CAN_FOCUS);
373 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
374 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
377 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
378 must be the same as those used in RCOptionEditor so that the configuration changes
379 are recognised when they occur.
381 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
382 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
383 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
384 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
385 _visibility.add (&output_button, X_("Output"), _("Output"), false);
386 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
388 parameter_changed (X_("mixer-element-visibility"));
389 ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
390 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
391 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
393 //watch for mouse enter/exit so we can do some stuff
394 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
395 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
397 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
400 MixerStrip::~MixerStrip ()
402 CatchDeletion (this);
404 if (this ==_entered_mixer_strip)
405 _entered_mixer_strip = NULL;
409 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
411 _entered_mixer_strip = this;
413 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
414 //because the mixerstrip control is a parent that encompasses the strip
415 deselect_all_processors();
421 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
423 //if we have moved outside our strip, but not into a child view, then deselect ourselves
424 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
425 _entered_mixer_strip= 0;
427 //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
428 gpm.gain_display.set_sensitive(false);
430 gpm.gain_display.set_sensitive(true);
432 //if we leave this mixer strip we need to clear out any selections
433 //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
440 MixerStrip::set_route (boost::shared_ptr<Route> rt)
442 //the rec/monitor stuff only shows up for tracks.
443 //the show_sends only shows up for buses.
444 //remove them all here, and we may add them back later
445 if (show_sends_button->get_parent()) {
446 rec_mon_table.remove (*show_sends_button);
448 if (rec_enable_button->get_parent()) {
449 rec_mon_table.remove (*rec_enable_button);
451 if (monitor_input_button->get_parent()) {
452 rec_mon_table.remove (*monitor_input_button);
454 if (monitor_disk_button->get_parent()) {
455 rec_mon_table.remove (*monitor_disk_button);
457 if (group_button.get_parent()) {
458 bottom_button_table.remove (group_button);
461 RouteUI::set_route (rt);
463 /* ProcessorBox needs access to _route so that it can read
466 processor_box.set_route (rt);
468 revert_to_default_display ();
470 /* unpack these from the parent and stuff them into our own
474 if (gpm.peak_display.get_parent()) {
475 gpm.peak_display.get_parent()->remove (gpm.peak_display);
477 if (gpm.gain_display.get_parent()) {
478 gpm.gain_display.get_parent()->remove (gpm.gain_display);
481 gpm.set_type (rt->meter_type());
483 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
484 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
486 if (solo_button->get_parent()) {
487 mute_solo_table.remove (*solo_button);
490 if (mute_button->get_parent()) {
491 mute_solo_table.remove (*mute_button);
494 if (route()->is_master()) {
495 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
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);
503 bottom_button_table.attach (group_button, 1, 2, 0, 1);
504 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
505 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
506 mute_button->show ();
507 solo_button->show ();
508 rec_mon_table.show ();
511 if (_mixer_owned && route()->is_master() ) {
513 HScrollbar scrollbar;
514 Gtk::Requisition requisition(scrollbar.size_request ());
515 int scrollbar_height = requisition.height;
517 spacer = manage (new EventBox);
518 spacer->set_size_request (-1, scrollbar_height+2);
519 global_vpacker.pack_start (*spacer, false, false);
524 monitor_input_button->show ();
525 monitor_disk_button->show ();
527 monitor_input_button->hide();
528 monitor_disk_button->hide ();
531 if (route()->trim() && route()->trim()->active()) {
532 trim_control.show ();
533 trim_control.set_controllable (route()->trim()->gain_control());
535 trim_control.hide ();
536 boost::shared_ptr<Controllable> none;
537 trim_control.set_controllable (none);
540 if (is_midi_track()) {
541 if (midi_input_enable_button == 0) {
542 midi_input_enable_button = manage (new ArdourButton);
543 midi_input_enable_button->set_name ("midi input button");
544 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
545 midi_input_enable_button->set_icon (ArdourButton::DinMidi);
546 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
547 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
548 ARDOUR_UI::instance()->set_tip (midi_input_enable_button, _("Enable/Disable MIDI input"));
550 input_button_box.remove (*midi_input_enable_button);
552 /* get current state */
553 midi_input_status_changed ();
554 input_button_box.pack_start (*midi_input_enable_button, false, false);
556 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
558 if (midi_input_enable_button) {
559 /* removal from the container will delete it */
560 input_button_box.remove (*midi_input_enable_button);
561 midi_input_enable_button = 0;
565 if (is_audio_track()) {
566 boost::shared_ptr<AudioTrack> at = audio_track();
567 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
572 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
573 rec_enable_button->set_sensitive (_session->writable());
574 rec_enable_button->show();
576 if (ARDOUR::Profile->get_mixbus()) {
577 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
578 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
579 } else if (ARDOUR::Profile->get_trx()) {
580 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
582 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
583 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
590 if (!_route->is_master()) {
591 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
592 show_sends_button->show();
596 meter_point_button.set_text (meter_point_string (_route->meter_point()));
598 delete route_ops_menu;
601 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
602 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
603 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
604 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
606 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
608 if (_route->panner_shell()) {
609 update_panner_choices();
610 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
613 if (is_audio_track()) {
614 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
617 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
618 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
620 set_stuff_from_route ();
622 /* now force an update of all the various elements */
624 update_mute_display ();
625 update_solo_display ();
628 route_group_changed ();
631 panners.setup_pan ();
633 if (has_audio_outputs ()) {
639 update_diskstream_display ();
640 update_input_display ();
641 update_output_display ();
643 add_events (Gdk::BUTTON_RELEASE_MASK);
645 processor_box.show ();
647 if (!route()->is_master() && !route()->is_monitor()) {
648 /* we don't allow master or control routes to be hidden */
653 gpm.reset_peak_display ();
654 gpm.gain_display.show ();
655 gpm.peak_display.show ();
658 width_hide_box.show();
660 global_vpacker.show();
661 mute_solo_table.show();
662 bottom_button_table.show();
664 meter_point_button.show();
665 input_button_box.show_all();
666 output_button.show();
668 _comment_button.show();
670 gpm.gain_automation_state_button.show();
672 parameter_changed ("mixer-element-visibility");
678 MixerStrip::set_stuff_from_route ()
680 /* if width is not set, it will be set by the MixerUI or editor */
682 string str = gui_property ("strip-width");
684 set_width_enum (Width (string_2_enum (str, _width)), this);
689 MixerStrip::set_width_enum (Width w, void* owner)
691 /* always set the gpm width again, things may be hidden */
694 panners.set_width (w);
696 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
698 _width_owner = owner;
702 if (_width_owner == this) {
703 set_gui_property ("strip-width", enum_2_string (_width));
708 const float scale = std::max(1.f, ARDOUR_UI::ui_scale);
713 if (show_sends_button) {
714 show_sends_button->set_text (_("Aux"));
717 gpm.gain_automation_style_button.set_text (
718 gpm.astyle_string(gain_automation->automation_style()));
719 gpm.gain_automation_state_button.set_text (
720 gpm.astate_string(gain_automation->automation_state()));
722 if (_route->panner()) {
723 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
724 panners.astyle_string(_route->panner()->automation_style()));
725 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
726 panners.astate_string(_route->panner()->automation_state()));
730 // panners expect an even number of horiz. pixels
731 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
733 set_size_request (width, -1);
739 if (show_sends_button) {
740 show_sends_button->set_text (_("Snd"));
743 gpm.gain_automation_style_button.set_text (
744 gpm.short_astyle_string(gain_automation->automation_style()));
745 gpm.gain_automation_state_button.set_text (
746 gpm.short_astate_string(gain_automation->automation_state()));
747 gain_meter().setup_meters (); // recalc meter width
749 if (_route->panner()) {
750 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
751 panners.short_astyle_string(_route->panner()->automation_style()));
752 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
753 panners.short_astate_string(_route->panner()->automation_state()));
757 // panners expect an even number of horiz. pixels
758 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
760 set_size_request (width, -1);
765 processor_box.set_width (w);
767 update_input_display ();
768 update_output_display ();
769 setup_comment_button ();
770 route_group_changed ();
776 MixerStrip::set_packed (bool yn)
781 set_gui_property ("visible", true);
783 set_gui_property ("visible", false);
788 struct RouteCompareByName {
789 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
790 return a->name().compare (b->name()) < 0;
795 MixerStrip::output_release (GdkEventButton *ev)
797 switch (ev->button) {
799 edit_output_configuration ();
807 MixerStrip::output_press (GdkEventButton *ev)
809 using namespace Menu_Helpers;
810 if (!_session->engine().connected()) {
811 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
816 MenuList& citems = output_menu.items();
817 switch (ev->button) {
820 return false; //wait for the mouse-up to pop the dialog
824 output_menu.set_name ("ArdourContextMenu");
826 output_menu_bundles.clear ();
828 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
830 citems.push_back (SeparatorElem());
831 uint32_t const n_with_separator = citems.size ();
833 ARDOUR::BundleList current = _route->output()->bundles_connected ();
835 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
837 /* give user bundles first chance at being in the menu */
839 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
840 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
841 maybe_add_bundle_to_output_menu (*i, current);
845 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
846 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
847 maybe_add_bundle_to_output_menu (*i, current);
851 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
852 RouteList copy = *routes;
853 copy.sort (RouteCompareByName ());
854 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
855 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
858 if (citems.size() == n_with_separator) {
859 /* no routes added; remove the separator */
863 citems.push_back (SeparatorElem());
865 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
868 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
869 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
874 citems.push_back (SeparatorElem());
875 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
877 output_menu.popup (1, ev->time);
888 MixerStrip::input_release (GdkEventButton *ev)
890 switch (ev->button) {
893 edit_input_configuration ();
905 MixerStrip::input_press (GdkEventButton *ev)
907 using namespace Menu_Helpers;
909 MenuList& citems = input_menu.items();
910 input_menu.set_name ("ArdourContextMenu");
913 if (!_session->engine().connected()) {
914 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
919 if (_session->actively_recording() && _route->record_enabled())
922 switch (ev->button) {
925 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
929 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
931 citems.push_back (SeparatorElem());
932 uint32_t const n_with_separator = citems.size ();
934 input_menu_bundles.clear ();
936 ARDOUR::BundleList current = _route->input()->bundles_connected ();
938 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
940 /* give user bundles first chance at being in the menu */
942 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
943 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
944 maybe_add_bundle_to_input_menu (*i, current);
948 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
949 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
950 maybe_add_bundle_to_input_menu (*i, current);
954 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
955 RouteList copy = *routes;
956 copy.sort (RouteCompareByName ());
957 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
958 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
961 if (citems.size() == n_with_separator) {
962 /* no routes added; remove the separator */
966 citems.push_back (SeparatorElem());
967 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
970 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
971 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
976 citems.push_back (SeparatorElem());
977 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
979 input_menu.popup (1, ev->time);
990 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
996 ARDOUR::BundleList current = _route->input()->bundles_connected ();
998 if (std::find (current.begin(), current.end(), c) == current.end()) {
999 _route->input()->connect_ports_to_bundle (c, true, this);
1001 _route->input()->disconnect_ports_from_bundle (c, this);
1006 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1008 if (ignore_toggle) {
1012 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1014 if (std::find (current.begin(), current.end(), c) == current.end()) {
1015 _route->output()->connect_ports_to_bundle (c, true, this);
1017 _route->output()->disconnect_ports_from_bundle (c, this);
1022 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1024 using namespace Menu_Helpers;
1026 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1030 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1031 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1035 if (i != input_menu_bundles.end()) {
1039 input_menu_bundles.push_back (b);
1041 MenuList& citems = input_menu.items();
1043 std::string n = b->name ();
1044 replace_all (n, "_", " ");
1046 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1050 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1052 using namespace Menu_Helpers;
1054 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1058 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1059 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1063 if (i != output_menu_bundles.end()) {
1067 output_menu_bundles.push_back (b);
1069 MenuList& citems = output_menu.items();
1071 std::string n = b->name ();
1072 replace_all (n, "_", " ");
1074 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1078 MixerStrip::update_diskstream_display ()
1080 if (is_track() && input_selector) {
1081 input_selector->hide_all ();
1084 route_color_changed ();
1088 MixerStrip::connect_to_pan ()
1090 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1092 panstate_connection.disconnect ();
1093 panstyle_connection.disconnect ();
1095 if (!_route->panner()) {
1099 boost::shared_ptr<Pannable> p = _route->pannable ();
1101 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1102 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1104 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1105 * However, that only works a panner was previously set.
1107 * PannerUI must remain subscribed to _panshell->Changed() in case
1108 * we switch the panner eg. AUX-Send and back
1109 * _route->panner_shell()->Changed() vs _panshell->Changed
1111 if (panners._panner == 0) {
1112 panners.panshell_changed ();
1114 update_panner_choices();
1118 MixerStrip::update_panner_choices ()
1120 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1121 if (!_route->panner_shell()) { return; }
1123 uint32_t in = _route->output()->n_ports().n_audio();
1125 if (_route->panner()) {
1126 in = _route->panner()->in().n_audio();
1129 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1133 * Output port labelling
1134 * =====================
1136 * Case 1: Each output has one connection, all connections are to system:playback_%i
1137 * out 1 -> system:playback_1
1138 * out 2 -> system:playback_2
1139 * out 3 -> system:playback_3
1142 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1143 * out 1 -> ardour:track_x/in 1
1144 * out 2 -> ardour:track_x/in 2
1145 * Display as: track_x
1147 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1148 * out 1 -> program x:foo
1149 * out 2 -> program x:foo
1150 * Display as: program x
1152 * Case 4: No connections (Disconnected)
1155 * Default case (unusual routing):
1156 * Display as: *number of connections*
1160 * .-----------------------------------------------.
1162 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1163 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1164 * '-----------------------------------------------'
1165 * .-----------------------------------------------.
1168 * '-----------------------------------------------'
1172 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1176 boost::shared_ptr<Port> port;
1177 vector<string> port_connections;
1179 uint32_t total_connection_count = 0;
1180 uint32_t io_connection_count = 0;
1181 uint32_t ardour_connection_count = 0;
1182 uint32_t system_connection_count = 0;
1183 uint32_t other_connection_count = 0;
1185 ostringstream label;
1187 bool have_label = false;
1188 bool each_io_has_one_connection = true;
1190 string connection_name;
1191 string ardour_track_name;
1192 string other_connection_type;
1193 string system_ports;
1196 ostringstream tooltip;
1197 char * tooltip_cstr;
1199 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1200 DataType dt = DataType::AUDIO;
1201 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 )
1202 dt = DataType::MIDI;
1205 io_count = route->n_inputs().n_total();
1206 tooltip << string_compose (_("<b>INPUT</b> to %1"), Glib::Markup::escape_text(route->name()));
1208 io_count = route->n_outputs().n_total();
1209 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(route->name()));
1213 for (io_index = 0; io_index < io_count; ++io_index) {
1215 port = route->input()->nth (io_index);
1217 port = route->output()->nth (io_index);
1220 //ignore any port connections that don't match our DataType
1221 if (port->type() != dt)
1224 port_connections.clear ();
1225 port->get_connections(port_connections);
1226 io_connection_count = 0;
1228 if (!port_connections.empty()) {
1229 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1231 string& connection_name (*i);
1233 if (connection_name.find("system:") == 0) {
1234 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1237 if (io_connection_count == 0) {
1238 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1240 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1243 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1246 if (connection_name.find("ardour:") == 0) {
1247 if (ardour_track_name.empty()) {
1248 // "ardour:Master/in 1" -> "ardour:Master/"
1249 string::size_type slash = connection_name.find("/");
1250 if (slash != string::npos) {
1251 ardour_track_name = connection_name.substr(0, slash + 1);
1255 if (connection_name.find(ardour_track_name) == 0) {
1256 ++ardour_connection_count;
1258 } else if (!pn.empty()) {
1259 if (system_ports.empty()) {
1262 system_ports += "/" + pn;
1264 if (connection_name.find("system:") == 0) {
1265 ++system_connection_count;
1267 } else if (connection_name.find("system:midi_") == 0) {
1269 // "system:midi_capture_123" -> "123"
1270 system_port = "M " + connection_name.substr(20);
1272 // "system:midi_playback_123" -> "123"
1273 system_port = "M " + connection_name.substr(21);
1276 if (system_ports.empty()) {
1277 system_ports += system_port;
1279 system_ports += "/" + system_port;
1282 ++system_connection_count;
1284 } else if (connection_name.find("system:") == 0) {
1286 // "system:capture_123" -> "123"
1287 system_port = connection_name.substr(15);
1289 // "system:playback_123" -> "123"
1290 system_port = connection_name.substr(16);
1293 if (system_ports.empty()) {
1294 system_ports += system_port;
1296 system_ports += "/" + system_port;
1299 ++system_connection_count;
1301 if (other_connection_type.empty()) {
1302 // "jamin:in 1" -> "jamin:"
1303 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1306 if (connection_name.find(other_connection_type) == 0) {
1307 ++other_connection_count;
1311 ++total_connection_count;
1312 ++io_connection_count;
1316 if (io_connection_count != 1) {
1317 each_io_has_one_connection = false;
1321 if (total_connection_count == 0) {
1322 tooltip << endl << _("Disconnected");
1325 tooltip_cstr = new char[tooltip.str().size() + 1];
1326 strcpy(tooltip_cstr, tooltip.str().c_str());
1329 ARDOUR_UI::instance()->set_tip (&input_button, tooltip_cstr, "");
1331 ARDOUR_UI::instance()->set_tip (&output_button, tooltip_cstr, "");
1334 if (each_io_has_one_connection) {
1335 if (total_connection_count == ardour_connection_count) {
1336 // all connections are to the same track in ardour
1337 // "ardour:Master/" -> "Master"
1338 string::size_type slash = ardour_track_name.find("/");
1339 if (slash != string::npos) {
1340 label << ardour_track_name.substr(7, slash - 7);
1344 else if (total_connection_count == system_connection_count) {
1345 // all connections are to system ports
1346 label << system_ports;
1349 else if (total_connection_count == other_connection_count) {
1350 // all connections are to the same external program eg jamin
1351 // "jamin:" -> "jamin"
1352 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1358 if (total_connection_count == 0) {
1362 // Odd configuration
1363 label << "*" << total_connection_count << "*";
1368 input_button.set_text (label.str());
1370 output_button.set_text (label.str());
1375 MixerStrip::update_input_display ()
1377 update_io_button (_route, _width, true);
1378 panners.setup_pan ();
1380 if (has_audio_outputs ()) {
1381 panners.show_all ();
1383 panners.hide_all ();
1389 MixerStrip::update_output_display ()
1391 update_io_button (_route, _width, false);
1392 gpm.setup_meters ();
1393 panners.setup_pan ();
1395 if (has_audio_outputs ()) {
1396 panners.show_all ();
1398 panners.hide_all ();
1403 MixerStrip::fast_update ()
1405 gpm.update_meters ();
1409 MixerStrip::diskstream_changed ()
1411 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1415 MixerStrip::io_changed_proxy ()
1417 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1421 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1423 boost::shared_ptr<Port> a = wa.lock ();
1424 boost::shared_ptr<Port> b = wb.lock ();
1426 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1427 update_input_display ();
1428 set_width_enum (_width, this);
1431 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1432 update_output_display ();
1433 set_width_enum (_width, this);
1438 MixerStrip::setup_comment_button ()
1443 if (_route->comment().empty ()) {
1444 _comment_button.unset_bg (STATE_NORMAL);
1445 _comment_button.set_text (_("Comments"));
1447 _comment_button.modify_bg (STATE_NORMAL, color ());
1448 _comment_button.set_text (_("*Comments*"));
1453 if (_route->comment().empty ()) {
1454 _comment_button.unset_bg (STATE_NORMAL);
1455 _comment_button.set_text (_("Cmt"));
1457 _comment_button.modify_bg (STATE_NORMAL, color ());
1458 _comment_button.set_text (_("*Cmt*"));
1463 ARDOUR_UI::instance()->set_tip (
1464 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1470 MixerStrip::select_route_group (GdkEventButton *ev)
1472 using namespace Menu_Helpers;
1474 if (ev->button == 1) {
1476 if (group_menu == 0) {
1478 PropertyList* plist = new PropertyList();
1480 plist->add (Properties::gain, true);
1481 plist->add (Properties::mute, true);
1482 plist->add (Properties::solo, true);
1484 group_menu = new RouteGroupMenu (_session, plist);
1488 r.push_back (route ());
1489 group_menu->build (r);
1490 group_menu->menu()->popup (1, ev->time);
1497 MixerStrip::route_group_changed ()
1499 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1501 RouteGroup *rg = _route->route_group();
1504 group_button.set_text (PBD::short_version (rg->name(), 5));
1508 group_button.set_text (_("Grp"));
1511 group_button.set_text (_("~G"));
1518 MixerStrip::route_color_changed ()
1520 name_button.modify_bg (STATE_NORMAL, color());
1521 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1522 reset_strip_style ();
1526 MixerStrip::show_passthru_color ()
1528 reset_strip_style ();
1532 MixerStrip::build_route_ops_menu ()
1534 using namespace Menu_Helpers;
1535 route_ops_menu = new Menu;
1536 route_ops_menu->set_name ("ArdourContextMenu");
1538 MenuList& items = route_ops_menu->items();
1540 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1542 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1544 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1546 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1548 items.push_back (SeparatorElem());
1550 if (!_route->is_master()) {
1551 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1553 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1554 rename_menu_item = &items.back();
1556 items.push_back (SeparatorElem());
1557 items.push_back (CheckMenuElem (_("Active")));
1558 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1559 i->set_active (_route->active());
1560 i->set_sensitive(! _session->transport_rolling());
1561 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1563 items.push_back (SeparatorElem());
1565 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1567 items.push_back (SeparatorElem());
1568 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1569 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1570 denormal_menu_item->set_active (_route->denormal_protection());
1572 if (!Profile->get_sae()) {
1573 items.push_back (SeparatorElem());
1574 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1577 items.push_back (SeparatorElem());
1580 /* note that this relies on selection being shared across editor and
1581 mixer (or global to the backend, in the future), which is the only
1582 sane thing for users anyway.
1585 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1587 Selection& selection (PublicEditor::instance().get_selection());
1588 if (!selection.selected (rtav)) {
1589 selection.set (rtav);
1592 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1598 MixerStrip::name_button_button_press (GdkEventButton* ev)
1600 if (ev->button == 3) {
1601 list_route_operations ();
1603 /* do not allow rename if the track is record-enabled */
1604 rename_menu_item->set_sensitive (!_route->record_enabled());
1605 route_ops_menu->popup (1, ev->time);
1614 MixerStrip::name_button_button_release (GdkEventButton* ev)
1616 if (ev->button == 1) {
1617 list_route_operations ();
1619 /* do not allow rename if the track is record-enabled */
1620 rename_menu_item->set_sensitive (!_route->record_enabled());
1621 route_ops_menu->popup (1, ev->time);
1628 MixerStrip::number_button_button_press (GdkEventButton* ev)
1630 if ( ev->button == 3 ) {
1631 list_route_operations ();
1633 /* do not allow rename if the track is record-enabled */
1634 rename_menu_item->set_sensitive (!_route->record_enabled());
1635 route_ops_menu->popup (1, ev->time);
1644 MixerStrip::list_route_operations ()
1646 delete route_ops_menu;
1647 build_route_ops_menu ();
1651 MixerStrip::set_selected (bool yn)
1653 AxisView::set_selected (yn);
1655 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1656 global_frame.set_name ("MixerStripSelectedFrame");
1658 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1659 global_frame.set_name ("MixerStripFrame");
1661 global_frame.queue_draw ();
1664 // processor_box.deselect_all_processors();
1668 MixerStrip::property_changed (const PropertyChange& what_changed)
1670 RouteUI::property_changed (what_changed);
1672 if (what_changed.contains (ARDOUR::Properties::name)) {
1678 MixerStrip::name_changed ()
1682 name_button.set_text (_route->name());
1685 name_button.set_text (PBD::short_version (_route->name(), 5));
1689 ARDOUR_UI::instance()->set_tip (name_button, _route->name());
1691 if (_session->config.get_track_name_number()) {
1692 const int64_t track_number = _route->track_number ();
1693 if (track_number == 0) {
1694 number_label.set_text ("-");
1696 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1699 number_label.set_text ("");
1704 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1706 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1710 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1712 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1716 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1718 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1722 MixerStrip::width_button_pressed (GdkEventButton* ev)
1724 if (ev->button != 1) {
1728 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1731 _mixer.set_strip_width (Narrow, true);
1735 _mixer.set_strip_width (Wide, true);
1741 set_width_enum (Narrow, this);
1744 set_width_enum (Wide, this);
1753 MixerStrip::hide_clicked ()
1755 // LAME fix to reset the button status for when it is redisplayed (part 1)
1756 hide_button.set_sensitive(false);
1759 Hiding(); /* EMIT_SIGNAL */
1761 _mixer.hide_strip (this);
1765 hide_button.set_sensitive(true);
1769 MixerStrip::set_embedded (bool yn)
1775 MixerStrip::map_frozen ()
1777 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1779 boost::shared_ptr<AudioTrack> at = audio_track();
1782 switch (at->freeze_state()) {
1783 case AudioTrack::Frozen:
1784 processor_box.set_sensitive (false);
1785 hide_redirect_editors ();
1788 processor_box.set_sensitive (true);
1789 // XXX need some way, maybe, to retoggle redirect editors
1796 MixerStrip::hide_redirect_editors ()
1798 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1802 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1804 boost::shared_ptr<Processor> processor (p.lock ());
1809 Gtk::Window* w = processor_box.get_processor_ui (processor);
1817 MixerStrip::reset_strip_style ()
1819 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1821 gpm.set_fader_name ("SendStripBase");
1825 if (is_midi_track()) {
1826 if (_route->active()) {
1827 set_name ("MidiTrackStripBase");
1829 set_name ("MidiTrackStripBaseInactive");
1831 gpm.set_fader_name ("MidiTrackFader");
1832 } else if (is_audio_track()) {
1833 if (_route->active()) {
1834 set_name ("AudioTrackStripBase");
1836 set_name ("AudioTrackStripBaseInactive");
1838 gpm.set_fader_name ("AudioTrackFader");
1840 if (_route->active()) {
1841 set_name ("AudioBusStripBase");
1843 set_name ("AudioBusStripBaseInactive");
1845 gpm.set_fader_name ("AudioBusFader");
1847 /* (no MIDI busses yet) */
1854 MixerStrip::engine_stopped ()
1859 MixerStrip::engine_running ()
1864 MixerStrip::meter_point_string (MeterPoint mp)
1877 case MeterPostFader:
1894 return S_("Meter|In");
1898 return S_("Meter|Pr");
1901 case MeterPostFader:
1902 return S_("Meter|Po");
1906 return S_("Meter|O");
1911 return S_("Meter|C");
1920 /** Called when the metering point has changed */
1922 MixerStrip::meter_changed ()
1924 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1925 gpm.setup_meters ();
1926 // reset peak when meter point changes
1927 gpm.reset_peak_display();
1930 /** The bus that we are displaying sends to has changed, or been turned off.
1931 * @param send_to New bus that we are displaying sends to, or 0.
1934 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1936 RouteUI::bus_send_display_changed (send_to);
1939 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1944 revert_to_default_display ();
1947 revert_to_default_display ();
1952 MixerStrip::drop_send ()
1954 boost::shared_ptr<Send> current_send;
1956 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1957 current_send->set_metering (false);
1960 send_gone_connection.disconnect ();
1961 input_button.set_sensitive (true);
1962 output_button.set_sensitive (true);
1963 group_button.set_sensitive (true);
1964 set_invert_sensitive (true);
1965 meter_point_button.set_sensitive (true);
1966 mute_button->set_sensitive (true);
1967 solo_button->set_sensitive (true);
1968 rec_enable_button->set_sensitive (true);
1969 solo_isolated_led->set_sensitive (true);
1970 solo_safe_led->set_sensitive (true);
1971 monitor_input_button->set_sensitive (true);
1972 monitor_disk_button->set_sensitive (true);
1973 _comment_button.set_sensitive (true);
1977 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1979 _current_delivery = d;
1980 DeliveryChanged (_current_delivery);
1984 MixerStrip::show_send (boost::shared_ptr<Send> send)
1990 set_current_delivery (send);
1992 send->meter()->set_type(_route->shared_peak_meter()->get_type());
1993 send->set_metering (true);
1994 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
1996 gain_meter().set_controls (_route, send->meter(), send->amp());
1997 gain_meter().setup_meters ();
1999 uint32_t const in = _current_delivery->pans_required();
2000 uint32_t const out = _current_delivery->pan_outs();
2002 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2003 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2004 panner_ui().setup_pan ();
2005 panner_ui().set_send_drawing_mode (true);
2006 panner_ui().show_all ();
2008 input_button.set_sensitive (false);
2009 group_button.set_sensitive (false);
2010 set_invert_sensitive (false);
2011 meter_point_button.set_sensitive (false);
2012 mute_button->set_sensitive (false);
2013 solo_button->set_sensitive (false);
2014 rec_enable_button->set_sensitive (false);
2015 solo_isolated_led->set_sensitive (false);
2016 solo_safe_led->set_sensitive (false);
2017 monitor_input_button->set_sensitive (false);
2018 monitor_disk_button->set_sensitive (false);
2019 _comment_button.set_sensitive (false);
2021 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2022 output_button.set_sensitive (false);
2025 reset_strip_style ();
2029 MixerStrip::revert_to_default_display ()
2033 set_current_delivery (_route->main_outs ());
2035 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2036 gain_meter().setup_meters ();
2038 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2039 update_panner_choices();
2040 panner_ui().setup_pan ();
2041 panner_ui().set_send_drawing_mode (false);
2043 if (has_audio_outputs ()) {
2044 panners.show_all ();
2046 panners.hide_all ();
2049 reset_strip_style ();
2053 MixerStrip::set_button_names ()
2057 mute_button->set_text (_("Mute"));
2058 monitor_input_button->set_text (_("In"));
2059 monitor_disk_button->set_text (_("Disk"));
2061 if (_route && _route->solo_safe()) {
2062 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2064 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2066 if (!Config->get_solo_control_is_listen_control()) {
2067 solo_button->set_text (_("Solo"));
2069 switch (Config->get_listen_position()) {
2070 case AfterFaderListen:
2071 solo_button->set_text (_("AFL"));
2073 case PreFaderListen:
2074 solo_button->set_text (_("PFL"));
2078 solo_isolated_led->set_text (_("Iso"));
2079 solo_safe_led->set_text (S_("SoloLock|Lock"));
2083 mute_button->set_text (S_("Mute|M"));
2084 monitor_input_button->set_text (S_("MonitorInput|I"));
2085 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2087 if (_route && _route->solo_safe()) {
2088 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2090 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2092 if (!Config->get_solo_control_is_listen_control()) {
2093 solo_button->set_text (S_("Solo|S"));
2095 switch (Config->get_listen_position()) {
2096 case AfterFaderListen:
2097 solo_button->set_text (S_("AfterFader|A"));
2099 case PreFaderListen:
2100 solo_button->set_text (S_("Prefader|P"));
2105 solo_isolated_led->set_text (S_("SoloIso|I"));
2106 solo_safe_led->set_text (S_("SoloLock|L"));
2111 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2113 meter_point_button.set_text ("");
2118 MixerStrip::plugin_selector()
2120 return _mixer.plugin_selector();
2124 MixerStrip::hide_things ()
2126 processor_box.hide_things ();
2130 MixerStrip::input_active_button_press (GdkEventButton*)
2132 /* nothing happens on press */
2137 MixerStrip::input_active_button_release (GdkEventButton* ev)
2139 boost::shared_ptr<MidiTrack> mt = midi_track ();
2145 boost::shared_ptr<RouteList> rl (new RouteList);
2147 rl->push_back (route());
2149 _session->set_exclusive_input_active (rl, !mt->input_active(),
2150 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2156 MixerStrip::midi_input_status_changed ()
2158 if (midi_input_enable_button) {
2159 boost::shared_ptr<MidiTrack> mt = midi_track ();
2161 midi_input_enable_button->set_active (mt->input_active ());
2166 MixerStrip::state_id () const
2168 return string_compose ("strip %1", _route->id().to_s());
2172 MixerStrip::parameter_changed (string p)
2174 if (p == _visibility.get_state_name()) {
2175 /* The user has made changes to the mixer strip visibility, so get
2176 our VisibilityGroup to reflect these changes in our widgets.
2178 _visibility.set_state (ARDOUR_UI::config()->get_mixer_strip_visibility ());
2180 else if (p == "track-name-number") {
2185 /** Called to decide whether the solo isolate / solo lock button visibility should
2186 * be overridden from that configured by the user. We do this for the master bus.
2188 * @return optional value that is present if visibility state should be overridden.
2190 boost::optional<bool>
2191 MixerStrip::override_solo_visibility () const
2193 if (_route && _route->is_master ()) {
2194 return boost::optional<bool> (false);
2197 return boost::optional<bool> ();
2201 MixerStrip::add_input_port (DataType t)
2203 _route->input()->add_port ("", this, t);
2207 MixerStrip::add_output_port (DataType t)
2209 _route->output()->add_port ("", this, t);
2213 MixerStrip::route_active_changed ()
2215 reset_strip_style ();
2219 MixerStrip::copy_processors ()
2221 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2225 MixerStrip::cut_processors ()
2227 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2231 MixerStrip::paste_processors ()
2233 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2237 MixerStrip::select_all_processors ()
2239 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2243 MixerStrip::deselect_all_processors ()
2245 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2249 MixerStrip::delete_processors ()
2251 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2255 MixerStrip::toggle_processors ()
2257 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2261 MixerStrip::ab_plugins ()
2263 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2267 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2269 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2272 if (ev->button == 3) {
2273 popup_level_meter_menu (ev);
2281 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2283 using namespace Gtk::Menu_Helpers;
2285 Gtk::Menu* m = manage (new Menu);
2286 MenuList& items = m->items ();
2288 RadioMenuItem::Group group;
2290 _suspend_menu_callbacks = true;
2291 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2292 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2293 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2294 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2295 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2297 if (gpm.meter_channels().n_audio() == 0) {
2298 m->popup (ev->button, ev->time);
2299 _suspend_menu_callbacks = false;
2303 RadioMenuItem::Group tgroup;
2304 items.push_back (SeparatorElem());
2306 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2307 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2308 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2309 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2310 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2311 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2312 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2313 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2314 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2315 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2318 if (_route->is_master()) {
2321 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2322 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2323 /* non-master bus */
2326 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2333 MeterType cmt = _route->meter_type();
2334 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2336 items.push_back (SeparatorElem());
2337 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2338 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2339 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2340 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2341 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2342 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2344 m->popup (ev->button, ev->time);
2345 _suspend_menu_callbacks = false;
2349 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2350 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2352 using namespace Menu_Helpers;
2354 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2355 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2356 i->set_active (_route->meter_point() == point);
2360 MixerStrip::set_meter_point (MeterPoint p)
2362 if (_suspend_menu_callbacks) return;
2363 _route->set_meter_point (p);
2367 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2368 RadioMenuItem::Group& group, string const & name, MeterType type)
2370 using namespace Menu_Helpers;
2372 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2373 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2374 i->set_active (_route->meter_type() == type);
2378 MixerStrip::set_meter_type (MeterType t)
2380 if (_suspend_menu_callbacks) return;