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/audio_track.h"
38 #include "ardour/audioengine.h"
39 #include "ardour/internal_send.h"
40 #include "ardour/meter.h"
41 #include "ardour/midi_track.h"
42 #include "ardour/pannable.h"
43 #include "ardour/panner.h"
44 #include "ardour/panner_shell.h"
45 #include "ardour/panner_manager.h"
46 #include "ardour/port.h"
47 #include "ardour/profile.h"
48 #include "ardour/route.h"
49 #include "ardour/route_group.h"
50 #include "ardour/send.h"
51 #include "ardour/session.h"
52 #include "ardour/types.h"
53 #include "ardour/user_bundle.h"
55 #include "ardour_ui.h"
56 #include "ardour_window.h"
57 #include "mixer_strip.h"
60 #include "ardour_button.h"
61 #include "public_editor.h"
63 #include "io_selector.h"
65 #include "gui_thread.h"
66 #include "route_group_menu.h"
67 #include "meter_patterns.h"
71 using namespace ARDOUR;
72 using namespace ARDOUR_UI_UTILS;
75 using namespace Gtkmm2ext;
77 using namespace ArdourMeter;
79 MixerStrip* MixerStrip::_entered_mixer_strip;
81 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
83 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
87 , _mixer_owned (in_mixer)
88 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
91 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
92 , rec_mon_table (2, 2)
93 , solo_iso_table (1, 2)
94 , mute_solo_table (1, 2)
95 , bottom_button_table (1, 3)
96 , meter_point_button (_("pre"))
97 , midi_input_enable_button (0)
98 , _comment_button (_("Comments"))
99 , _visibility (X_("mixer-element-visibility"))
104 /* the editor mixer strip: don't destroy it every time
105 the underlying route goes away.
108 self_destruct = false;
112 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
116 , _mixer_owned (in_mixer)
117 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
120 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
121 , rec_mon_table (2, 2)
122 , solo_iso_table (1, 2)
123 , mute_solo_table (1, 2)
124 , bottom_button_table (1, 3)
125 , meter_point_button (_("pre"))
126 , midi_input_enable_button (0)
127 , _comment_button (_("Comments"))
128 , _visibility (X_("mixer-element-visibility"))
137 _entered_mixer_strip= 0;
140 ignore_comment_edit = false;
141 ignore_toggle = false;
146 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
147 longest_label = "longest label";
149 string t = _("Click to toggle the width of this mixer strip.");
151 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
154 width_button.set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
155 width_button.set_icon (ArdourButton::StripWidth);
156 ARDOUR_UI::instance()->set_tip (width_button, t);
158 hide_button.set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
159 hide_button.set_icon (ArdourButton::CloseCross);
160 ARDOUR_UI::instance()->set_tip (&hide_button, _("Hide this mixer strip"));
162 input_button_box.set_spacing(2);
164 input_button.set_text (_("Input"));
165 input_button.set_name ("mixer strip button");
166 input_button_box.pack_start (input_button, true, true);
168 output_button.set_text (_("Output"));
169 output_button.set_name ("mixer strip button");
171 ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Click to select metering point"), "");
172 meter_point_button.set_name ("mixer strip button");
174 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
176 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
177 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
179 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
181 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
182 solo_isolated_led->show ();
183 solo_isolated_led->set_no_show_all (true);
184 solo_isolated_led->set_name (X_("solo isolate"));
185 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
186 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
187 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
189 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
190 solo_safe_led->show ();
191 solo_safe_led->set_no_show_all (true);
192 solo_safe_led->set_name (X_("solo safe"));
193 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
194 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
195 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
197 solo_safe_led->set_text (S_("SoloLock|Lock"));
198 solo_isolated_led->set_text (_("Iso"));
200 solo_iso_table.set_homogeneous (true);
201 solo_iso_table.set_spacings (2);
202 if (!ARDOUR::Profile->get_trx()) {
203 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
204 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
206 solo_iso_table.show ();
208 rec_mon_table.set_homogeneous (true);
209 rec_mon_table.set_row_spacings (2);
210 rec_mon_table.set_col_spacings (2);
211 if (ARDOUR::Profile->get_mixbus()) {
212 rec_mon_table.resize (1, 3);
213 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
214 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
215 } else if (!ARDOUR::Profile->get_trx()) {
216 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
217 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
219 rec_mon_table.show ();
221 if (solo_isolated_led) {
222 button_size_group->add_widget (*solo_isolated_led);
225 button_size_group->add_widget (*solo_safe_led);
228 if (!ARDOUR::Profile->get_mixbus()) {
229 if (rec_enable_button) {
230 button_size_group->add_widget (*rec_enable_button);
232 if (monitor_disk_button) {
233 button_size_group->add_widget (*monitor_disk_button);
235 if (monitor_input_button) {
236 button_size_group->add_widget (*monitor_input_button);
240 mute_solo_table.set_homogeneous (true);
241 mute_solo_table.set_spacings (2);
243 bottom_button_table.set_spacings (2);
244 bottom_button_table.set_homogeneous (true);
245 bottom_button_table.attach (group_button, 1, 2, 0, 1);
246 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
248 name_button.set_name ("mixer strip button");
249 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
250 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
252 ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
253 group_button.set_name ("mixer strip button");
255 _comment_button.set_name (X_("mixer strip button"));
256 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
258 global_vpacker.set_border_width (1);
259 global_vpacker.set_spacing (0);
261 width_button.set_name ("mixer strip button");
262 hide_button.set_name ("mixer strip button");
264 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
265 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
267 // width_hide_box.set_border_width (1);
268 width_hide_box.set_spacing (2);
269 width_hide_box.pack_start (width_button, false, true);
270 width_hide_box.pack_start (number_label, true, true);
271 width_hide_box.pack_end (hide_button, false, true);
273 number_label.set_text ("-");
274 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
275 number_label.set_no_show_all ();
276 number_label.set_name ("tracknumber label");
277 number_label.set_fixed_colors (0x80808080, 0x80808080);
278 number_label.set_alignment (.5, .5);
279 number_label.set_fallthrough_to_parent (true);
281 global_vpacker.set_spacing (2);
282 if (!ARDOUR::Profile->get_trx()) {
283 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
284 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
285 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
286 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
287 global_vpacker.pack_start (processor_box, true, true);
289 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
290 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
291 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
292 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
293 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
294 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
295 if (!ARDOUR::Profile->get_trx()) {
296 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
299 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
302 global_frame.add (global_vpacker);
303 global_frame.set_shadow_type (Gtk::SHADOW_IN);
304 global_frame.set_name ("BaseFrame");
308 /* force setting of visible selected status */
311 set_selected (false);
316 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
317 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
319 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
320 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
321 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
323 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
324 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
326 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
327 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
328 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
330 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
332 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
333 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
335 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
339 /* start off as a passthru strip. we'll correct this, if necessary,
340 in update_diskstream_display().
343 /* start off as a passthru strip. we'll correct this, if necessary,
344 in update_diskstream_display().
347 if (is_midi_track()) {
348 set_name ("MidiTrackStripBase");
350 set_name ("AudioTrackStripBase");
353 add_events (Gdk::BUTTON_RELEASE_MASK|
354 Gdk::ENTER_NOTIFY_MASK|
355 Gdk::LEAVE_NOTIFY_MASK|
357 Gdk::KEY_RELEASE_MASK);
359 set_flags (get_flags() | Gtk::CAN_FOCUS);
361 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
362 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
365 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
366 must be the same as those used in RCOptionEditor so that the configuration changes
367 are recognised when they occur.
369 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
370 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
371 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
372 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
373 _visibility.add (&output_button, X_("Output"), _("Output"), false);
374 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
376 parameter_changed (X_("mixer-element-visibility"));
377 ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
378 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
379 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
381 //watch for mouse enter/exit so we can do some stuff
382 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
383 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
385 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
388 MixerStrip::~MixerStrip ()
390 CatchDeletion (this);
392 if (this ==_entered_mixer_strip)
393 _entered_mixer_strip = NULL;
397 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
399 _entered_mixer_strip = this;
401 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
402 //because the mixerstrip control is a parent that encompasses the strip
403 deselect_all_processors();
409 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
411 //if we have moved outside our strip, but not into a child view, then deselect ourselves
412 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
413 _entered_mixer_strip= 0;
415 //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
416 gpm.gain_display.set_sensitive(false);
418 gpm.gain_display.set_sensitive(true);
420 //if we leave this mixer strip we need to clear out any selections
421 //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
428 MixerStrip::set_route (boost::shared_ptr<Route> rt)
430 //the rec/monitor stuff only shows up for tracks.
431 //the show_sends only shows up for buses.
432 //remove them all here, and we may add them back later
433 if (show_sends_button->get_parent()) {
434 rec_mon_table.remove (*show_sends_button);
436 if (rec_enable_button->get_parent()) {
437 rec_mon_table.remove (*rec_enable_button);
439 if (monitor_input_button->get_parent()) {
440 rec_mon_table.remove (*monitor_input_button);
442 if (monitor_disk_button->get_parent()) {
443 rec_mon_table.remove (*monitor_disk_button);
445 if (group_button.get_parent()) {
446 bottom_button_table.remove (group_button);
449 RouteUI::set_route (rt);
451 /* ProcessorBox needs access to _route so that it can read
454 processor_box.set_route (rt);
456 revert_to_default_display ();
458 /* unpack these from the parent and stuff them into our own
462 if (gpm.peak_display.get_parent()) {
463 gpm.peak_display.get_parent()->remove (gpm.peak_display);
465 if (gpm.gain_display.get_parent()) {
466 gpm.gain_display.get_parent()->remove (gpm.gain_display);
469 gpm.set_type (rt->meter_type());
471 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
472 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
474 if (solo_button->get_parent()) {
475 mute_solo_table.remove (*solo_button);
478 if (mute_button->get_parent()) {
479 mute_solo_table.remove (*mute_button);
482 if (route()->is_master()) {
483 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
484 solo_button->hide ();
485 mute_button->show ();
486 rec_mon_table.hide ();
487 if (solo_iso_table.get_parent()) {
488 solo_iso_table.get_parent()->remove(solo_iso_table);
491 bottom_button_table.attach (group_button, 1, 2, 0, 1);
492 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
493 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
494 mute_button->show ();
495 solo_button->show ();
496 rec_mon_table.show ();
499 if (_mixer_owned && route()->is_master() ) {
501 HScrollbar scrollbar;
502 Gtk::Requisition requisition(scrollbar.size_request ());
503 int scrollbar_height = requisition.height;
505 spacer = manage (new EventBox);
506 spacer->set_size_request (-1, scrollbar_height+2);
507 global_vpacker.pack_start (*spacer, false, false);
512 monitor_input_button->show ();
513 monitor_disk_button->show ();
515 monitor_input_button->hide();
516 monitor_disk_button->hide ();
519 if (is_midi_track()) {
520 if (midi_input_enable_button == 0) {
521 midi_input_enable_button = manage (new ArdourButton);
522 midi_input_enable_button->set_name ("midi input button");
523 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
524 midi_input_enable_button->set_icon (ArdourButton::DinMidi);
525 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
526 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
527 ARDOUR_UI::instance()->set_tip (midi_input_enable_button, _("Enable/Disable MIDI input"));
529 input_button_box.remove (*midi_input_enable_button);
531 /* get current state */
532 midi_input_status_changed ();
533 input_button_box.pack_start (*midi_input_enable_button, false, false);
535 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
537 if (midi_input_enable_button) {
538 /* removal from the container will delete it */
539 input_button_box.remove (*midi_input_enable_button);
540 midi_input_enable_button = 0;
544 if (is_audio_track()) {
545 boost::shared_ptr<AudioTrack> at = audio_track();
546 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
551 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
552 rec_enable_button->set_sensitive (_session->writable());
553 rec_enable_button->show();
555 if (ARDOUR::Profile->get_mixbus()) {
556 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
557 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
558 } else if (ARDOUR::Profile->get_trx()) {
559 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
561 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
562 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
569 if (!_route->is_master()) {
570 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
571 show_sends_button->show();
575 meter_point_button.set_text (meter_point_string (_route->meter_point()));
577 delete route_ops_menu;
580 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
581 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
582 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
583 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
585 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
587 if (_route->panner_shell()) {
588 update_panner_choices();
589 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
592 if (is_audio_track()) {
593 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
596 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
597 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
599 set_stuff_from_route ();
601 /* now force an update of all the various elements */
603 update_mute_display ();
604 update_solo_display ();
607 route_group_changed ();
610 panners.setup_pan ();
612 if (has_audio_outputs ()) {
618 update_diskstream_display ();
619 update_input_display ();
620 update_output_display ();
622 add_events (Gdk::BUTTON_RELEASE_MASK);
624 processor_box.show ();
626 if (!route()->is_master() && !route()->is_monitor()) {
627 /* we don't allow master or control routes to be hidden */
632 gpm.reset_peak_display ();
633 gpm.gain_display.show ();
634 gpm.peak_display.show ();
637 width_hide_box.show();
639 global_vpacker.show();
640 mute_solo_table.show();
641 bottom_button_table.show();
643 meter_point_button.show();
644 input_button_box.show_all();
645 output_button.show();
647 _comment_button.show();
649 gpm.gain_automation_state_button.show();
651 parameter_changed ("mixer-element-visibility");
657 MixerStrip::set_stuff_from_route ()
659 /* if width is not set, it will be set by the MixerUI or editor */
661 string str = gui_property ("strip-width");
663 set_width_enum (Width (string_2_enum (str, _width)), this);
668 MixerStrip::set_width_enum (Width w, void* owner)
670 /* always set the gpm width again, things may be hidden */
673 panners.set_width (w);
675 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
677 _width_owner = owner;
681 if (_width_owner == this) {
682 set_gui_property ("strip-width", enum_2_string (_width));
687 const double scale = std::max(1.0, ARDOUR_UI::config()->get_font_scale() / 102400.);
692 if (show_sends_button) {
693 show_sends_button->set_text (_("Aux"));
696 gpm.gain_automation_style_button.set_text (
697 gpm.astyle_string(gain_automation->automation_style()));
698 gpm.gain_automation_state_button.set_text (
699 gpm.astate_string(gain_automation->automation_state()));
701 if (_route->panner()) {
702 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
703 panners.astyle_string(_route->panner()->automation_style()));
704 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
705 panners.astate_string(_route->panner()->automation_state()));
709 set_size_request (max (110 * scale, gpm.get_gm_width() + 5 * scale), -1);
714 if (show_sends_button) {
715 show_sends_button->set_text (_("Snd"));
718 gpm.gain_automation_style_button.set_text (
719 gpm.short_astyle_string(gain_automation->automation_style()));
720 gpm.gain_automation_state_button.set_text (
721 gpm.short_astate_string(gain_automation->automation_state()));
722 gain_meter().setup_meters (); // recalc meter width
724 if (_route->panner()) {
725 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
726 panners.short_astyle_string(_route->panner()->automation_style()));
727 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
728 panners.short_astate_string(_route->panner()->automation_state()));
731 set_size_request (max (60 * scale, gpm.get_gm_width() + 10 * scale), -1);
735 processor_box.set_width (w);
737 update_input_display ();
738 update_output_display ();
739 setup_comment_button ();
740 route_group_changed ();
746 MixerStrip::set_packed (bool yn)
751 set_gui_property ("visible", true);
753 set_gui_property ("visible", false);
758 struct RouteCompareByName {
759 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
760 return a->name().compare (b->name()) < 0;
765 MixerStrip::output_release (GdkEventButton *ev)
767 switch (ev->button) {
769 edit_output_configuration ();
777 MixerStrip::output_press (GdkEventButton *ev)
779 using namespace Menu_Helpers;
780 if (!_session->engine().connected()) {
781 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
786 MenuList& citems = output_menu.items();
787 switch (ev->button) {
790 return false; //wait for the mouse-up to pop the dialog
794 output_menu.set_name ("ArdourContextMenu");
796 output_menu_bundles.clear ();
798 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
800 citems.push_back (SeparatorElem());
801 uint32_t const n_with_separator = citems.size ();
803 ARDOUR::BundleList current = _route->output()->bundles_connected ();
805 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
807 /* give user bundles first chance at being in the menu */
809 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
810 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
811 maybe_add_bundle_to_output_menu (*i, current);
815 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
816 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
817 maybe_add_bundle_to_output_menu (*i, current);
821 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
822 RouteList copy = *routes;
823 copy.sort (RouteCompareByName ());
824 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
825 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
828 if (citems.size() == n_with_separator) {
829 /* no routes added; remove the separator */
833 citems.push_back (SeparatorElem());
835 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
838 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
839 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
844 citems.push_back (SeparatorElem());
845 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
847 output_menu.popup (1, ev->time);
858 MixerStrip::input_release (GdkEventButton *ev)
860 switch (ev->button) {
863 edit_input_configuration ();
875 MixerStrip::input_press (GdkEventButton *ev)
877 using namespace Menu_Helpers;
879 MenuList& citems = input_menu.items();
880 input_menu.set_name ("ArdourContextMenu");
883 if (!_session->engine().connected()) {
884 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
889 if (_session->actively_recording() && _route->record_enabled())
892 switch (ev->button) {
895 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
899 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
901 citems.push_back (SeparatorElem());
902 uint32_t const n_with_separator = citems.size ();
904 input_menu_bundles.clear ();
906 ARDOUR::BundleList current = _route->input()->bundles_connected ();
908 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
910 /* give user bundles first chance at being in the menu */
912 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
913 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
914 maybe_add_bundle_to_input_menu (*i, current);
918 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
919 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
920 maybe_add_bundle_to_input_menu (*i, current);
924 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
925 RouteList copy = *routes;
926 copy.sort (RouteCompareByName ());
927 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
928 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
931 if (citems.size() == n_with_separator) {
932 /* no routes added; remove the separator */
936 citems.push_back (SeparatorElem());
937 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
940 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
941 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
946 citems.push_back (SeparatorElem());
947 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
949 input_menu.popup (1, ev->time);
960 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
966 ARDOUR::BundleList current = _route->input()->bundles_connected ();
968 if (std::find (current.begin(), current.end(), c) == current.end()) {
969 _route->input()->connect_ports_to_bundle (c, true, this);
971 _route->input()->disconnect_ports_from_bundle (c, this);
976 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
982 ARDOUR::BundleList current = _route->output()->bundles_connected ();
984 if (std::find (current.begin(), current.end(), c) == current.end()) {
985 _route->output()->connect_ports_to_bundle (c, true, this);
987 _route->output()->disconnect_ports_from_bundle (c, this);
992 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
994 using namespace Menu_Helpers;
996 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1000 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1001 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1005 if (i != input_menu_bundles.end()) {
1009 input_menu_bundles.push_back (b);
1011 MenuList& citems = input_menu.items();
1013 std::string n = b->name ();
1014 replace_all (n, "_", " ");
1016 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1020 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1022 using namespace Menu_Helpers;
1024 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1028 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1029 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1033 if (i != output_menu_bundles.end()) {
1037 output_menu_bundles.push_back (b);
1039 MenuList& citems = output_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_output_chosen), b)));
1048 MixerStrip::update_diskstream_display ()
1050 if (is_track() && input_selector) {
1051 input_selector->hide_all ();
1054 route_color_changed ();
1058 MixerStrip::connect_to_pan ()
1060 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1062 panstate_connection.disconnect ();
1063 panstyle_connection.disconnect ();
1065 if (!_route->panner()) {
1069 boost::shared_ptr<Pannable> p = _route->pannable ();
1071 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1072 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1074 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1075 * However, that only works a panner was previously set.
1077 * PannerUI must remain subscribed to _panshell->Changed() in case
1078 * we switch the panner eg. AUX-Send and back
1079 * _route->panner_shell()->Changed() vs _panshell->Changed
1081 if (panners._panner == 0) {
1082 panners.panshell_changed ();
1084 update_panner_choices();
1088 MixerStrip::update_panner_choices ()
1090 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1091 if (!_route->panner_shell()) { return; }
1093 uint32_t in = _route->output()->n_ports().n_audio();
1095 if (_route->panner()) {
1096 in = _route->panner()->in().n_audio();
1099 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1103 * Output port labelling
1104 * =====================
1106 * Case 1: Each output has one connection, all connections are to system:playback_%i
1107 * out 1 -> system:playback_1
1108 * out 2 -> system:playback_2
1109 * out 3 -> system:playback_3
1112 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1113 * out 1 -> ardour:track_x/in 1
1114 * out 2 -> ardour:track_x/in 2
1115 * Display as: track_x
1117 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1118 * out 1 -> program x:foo
1119 * out 2 -> program x:foo
1120 * Display as: program x
1122 * Case 4: No connections (Disconnected)
1125 * Default case (unusual routing):
1126 * Display as: *number of connections*
1130 * .-----------------------------------------------.
1132 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1133 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1134 * '-----------------------------------------------'
1135 * .-----------------------------------------------.
1138 * '-----------------------------------------------'
1142 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1146 boost::shared_ptr<Port> port;
1147 vector<string> port_connections;
1149 uint32_t total_connection_count = 0;
1150 uint32_t io_connection_count = 0;
1151 uint32_t ardour_connection_count = 0;
1152 uint32_t system_connection_count = 0;
1153 uint32_t other_connection_count = 0;
1155 ostringstream label;
1157 bool have_label = false;
1158 bool each_io_has_one_connection = true;
1160 string connection_name;
1161 string ardour_track_name;
1162 string other_connection_type;
1163 string system_ports;
1166 ostringstream tooltip;
1167 char * tooltip_cstr;
1169 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1170 DataType dt = DataType::AUDIO;
1171 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 )
1172 dt = DataType::MIDI;
1175 io_count = route->n_inputs().n_total();
1176 tooltip << string_compose (_("<b>INPUT</b> to %1"), Glib::Markup::escape_text(route->name()));
1178 io_count = route->n_outputs().n_total();
1179 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(route->name()));
1183 for (io_index = 0; io_index < io_count; ++io_index) {
1185 port = route->input()->nth (io_index);
1187 port = route->output()->nth (io_index);
1190 //ignore any port connections that don't match our DataType
1191 if (port->type() != dt)
1194 port_connections.clear ();
1195 port->get_connections(port_connections);
1196 io_connection_count = 0;
1198 if (!port_connections.empty()) {
1199 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1201 string& connection_name (*i);
1203 if (connection_name.find("system:") == 0) {
1204 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1207 if (io_connection_count == 0) {
1208 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1210 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1213 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1216 if (connection_name.find("ardour:") == 0) {
1217 if (ardour_track_name.empty()) {
1218 // "ardour:Master/in 1" -> "ardour:Master/"
1219 string::size_type slash = connection_name.find("/");
1220 if (slash != string::npos) {
1221 ardour_track_name = connection_name.substr(0, slash + 1);
1225 if (connection_name.find(ardour_track_name) == 0) {
1226 ++ardour_connection_count;
1228 } else if (!pn.empty()) {
1229 if (system_ports.empty()) {
1232 system_ports += "/" + pn;
1234 if (connection_name.find("system:") == 0) {
1235 ++system_connection_count;
1237 } else if (connection_name.find("system:midi_") == 0) {
1239 // "system:midi_capture_123" -> "123"
1240 system_port = "M " + connection_name.substr(20);
1242 // "system:midi_playback_123" -> "123"
1243 system_port = "M " + connection_name.substr(21);
1246 if (system_ports.empty()) {
1247 system_ports += system_port;
1249 system_ports += "/" + system_port;
1252 ++system_connection_count;
1254 } else if (connection_name.find("system:") == 0) {
1256 // "system:capture_123" -> "123"
1257 system_port = connection_name.substr(15);
1259 // "system:playback_123" -> "123"
1260 system_port = connection_name.substr(16);
1263 if (system_ports.empty()) {
1264 system_ports += system_port;
1266 system_ports += "/" + system_port;
1269 ++system_connection_count;
1271 if (other_connection_type.empty()) {
1272 // "jamin:in 1" -> "jamin:"
1273 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1276 if (connection_name.find(other_connection_type) == 0) {
1277 ++other_connection_count;
1281 ++total_connection_count;
1282 ++io_connection_count;
1286 if (io_connection_count != 1) {
1287 each_io_has_one_connection = false;
1291 if (total_connection_count == 0) {
1292 tooltip << endl << _("Disconnected");
1295 tooltip_cstr = new char[tooltip.str().size() + 1];
1296 strcpy(tooltip_cstr, tooltip.str().c_str());
1299 ARDOUR_UI::instance()->set_tip (&input_button, tooltip_cstr, "");
1301 ARDOUR_UI::instance()->set_tip (&output_button, tooltip_cstr, "");
1304 if (each_io_has_one_connection) {
1305 if (total_connection_count == ardour_connection_count) {
1306 // all connections are to the same track in ardour
1307 // "ardour:Master/" -> "Master"
1308 string::size_type slash = ardour_track_name.find("/");
1309 if (slash != string::npos) {
1310 label << ardour_track_name.substr(7, slash - 7);
1314 else if (total_connection_count == system_connection_count) {
1315 // all connections are to system ports
1316 label << system_ports;
1319 else if (total_connection_count == other_connection_count) {
1320 // all connections are to the same external program eg jamin
1321 // "jamin:" -> "jamin"
1322 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1328 if (total_connection_count == 0) {
1332 // Odd configuration
1333 label << "*" << total_connection_count << "*";
1338 input_button.set_text (label.str());
1340 output_button.set_text (label.str());
1345 MixerStrip::update_input_display ()
1347 update_io_button (_route, _width, true);
1348 panners.setup_pan ();
1350 if (has_audio_outputs ()) {
1351 panners.show_all ();
1353 panners.hide_all ();
1359 MixerStrip::update_output_display ()
1361 update_io_button (_route, _width, false);
1362 gpm.setup_meters ();
1363 panners.setup_pan ();
1365 if (has_audio_outputs ()) {
1366 panners.show_all ();
1368 panners.hide_all ();
1373 MixerStrip::fast_update ()
1375 gpm.update_meters ();
1379 MixerStrip::diskstream_changed ()
1381 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1385 MixerStrip::io_changed_proxy ()
1387 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1391 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1393 boost::shared_ptr<Port> a = wa.lock ();
1394 boost::shared_ptr<Port> b = wb.lock ();
1396 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1397 update_input_display ();
1398 set_width_enum (_width, this);
1401 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1402 update_output_display ();
1403 set_width_enum (_width, this);
1408 MixerStrip::setup_comment_button ()
1413 if (_route->comment().empty ()) {
1414 _comment_button.unset_bg (STATE_NORMAL);
1415 _comment_button.set_text (_("Comments"));
1417 _comment_button.modify_bg (STATE_NORMAL, color ());
1418 _comment_button.set_text (_("*Comments*"));
1423 if (_route->comment().empty ()) {
1424 _comment_button.unset_bg (STATE_NORMAL);
1425 _comment_button.set_text (_("Cmt"));
1427 _comment_button.modify_bg (STATE_NORMAL, color ());
1428 _comment_button.set_text (_("*Cmt*"));
1433 ARDOUR_UI::instance()->set_tip (
1434 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1440 MixerStrip::select_route_group (GdkEventButton *ev)
1442 using namespace Menu_Helpers;
1444 if (ev->button == 1) {
1446 if (group_menu == 0) {
1448 PropertyList* plist = new PropertyList();
1450 plist->add (Properties::gain, true);
1451 plist->add (Properties::mute, true);
1452 plist->add (Properties::solo, true);
1454 group_menu = new RouteGroupMenu (_session, plist);
1458 r.push_back (route ());
1459 group_menu->build (r);
1460 group_menu->menu()->popup (1, ev->time);
1467 MixerStrip::route_group_changed ()
1469 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1471 RouteGroup *rg = _route->route_group();
1474 group_button.set_text (PBD::short_version (rg->name(), 5));
1478 group_button.set_text (_("Grp"));
1481 group_button.set_text (_("~G"));
1488 MixerStrip::route_color_changed ()
1490 name_button.modify_bg (STATE_NORMAL, color());
1491 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1492 reset_strip_style ();
1496 MixerStrip::show_passthru_color ()
1498 reset_strip_style ();
1502 MixerStrip::build_route_ops_menu ()
1504 using namespace Menu_Helpers;
1505 route_ops_menu = new Menu;
1506 route_ops_menu->set_name ("ArdourContextMenu");
1508 MenuList& items = route_ops_menu->items();
1510 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1512 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1514 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1516 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1518 items.push_back (SeparatorElem());
1520 if (!_route->is_master()) {
1521 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1523 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1524 rename_menu_item = &items.back();
1526 items.push_back (SeparatorElem());
1527 items.push_back (CheckMenuElem (_("Active")));
1528 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1529 i->set_active (_route->active());
1530 i->set_sensitive(! _session->transport_rolling());
1531 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1533 items.push_back (SeparatorElem());
1535 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1537 items.push_back (SeparatorElem());
1538 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1539 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1540 denormal_menu_item->set_active (_route->denormal_protection());
1542 if (!Profile->get_sae()) {
1543 items.push_back (SeparatorElem());
1544 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1547 items.push_back (SeparatorElem());
1550 /* note that this relies on selection being shared across editor and
1551 mixer (or global to the backend, in the future), which is the only
1552 sane thing for users anyway.
1555 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1557 Selection& selection (PublicEditor::instance().get_selection());
1558 if (!selection.selected (rtav)) {
1559 selection.set (rtav);
1562 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1568 MixerStrip::name_button_button_press (GdkEventButton* ev)
1570 if (ev->button == 3) {
1571 list_route_operations ();
1573 /* do not allow rename if the track is record-enabled */
1574 rename_menu_item->set_sensitive (!_route->record_enabled());
1575 route_ops_menu->popup (1, ev->time);
1584 MixerStrip::name_button_button_release (GdkEventButton* ev)
1586 if (ev->button == 1) {
1587 list_route_operations ();
1589 /* do not allow rename if the track is record-enabled */
1590 rename_menu_item->set_sensitive (!_route->record_enabled());
1591 route_ops_menu->popup (1, ev->time);
1598 MixerStrip::number_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::list_route_operations ()
1616 delete route_ops_menu;
1617 build_route_ops_menu ();
1621 MixerStrip::set_selected (bool yn)
1623 AxisView::set_selected (yn);
1625 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1626 global_frame.set_name ("MixerStripSelectedFrame");
1628 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1629 global_frame.set_name ("MixerStripFrame");
1631 global_frame.queue_draw ();
1634 // processor_box.deselect_all_processors();
1638 MixerStrip::property_changed (const PropertyChange& what_changed)
1640 RouteUI::property_changed (what_changed);
1642 if (what_changed.contains (ARDOUR::Properties::name)) {
1648 MixerStrip::name_changed ()
1652 name_button.set_text (_route->name());
1655 name_button.set_text (PBD::short_version (_route->name(), 5));
1659 ARDOUR_UI::instance()->set_tip (name_button, _route->name());
1661 if (_session->config.get_track_name_number()) {
1662 const int64_t track_number = _route->track_number ();
1663 if (track_number == 0) {
1664 number_label.set_text ("-");
1666 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1669 number_label.set_text ("");
1674 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1676 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1680 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1682 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1686 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1688 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1692 MixerStrip::width_button_pressed (GdkEventButton* ev)
1694 if (ev->button != 1) {
1698 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1701 _mixer.set_strip_width (Narrow, true);
1705 _mixer.set_strip_width (Wide, true);
1711 set_width_enum (Narrow, this);
1714 set_width_enum (Wide, this);
1723 MixerStrip::hide_clicked ()
1725 // LAME fix to reset the button status for when it is redisplayed (part 1)
1726 hide_button.set_sensitive(false);
1729 Hiding(); /* EMIT_SIGNAL */
1731 _mixer.hide_strip (this);
1735 hide_button.set_sensitive(true);
1739 MixerStrip::set_embedded (bool yn)
1745 MixerStrip::map_frozen ()
1747 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1749 boost::shared_ptr<AudioTrack> at = audio_track();
1752 switch (at->freeze_state()) {
1753 case AudioTrack::Frozen:
1754 processor_box.set_sensitive (false);
1755 hide_redirect_editors ();
1758 processor_box.set_sensitive (true);
1759 // XXX need some way, maybe, to retoggle redirect editors
1766 MixerStrip::hide_redirect_editors ()
1768 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1772 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1774 boost::shared_ptr<Processor> processor (p.lock ());
1779 Gtk::Window* w = processor_box.get_processor_ui (processor);
1787 MixerStrip::reset_strip_style ()
1789 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1791 gpm.set_fader_name ("SendStripBase");
1795 if (is_midi_track()) {
1796 if (_route->active()) {
1797 set_name ("MidiTrackStripBase");
1799 set_name ("MidiTrackStripBaseInactive");
1801 gpm.set_fader_name ("MidiTrackFader");
1802 } else if (is_audio_track()) {
1803 if (_route->active()) {
1804 set_name ("AudioTrackStripBase");
1806 set_name ("AudioTrackStripBaseInactive");
1808 gpm.set_fader_name ("AudioTrackFader");
1810 if (_route->active()) {
1811 set_name ("AudioBusStripBase");
1813 set_name ("AudioBusStripBaseInactive");
1815 gpm.set_fader_name ("AudioBusFader");
1817 /* (no MIDI busses yet) */
1824 MixerStrip::engine_stopped ()
1829 MixerStrip::engine_running ()
1834 MixerStrip::meter_point_string (MeterPoint mp)
1847 case MeterPostFader:
1864 return S_("Meter|In");
1868 return S_("Meter|Pr");
1871 case MeterPostFader:
1872 return S_("Meter|Po");
1876 return S_("Meter|O");
1881 return S_("Meter|C");
1890 /** Called when the metering point has changed */
1892 MixerStrip::meter_changed ()
1894 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1895 gpm.setup_meters ();
1896 // reset peak when meter point changes
1897 gpm.reset_peak_display();
1900 /** The bus that we are displaying sends to has changed, or been turned off.
1901 * @param send_to New bus that we are displaying sends to, or 0.
1904 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1906 RouteUI::bus_send_display_changed (send_to);
1909 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1914 revert_to_default_display ();
1917 revert_to_default_display ();
1922 MixerStrip::drop_send ()
1924 boost::shared_ptr<Send> current_send;
1926 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1927 current_send->set_metering (false);
1930 send_gone_connection.disconnect ();
1931 input_button.set_sensitive (true);
1932 output_button.set_sensitive (true);
1933 group_button.set_sensitive (true);
1934 set_invert_sensitive (true);
1935 meter_point_button.set_sensitive (true);
1936 mute_button->set_sensitive (true);
1937 solo_button->set_sensitive (true);
1938 rec_enable_button->set_sensitive (true);
1939 solo_isolated_led->set_sensitive (true);
1940 solo_safe_led->set_sensitive (true);
1941 monitor_input_button->set_sensitive (true);
1942 monitor_disk_button->set_sensitive (true);
1943 _comment_button.set_sensitive (true);
1947 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1949 _current_delivery = d;
1950 DeliveryChanged (_current_delivery);
1954 MixerStrip::show_send (boost::shared_ptr<Send> send)
1960 set_current_delivery (send);
1962 send->meter()->set_type(_route->shared_peak_meter()->get_type());
1963 send->set_metering (true);
1964 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
1966 gain_meter().set_controls (_route, send->meter(), send->amp());
1967 gain_meter().setup_meters ();
1969 uint32_t const in = _current_delivery->pans_required();
1970 uint32_t const out = _current_delivery->pan_outs();
1972 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
1973 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1974 panner_ui().setup_pan ();
1975 panner_ui().set_send_drawing_mode (true);
1976 panner_ui().show_all ();
1978 input_button.set_sensitive (false);
1979 group_button.set_sensitive (false);
1980 set_invert_sensitive (false);
1981 meter_point_button.set_sensitive (false);
1982 mute_button->set_sensitive (false);
1983 solo_button->set_sensitive (false);
1984 rec_enable_button->set_sensitive (false);
1985 solo_isolated_led->set_sensitive (false);
1986 solo_safe_led->set_sensitive (false);
1987 monitor_input_button->set_sensitive (false);
1988 monitor_disk_button->set_sensitive (false);
1989 _comment_button.set_sensitive (false);
1991 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
1992 output_button.set_sensitive (false);
1995 reset_strip_style ();
1999 MixerStrip::revert_to_default_display ()
2003 set_current_delivery (_route->main_outs ());
2005 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2006 gain_meter().setup_meters ();
2008 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2009 update_panner_choices();
2010 panner_ui().setup_pan ();
2011 panner_ui().set_send_drawing_mode (false);
2013 if (has_audio_outputs ()) {
2014 panners.show_all ();
2016 panners.hide_all ();
2019 reset_strip_style ();
2023 MixerStrip::set_button_names ()
2027 mute_button->set_text (_("Mute"));
2028 monitor_input_button->set_text (_("In"));
2029 monitor_disk_button->set_text (_("Disk"));
2031 if (_route && _route->solo_safe()) {
2032 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2034 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2036 if (!Config->get_solo_control_is_listen_control()) {
2037 solo_button->set_text (_("Solo"));
2039 switch (Config->get_listen_position()) {
2040 case AfterFaderListen:
2041 solo_button->set_text (_("AFL"));
2043 case PreFaderListen:
2044 solo_button->set_text (_("PFL"));
2048 solo_isolated_led->set_text (_("Iso"));
2049 solo_safe_led->set_text (S_("SoloLock|Lock"));
2053 mute_button->set_text (S_("Mute|M"));
2054 monitor_input_button->set_text (S_("MonitorInput|I"));
2055 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2057 if (_route && _route->solo_safe()) {
2058 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2060 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2062 if (!Config->get_solo_control_is_listen_control()) {
2063 solo_button->set_text (S_("Solo|S"));
2065 switch (Config->get_listen_position()) {
2066 case AfterFaderListen:
2067 solo_button->set_text (S_("AfterFader|A"));
2069 case PreFaderListen:
2070 solo_button->set_text (S_("Prefader|P"));
2075 solo_isolated_led->set_text (S_("SoloIso|I"));
2076 solo_safe_led->set_text (S_("SoloLock|L"));
2081 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2083 meter_point_button.set_text ("");
2088 MixerStrip::plugin_selector()
2090 return _mixer.plugin_selector();
2094 MixerStrip::hide_things ()
2096 processor_box.hide_things ();
2100 MixerStrip::input_active_button_press (GdkEventButton*)
2102 /* nothing happens on press */
2107 MixerStrip::input_active_button_release (GdkEventButton* ev)
2109 boost::shared_ptr<MidiTrack> mt = midi_track ();
2115 boost::shared_ptr<RouteList> rl (new RouteList);
2117 rl->push_back (route());
2119 _session->set_exclusive_input_active (rl, !mt->input_active(),
2120 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2126 MixerStrip::midi_input_status_changed ()
2128 if (midi_input_enable_button) {
2129 boost::shared_ptr<MidiTrack> mt = midi_track ();
2131 midi_input_enable_button->set_active (mt->input_active ());
2136 MixerStrip::state_id () const
2138 return string_compose ("strip %1", _route->id().to_s());
2142 MixerStrip::parameter_changed (string p)
2144 if (p == _visibility.get_state_name()) {
2145 /* The user has made changes to the mixer strip visibility, so get
2146 our VisibilityGroup to reflect these changes in our widgets.
2148 _visibility.set_state (ARDOUR_UI::config()->get_mixer_strip_visibility ());
2150 else if (p == "track-name-number") {
2155 /** Called to decide whether the solo isolate / solo lock button visibility should
2156 * be overridden from that configured by the user. We do this for the master bus.
2158 * @return optional value that is present if visibility state should be overridden.
2160 boost::optional<bool>
2161 MixerStrip::override_solo_visibility () const
2163 if (_route && _route->is_master ()) {
2164 return boost::optional<bool> (false);
2167 return boost::optional<bool> ();
2171 MixerStrip::add_input_port (DataType t)
2173 _route->input()->add_port ("", this, t);
2177 MixerStrip::add_output_port (DataType t)
2179 _route->output()->add_port ("", this, t);
2183 MixerStrip::route_active_changed ()
2185 reset_strip_style ();
2189 MixerStrip::copy_processors ()
2191 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2195 MixerStrip::cut_processors ()
2197 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2201 MixerStrip::paste_processors ()
2203 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2207 MixerStrip::select_all_processors ()
2209 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2213 MixerStrip::deselect_all_processors ()
2215 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2219 MixerStrip::delete_processors ()
2221 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2225 MixerStrip::toggle_processors ()
2227 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2231 MixerStrip::ab_plugins ()
2233 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2237 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2239 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2242 if (ev->button == 3) {
2243 popup_level_meter_menu (ev);
2251 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2253 using namespace Gtk::Menu_Helpers;
2255 Gtk::Menu* m = manage (new Menu);
2256 MenuList& items = m->items ();
2258 RadioMenuItem::Group group;
2260 _suspend_menu_callbacks = true;
2261 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2262 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2263 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2264 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2265 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2267 if (gpm.meter_channels().n_audio() == 0) {
2268 m->popup (ev->button, ev->time);
2269 _suspend_menu_callbacks = false;
2273 RadioMenuItem::Group tgroup;
2274 items.push_back (SeparatorElem());
2276 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2277 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2278 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2279 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2280 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2281 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2282 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2283 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2284 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2285 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2288 if (_route->is_master()) {
2291 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2292 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2293 /* non-master bus */
2296 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2303 MeterType cmt = _route->meter_type();
2304 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2306 items.push_back (SeparatorElem());
2307 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2308 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2309 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2310 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2311 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2312 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2314 m->popup (ev->button, ev->time);
2315 _suspend_menu_callbacks = false;
2319 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2320 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2322 using namespace Menu_Helpers;
2324 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2325 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2326 i->set_active (_route->meter_point() == point);
2330 MixerStrip::set_meter_point (MeterPoint p)
2332 if (_suspend_menu_callbacks) return;
2333 _route->set_meter_point (p);
2337 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2338 RadioMenuItem::Group& group, string const & name, MeterType type)
2340 using namespace Menu_Helpers;
2342 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2343 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2344 i->set_active (_route->meter_type() == type);
2348 MixerStrip::set_meter_type (MeterType t)
2350 if (_suspend_menu_callbacks) return;