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 // panners expect an even number of horiz. pixels
710 int width = rint(max (110 * scale, gpm.get_gm_width() + 10 * scale)) + 1;
712 set_size_request (width, -1);
718 if (show_sends_button) {
719 show_sends_button->set_text (_("Snd"));
722 gpm.gain_automation_style_button.set_text (
723 gpm.short_astyle_string(gain_automation->automation_style()));
724 gpm.gain_automation_state_button.set_text (
725 gpm.short_astate_string(gain_automation->automation_state()));
726 gain_meter().setup_meters (); // recalc meter width
728 if (_route->panner()) {
729 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
730 panners.short_astyle_string(_route->panner()->automation_style()));
731 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
732 panners.short_astate_string(_route->panner()->automation_state()));
736 // panners expect an even number of horiz. pixels
737 int width = rint(max (60 * scale, gpm.get_gm_width() + 10 * scale)) + 1;
739 set_size_request (width, -1);
744 processor_box.set_width (w);
746 update_input_display ();
747 update_output_display ();
748 setup_comment_button ();
749 route_group_changed ();
755 MixerStrip::set_packed (bool yn)
760 set_gui_property ("visible", true);
762 set_gui_property ("visible", false);
767 struct RouteCompareByName {
768 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
769 return a->name().compare (b->name()) < 0;
774 MixerStrip::output_release (GdkEventButton *ev)
776 switch (ev->button) {
778 edit_output_configuration ();
786 MixerStrip::output_press (GdkEventButton *ev)
788 using namespace Menu_Helpers;
789 if (!_session->engine().connected()) {
790 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
795 MenuList& citems = output_menu.items();
796 switch (ev->button) {
799 return false; //wait for the mouse-up to pop the dialog
803 output_menu.set_name ("ArdourContextMenu");
805 output_menu_bundles.clear ();
807 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
809 citems.push_back (SeparatorElem());
810 uint32_t const n_with_separator = citems.size ();
812 ARDOUR::BundleList current = _route->output()->bundles_connected ();
814 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
816 /* give user bundles first chance at being in the menu */
818 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
819 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
820 maybe_add_bundle_to_output_menu (*i, current);
824 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
825 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
826 maybe_add_bundle_to_output_menu (*i, current);
830 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
831 RouteList copy = *routes;
832 copy.sort (RouteCompareByName ());
833 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
834 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
837 if (citems.size() == n_with_separator) {
838 /* no routes added; remove the separator */
842 citems.push_back (SeparatorElem());
844 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
847 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
848 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
853 citems.push_back (SeparatorElem());
854 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
856 output_menu.popup (1, ev->time);
867 MixerStrip::input_release (GdkEventButton *ev)
869 switch (ev->button) {
872 edit_input_configuration ();
884 MixerStrip::input_press (GdkEventButton *ev)
886 using namespace Menu_Helpers;
888 MenuList& citems = input_menu.items();
889 input_menu.set_name ("ArdourContextMenu");
892 if (!_session->engine().connected()) {
893 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
898 if (_session->actively_recording() && _route->record_enabled())
901 switch (ev->button) {
904 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
908 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
910 citems.push_back (SeparatorElem());
911 uint32_t const n_with_separator = citems.size ();
913 input_menu_bundles.clear ();
915 ARDOUR::BundleList current = _route->input()->bundles_connected ();
917 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
919 /* give user bundles first chance at being in the menu */
921 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
922 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
923 maybe_add_bundle_to_input_menu (*i, current);
927 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
928 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
929 maybe_add_bundle_to_input_menu (*i, current);
933 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
934 RouteList copy = *routes;
935 copy.sort (RouteCompareByName ());
936 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
937 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
940 if (citems.size() == n_with_separator) {
941 /* no routes added; remove the separator */
945 citems.push_back (SeparatorElem());
946 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
949 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
950 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
955 citems.push_back (SeparatorElem());
956 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
958 input_menu.popup (1, ev->time);
969 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
975 ARDOUR::BundleList current = _route->input()->bundles_connected ();
977 if (std::find (current.begin(), current.end(), c) == current.end()) {
978 _route->input()->connect_ports_to_bundle (c, true, this);
980 _route->input()->disconnect_ports_from_bundle (c, this);
985 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
991 ARDOUR::BundleList current = _route->output()->bundles_connected ();
993 if (std::find (current.begin(), current.end(), c) == current.end()) {
994 _route->output()->connect_ports_to_bundle (c, true, this);
996 _route->output()->disconnect_ports_from_bundle (c, this);
1001 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1003 using namespace Menu_Helpers;
1005 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1009 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1010 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1014 if (i != input_menu_bundles.end()) {
1018 input_menu_bundles.push_back (b);
1020 MenuList& citems = input_menu.items();
1022 std::string n = b->name ();
1023 replace_all (n, "_", " ");
1025 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1029 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1031 using namespace Menu_Helpers;
1033 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1037 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1038 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1042 if (i != output_menu_bundles.end()) {
1046 output_menu_bundles.push_back (b);
1048 MenuList& citems = output_menu.items();
1050 std::string n = b->name ();
1051 replace_all (n, "_", " ");
1053 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1057 MixerStrip::update_diskstream_display ()
1059 if (is_track() && input_selector) {
1060 input_selector->hide_all ();
1063 route_color_changed ();
1067 MixerStrip::connect_to_pan ()
1069 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1071 panstate_connection.disconnect ();
1072 panstyle_connection.disconnect ();
1074 if (!_route->panner()) {
1078 boost::shared_ptr<Pannable> p = _route->pannable ();
1080 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1081 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1083 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1084 * However, that only works a panner was previously set.
1086 * PannerUI must remain subscribed to _panshell->Changed() in case
1087 * we switch the panner eg. AUX-Send and back
1088 * _route->panner_shell()->Changed() vs _panshell->Changed
1090 if (panners._panner == 0) {
1091 panners.panshell_changed ();
1093 update_panner_choices();
1097 MixerStrip::update_panner_choices ()
1099 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1100 if (!_route->panner_shell()) { return; }
1102 uint32_t in = _route->output()->n_ports().n_audio();
1104 if (_route->panner()) {
1105 in = _route->panner()->in().n_audio();
1108 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1112 * Output port labelling
1113 * =====================
1115 * Case 1: Each output has one connection, all connections are to system:playback_%i
1116 * out 1 -> system:playback_1
1117 * out 2 -> system:playback_2
1118 * out 3 -> system:playback_3
1121 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1122 * out 1 -> ardour:track_x/in 1
1123 * out 2 -> ardour:track_x/in 2
1124 * Display as: track_x
1126 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1127 * out 1 -> program x:foo
1128 * out 2 -> program x:foo
1129 * Display as: program x
1131 * Case 4: No connections (Disconnected)
1134 * Default case (unusual routing):
1135 * Display as: *number of connections*
1139 * .-----------------------------------------------.
1141 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1142 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1143 * '-----------------------------------------------'
1144 * .-----------------------------------------------.
1147 * '-----------------------------------------------'
1151 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1155 boost::shared_ptr<Port> port;
1156 vector<string> port_connections;
1158 uint32_t total_connection_count = 0;
1159 uint32_t io_connection_count = 0;
1160 uint32_t ardour_connection_count = 0;
1161 uint32_t system_connection_count = 0;
1162 uint32_t other_connection_count = 0;
1164 ostringstream label;
1166 bool have_label = false;
1167 bool each_io_has_one_connection = true;
1169 string connection_name;
1170 string ardour_track_name;
1171 string other_connection_type;
1172 string system_ports;
1175 ostringstream tooltip;
1176 char * tooltip_cstr;
1178 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1179 DataType dt = DataType::AUDIO;
1180 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 )
1181 dt = DataType::MIDI;
1184 io_count = route->n_inputs().n_total();
1185 tooltip << string_compose (_("<b>INPUT</b> to %1"), Glib::Markup::escape_text(route->name()));
1187 io_count = route->n_outputs().n_total();
1188 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(route->name()));
1192 for (io_index = 0; io_index < io_count; ++io_index) {
1194 port = route->input()->nth (io_index);
1196 port = route->output()->nth (io_index);
1199 //ignore any port connections that don't match our DataType
1200 if (port->type() != dt)
1203 port_connections.clear ();
1204 port->get_connections(port_connections);
1205 io_connection_count = 0;
1207 if (!port_connections.empty()) {
1208 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1210 string& connection_name (*i);
1212 if (connection_name.find("system:") == 0) {
1213 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1216 if (io_connection_count == 0) {
1217 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1219 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1222 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1225 if (connection_name.find("ardour:") == 0) {
1226 if (ardour_track_name.empty()) {
1227 // "ardour:Master/in 1" -> "ardour:Master/"
1228 string::size_type slash = connection_name.find("/");
1229 if (slash != string::npos) {
1230 ardour_track_name = connection_name.substr(0, slash + 1);
1234 if (connection_name.find(ardour_track_name) == 0) {
1235 ++ardour_connection_count;
1237 } else if (!pn.empty()) {
1238 if (system_ports.empty()) {
1241 system_ports += "/" + pn;
1243 if (connection_name.find("system:") == 0) {
1244 ++system_connection_count;
1246 } else if (connection_name.find("system:midi_") == 0) {
1248 // "system:midi_capture_123" -> "123"
1249 system_port = "M " + connection_name.substr(20);
1251 // "system:midi_playback_123" -> "123"
1252 system_port = "M " + connection_name.substr(21);
1255 if (system_ports.empty()) {
1256 system_ports += system_port;
1258 system_ports += "/" + system_port;
1261 ++system_connection_count;
1263 } else if (connection_name.find("system:") == 0) {
1265 // "system:capture_123" -> "123"
1266 system_port = connection_name.substr(15);
1268 // "system:playback_123" -> "123"
1269 system_port = connection_name.substr(16);
1272 if (system_ports.empty()) {
1273 system_ports += system_port;
1275 system_ports += "/" + system_port;
1278 ++system_connection_count;
1280 if (other_connection_type.empty()) {
1281 // "jamin:in 1" -> "jamin:"
1282 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1285 if (connection_name.find(other_connection_type) == 0) {
1286 ++other_connection_count;
1290 ++total_connection_count;
1291 ++io_connection_count;
1295 if (io_connection_count != 1) {
1296 each_io_has_one_connection = false;
1300 if (total_connection_count == 0) {
1301 tooltip << endl << _("Disconnected");
1304 tooltip_cstr = new char[tooltip.str().size() + 1];
1305 strcpy(tooltip_cstr, tooltip.str().c_str());
1308 ARDOUR_UI::instance()->set_tip (&input_button, tooltip_cstr, "");
1310 ARDOUR_UI::instance()->set_tip (&output_button, tooltip_cstr, "");
1313 if (each_io_has_one_connection) {
1314 if (total_connection_count == ardour_connection_count) {
1315 // all connections are to the same track in ardour
1316 // "ardour:Master/" -> "Master"
1317 string::size_type slash = ardour_track_name.find("/");
1318 if (slash != string::npos) {
1319 label << ardour_track_name.substr(7, slash - 7);
1323 else if (total_connection_count == system_connection_count) {
1324 // all connections are to system ports
1325 label << system_ports;
1328 else if (total_connection_count == other_connection_count) {
1329 // all connections are to the same external program eg jamin
1330 // "jamin:" -> "jamin"
1331 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1337 if (total_connection_count == 0) {
1341 // Odd configuration
1342 label << "*" << total_connection_count << "*";
1347 input_button.set_text (label.str());
1349 output_button.set_text (label.str());
1354 MixerStrip::update_input_display ()
1356 update_io_button (_route, _width, true);
1357 panners.setup_pan ();
1359 if (has_audio_outputs ()) {
1360 panners.show_all ();
1362 panners.hide_all ();
1368 MixerStrip::update_output_display ()
1370 update_io_button (_route, _width, false);
1371 gpm.setup_meters ();
1372 panners.setup_pan ();
1374 if (has_audio_outputs ()) {
1375 panners.show_all ();
1377 panners.hide_all ();
1382 MixerStrip::fast_update ()
1384 gpm.update_meters ();
1388 MixerStrip::diskstream_changed ()
1390 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1394 MixerStrip::io_changed_proxy ()
1396 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1400 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1402 boost::shared_ptr<Port> a = wa.lock ();
1403 boost::shared_ptr<Port> b = wb.lock ();
1405 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1406 update_input_display ();
1407 set_width_enum (_width, this);
1410 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1411 update_output_display ();
1412 set_width_enum (_width, this);
1417 MixerStrip::setup_comment_button ()
1422 if (_route->comment().empty ()) {
1423 _comment_button.unset_bg (STATE_NORMAL);
1424 _comment_button.set_text (_("Comments"));
1426 _comment_button.modify_bg (STATE_NORMAL, color ());
1427 _comment_button.set_text (_("*Comments*"));
1432 if (_route->comment().empty ()) {
1433 _comment_button.unset_bg (STATE_NORMAL);
1434 _comment_button.set_text (_("Cmt"));
1436 _comment_button.modify_bg (STATE_NORMAL, color ());
1437 _comment_button.set_text (_("*Cmt*"));
1442 ARDOUR_UI::instance()->set_tip (
1443 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1449 MixerStrip::select_route_group (GdkEventButton *ev)
1451 using namespace Menu_Helpers;
1453 if (ev->button == 1) {
1455 if (group_menu == 0) {
1457 PropertyList* plist = new PropertyList();
1459 plist->add (Properties::gain, true);
1460 plist->add (Properties::mute, true);
1461 plist->add (Properties::solo, true);
1463 group_menu = new RouteGroupMenu (_session, plist);
1467 r.push_back (route ());
1468 group_menu->build (r);
1469 group_menu->menu()->popup (1, ev->time);
1476 MixerStrip::route_group_changed ()
1478 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1480 RouteGroup *rg = _route->route_group();
1483 group_button.set_text (PBD::short_version (rg->name(), 5));
1487 group_button.set_text (_("Grp"));
1490 group_button.set_text (_("~G"));
1497 MixerStrip::route_color_changed ()
1499 name_button.modify_bg (STATE_NORMAL, color());
1500 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1501 reset_strip_style ();
1505 MixerStrip::show_passthru_color ()
1507 reset_strip_style ();
1511 MixerStrip::build_route_ops_menu ()
1513 using namespace Menu_Helpers;
1514 route_ops_menu = new Menu;
1515 route_ops_menu->set_name ("ArdourContextMenu");
1517 MenuList& items = route_ops_menu->items();
1519 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1521 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1523 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1525 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1527 items.push_back (SeparatorElem());
1529 if (!_route->is_master()) {
1530 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1532 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1533 rename_menu_item = &items.back();
1535 items.push_back (SeparatorElem());
1536 items.push_back (CheckMenuElem (_("Active")));
1537 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1538 i->set_active (_route->active());
1539 i->set_sensitive(! _session->transport_rolling());
1540 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1542 items.push_back (SeparatorElem());
1544 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1546 items.push_back (SeparatorElem());
1547 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1548 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1549 denormal_menu_item->set_active (_route->denormal_protection());
1551 if (!Profile->get_sae()) {
1552 items.push_back (SeparatorElem());
1553 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1556 items.push_back (SeparatorElem());
1559 /* note that this relies on selection being shared across editor and
1560 mixer (or global to the backend, in the future), which is the only
1561 sane thing for users anyway.
1564 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1566 Selection& selection (PublicEditor::instance().get_selection());
1567 if (!selection.selected (rtav)) {
1568 selection.set (rtav);
1571 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1577 MixerStrip::name_button_button_press (GdkEventButton* ev)
1579 if (ev->button == 3) {
1580 list_route_operations ();
1582 /* do not allow rename if the track is record-enabled */
1583 rename_menu_item->set_sensitive (!_route->record_enabled());
1584 route_ops_menu->popup (1, ev->time);
1593 MixerStrip::name_button_button_release (GdkEventButton* ev)
1595 if (ev->button == 1) {
1596 list_route_operations ();
1598 /* do not allow rename if the track is record-enabled */
1599 rename_menu_item->set_sensitive (!_route->record_enabled());
1600 route_ops_menu->popup (1, ev->time);
1607 MixerStrip::number_button_button_press (GdkEventButton* ev)
1609 if ( ev->button == 3 ) {
1610 list_route_operations ();
1612 /* do not allow rename if the track is record-enabled */
1613 rename_menu_item->set_sensitive (!_route->record_enabled());
1614 route_ops_menu->popup (1, ev->time);
1623 MixerStrip::list_route_operations ()
1625 delete route_ops_menu;
1626 build_route_ops_menu ();
1630 MixerStrip::set_selected (bool yn)
1632 AxisView::set_selected (yn);
1634 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1635 global_frame.set_name ("MixerStripSelectedFrame");
1637 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1638 global_frame.set_name ("MixerStripFrame");
1640 global_frame.queue_draw ();
1643 // processor_box.deselect_all_processors();
1647 MixerStrip::property_changed (const PropertyChange& what_changed)
1649 RouteUI::property_changed (what_changed);
1651 if (what_changed.contains (ARDOUR::Properties::name)) {
1657 MixerStrip::name_changed ()
1661 name_button.set_text (_route->name());
1664 name_button.set_text (PBD::short_version (_route->name(), 5));
1668 ARDOUR_UI::instance()->set_tip (name_button, _route->name());
1670 if (_session->config.get_track_name_number()) {
1671 const int64_t track_number = _route->track_number ();
1672 if (track_number == 0) {
1673 number_label.set_text ("-");
1675 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1678 number_label.set_text ("");
1683 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1685 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1689 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1691 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1695 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1697 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1701 MixerStrip::width_button_pressed (GdkEventButton* ev)
1703 if (ev->button != 1) {
1707 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1710 _mixer.set_strip_width (Narrow, true);
1714 _mixer.set_strip_width (Wide, true);
1720 set_width_enum (Narrow, this);
1723 set_width_enum (Wide, this);
1732 MixerStrip::hide_clicked ()
1734 // LAME fix to reset the button status for when it is redisplayed (part 1)
1735 hide_button.set_sensitive(false);
1738 Hiding(); /* EMIT_SIGNAL */
1740 _mixer.hide_strip (this);
1744 hide_button.set_sensitive(true);
1748 MixerStrip::set_embedded (bool yn)
1754 MixerStrip::map_frozen ()
1756 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1758 boost::shared_ptr<AudioTrack> at = audio_track();
1761 switch (at->freeze_state()) {
1762 case AudioTrack::Frozen:
1763 processor_box.set_sensitive (false);
1764 hide_redirect_editors ();
1767 processor_box.set_sensitive (true);
1768 // XXX need some way, maybe, to retoggle redirect editors
1775 MixerStrip::hide_redirect_editors ()
1777 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1781 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1783 boost::shared_ptr<Processor> processor (p.lock ());
1788 Gtk::Window* w = processor_box.get_processor_ui (processor);
1796 MixerStrip::reset_strip_style ()
1798 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1800 gpm.set_fader_name ("SendStripBase");
1804 if (is_midi_track()) {
1805 if (_route->active()) {
1806 set_name ("MidiTrackStripBase");
1808 set_name ("MidiTrackStripBaseInactive");
1810 gpm.set_fader_name ("MidiTrackFader");
1811 } else if (is_audio_track()) {
1812 if (_route->active()) {
1813 set_name ("AudioTrackStripBase");
1815 set_name ("AudioTrackStripBaseInactive");
1817 gpm.set_fader_name ("AudioTrackFader");
1819 if (_route->active()) {
1820 set_name ("AudioBusStripBase");
1822 set_name ("AudioBusStripBaseInactive");
1824 gpm.set_fader_name ("AudioBusFader");
1826 /* (no MIDI busses yet) */
1833 MixerStrip::engine_stopped ()
1838 MixerStrip::engine_running ()
1843 MixerStrip::meter_point_string (MeterPoint mp)
1856 case MeterPostFader:
1873 return S_("Meter|In");
1877 return S_("Meter|Pr");
1880 case MeterPostFader:
1881 return S_("Meter|Po");
1885 return S_("Meter|O");
1890 return S_("Meter|C");
1899 /** Called when the metering point has changed */
1901 MixerStrip::meter_changed ()
1903 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1904 gpm.setup_meters ();
1905 // reset peak when meter point changes
1906 gpm.reset_peak_display();
1909 /** The bus that we are displaying sends to has changed, or been turned off.
1910 * @param send_to New bus that we are displaying sends to, or 0.
1913 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1915 RouteUI::bus_send_display_changed (send_to);
1918 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1923 revert_to_default_display ();
1926 revert_to_default_display ();
1931 MixerStrip::drop_send ()
1933 boost::shared_ptr<Send> current_send;
1935 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1936 current_send->set_metering (false);
1939 send_gone_connection.disconnect ();
1940 input_button.set_sensitive (true);
1941 output_button.set_sensitive (true);
1942 group_button.set_sensitive (true);
1943 set_invert_sensitive (true);
1944 meter_point_button.set_sensitive (true);
1945 mute_button->set_sensitive (true);
1946 solo_button->set_sensitive (true);
1947 rec_enable_button->set_sensitive (true);
1948 solo_isolated_led->set_sensitive (true);
1949 solo_safe_led->set_sensitive (true);
1950 monitor_input_button->set_sensitive (true);
1951 monitor_disk_button->set_sensitive (true);
1952 _comment_button.set_sensitive (true);
1956 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1958 _current_delivery = d;
1959 DeliveryChanged (_current_delivery);
1963 MixerStrip::show_send (boost::shared_ptr<Send> send)
1969 set_current_delivery (send);
1971 send->meter()->set_type(_route->shared_peak_meter()->get_type());
1972 send->set_metering (true);
1973 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
1975 gain_meter().set_controls (_route, send->meter(), send->amp());
1976 gain_meter().setup_meters ();
1978 uint32_t const in = _current_delivery->pans_required();
1979 uint32_t const out = _current_delivery->pan_outs();
1981 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
1982 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1983 panner_ui().setup_pan ();
1984 panner_ui().set_send_drawing_mode (true);
1985 panner_ui().show_all ();
1987 input_button.set_sensitive (false);
1988 group_button.set_sensitive (false);
1989 set_invert_sensitive (false);
1990 meter_point_button.set_sensitive (false);
1991 mute_button->set_sensitive (false);
1992 solo_button->set_sensitive (false);
1993 rec_enable_button->set_sensitive (false);
1994 solo_isolated_led->set_sensitive (false);
1995 solo_safe_led->set_sensitive (false);
1996 monitor_input_button->set_sensitive (false);
1997 monitor_disk_button->set_sensitive (false);
1998 _comment_button.set_sensitive (false);
2000 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2001 output_button.set_sensitive (false);
2004 reset_strip_style ();
2008 MixerStrip::revert_to_default_display ()
2012 set_current_delivery (_route->main_outs ());
2014 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2015 gain_meter().setup_meters ();
2017 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2018 update_panner_choices();
2019 panner_ui().setup_pan ();
2020 panner_ui().set_send_drawing_mode (false);
2022 if (has_audio_outputs ()) {
2023 panners.show_all ();
2025 panners.hide_all ();
2028 reset_strip_style ();
2032 MixerStrip::set_button_names ()
2036 mute_button->set_text (_("Mute"));
2037 monitor_input_button->set_text (_("In"));
2038 monitor_disk_button->set_text (_("Disk"));
2040 if (_route && _route->solo_safe()) {
2041 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2043 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2045 if (!Config->get_solo_control_is_listen_control()) {
2046 solo_button->set_text (_("Solo"));
2048 switch (Config->get_listen_position()) {
2049 case AfterFaderListen:
2050 solo_button->set_text (_("AFL"));
2052 case PreFaderListen:
2053 solo_button->set_text (_("PFL"));
2057 solo_isolated_led->set_text (_("Iso"));
2058 solo_safe_led->set_text (S_("SoloLock|Lock"));
2062 mute_button->set_text (S_("Mute|M"));
2063 monitor_input_button->set_text (S_("MonitorInput|I"));
2064 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2066 if (_route && _route->solo_safe()) {
2067 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2069 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2071 if (!Config->get_solo_control_is_listen_control()) {
2072 solo_button->set_text (S_("Solo|S"));
2074 switch (Config->get_listen_position()) {
2075 case AfterFaderListen:
2076 solo_button->set_text (S_("AfterFader|A"));
2078 case PreFaderListen:
2079 solo_button->set_text (S_("Prefader|P"));
2084 solo_isolated_led->set_text (S_("SoloIso|I"));
2085 solo_safe_led->set_text (S_("SoloLock|L"));
2090 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2092 meter_point_button.set_text ("");
2097 MixerStrip::plugin_selector()
2099 return _mixer.plugin_selector();
2103 MixerStrip::hide_things ()
2105 processor_box.hide_things ();
2109 MixerStrip::input_active_button_press (GdkEventButton*)
2111 /* nothing happens on press */
2116 MixerStrip::input_active_button_release (GdkEventButton* ev)
2118 boost::shared_ptr<MidiTrack> mt = midi_track ();
2124 boost::shared_ptr<RouteList> rl (new RouteList);
2126 rl->push_back (route());
2128 _session->set_exclusive_input_active (rl, !mt->input_active(),
2129 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2135 MixerStrip::midi_input_status_changed ()
2137 if (midi_input_enable_button) {
2138 boost::shared_ptr<MidiTrack> mt = midi_track ();
2140 midi_input_enable_button->set_active (mt->input_active ());
2145 MixerStrip::state_id () const
2147 return string_compose ("strip %1", _route->id().to_s());
2151 MixerStrip::parameter_changed (string p)
2153 if (p == _visibility.get_state_name()) {
2154 /* The user has made changes to the mixer strip visibility, so get
2155 our VisibilityGroup to reflect these changes in our widgets.
2157 _visibility.set_state (ARDOUR_UI::config()->get_mixer_strip_visibility ());
2159 else if (p == "track-name-number") {
2164 /** Called to decide whether the solo isolate / solo lock button visibility should
2165 * be overridden from that configured by the user. We do this for the master bus.
2167 * @return optional value that is present if visibility state should be overridden.
2169 boost::optional<bool>
2170 MixerStrip::override_solo_visibility () const
2172 if (_route && _route->is_master ()) {
2173 return boost::optional<bool> (false);
2176 return boost::optional<bool> ();
2180 MixerStrip::add_input_port (DataType t)
2182 _route->input()->add_port ("", this, t);
2186 MixerStrip::add_output_port (DataType t)
2188 _route->output()->add_port ("", this, t);
2192 MixerStrip::route_active_changed ()
2194 reset_strip_style ();
2198 MixerStrip::copy_processors ()
2200 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2204 MixerStrip::cut_processors ()
2206 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2210 MixerStrip::paste_processors ()
2212 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2216 MixerStrip::select_all_processors ()
2218 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2222 MixerStrip::deselect_all_processors ()
2224 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2228 MixerStrip::delete_processors ()
2230 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2234 MixerStrip::toggle_processors ()
2236 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2240 MixerStrip::ab_plugins ()
2242 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2246 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2248 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2251 if (ev->button == 3) {
2252 popup_level_meter_menu (ev);
2260 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2262 using namespace Gtk::Menu_Helpers;
2264 Gtk::Menu* m = manage (new Menu);
2265 MenuList& items = m->items ();
2267 RadioMenuItem::Group group;
2269 _suspend_menu_callbacks = true;
2270 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2271 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2272 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2273 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2274 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2276 if (gpm.meter_channels().n_audio() == 0) {
2277 m->popup (ev->button, ev->time);
2278 _suspend_menu_callbacks = false;
2282 RadioMenuItem::Group tgroup;
2283 items.push_back (SeparatorElem());
2285 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2286 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2287 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2288 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2289 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2290 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2291 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2292 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2293 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2294 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2297 if (_route->is_master()) {
2300 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2301 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2302 /* non-master bus */
2305 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2312 MeterType cmt = _route->meter_type();
2313 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2315 items.push_back (SeparatorElem());
2316 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2317 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2318 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2319 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2320 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2321 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2323 m->popup (ev->button, ev->time);
2324 _suspend_menu_callbacks = false;
2328 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2329 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2331 using namespace Menu_Helpers;
2333 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2334 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2335 i->set_active (_route->meter_point() == point);
2339 MixerStrip::set_meter_point (MeterPoint p)
2341 if (_suspend_menu_callbacks) return;
2342 _route->set_meter_point (p);
2346 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2347 RadioMenuItem::Group& group, string const & name, MeterType type)
2349 using namespace Menu_Helpers;
2351 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2352 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2353 i->set_active (_route->meter_type() == type);
2357 MixerStrip::set_meter_type (MeterType t)
2359 if (_suspend_menu_callbacks) return;