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));
690 if (show_sends_button) {
691 show_sends_button->set_text (_("Aux"));
694 gpm.gain_automation_style_button.set_text (
695 gpm.astyle_string(gain_automation->automation_style()));
696 gpm.gain_automation_state_button.set_text (
697 gpm.astate_string(gain_automation->automation_state()));
699 if (_route->panner()) {
700 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
701 panners.astyle_string(_route->panner()->automation_style()));
702 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
703 panners.astate_string(_route->panner()->automation_state()));
707 set_size_request (max (110, gpm.get_gm_width()+5), -1);
712 if (show_sends_button) {
713 show_sends_button->set_text (_("Snd"));
716 gpm.gain_automation_style_button.set_text (
717 gpm.short_astyle_string(gain_automation->automation_style()));
718 gpm.gain_automation_state_button.set_text (
719 gpm.short_astate_string(gain_automation->automation_state()));
720 gain_meter().setup_meters (); // recalc meter width
722 if (_route->panner()) {
723 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
724 panners.short_astyle_string(_route->panner()->automation_style()));
725 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
726 panners.short_astate_string(_route->panner()->automation_state()));
729 set_size_request (max (60, gpm.get_gm_width() + 10), -1);
733 processor_box.set_width (w);
735 update_input_display ();
736 update_output_display ();
737 setup_comment_button ();
738 route_group_changed ();
744 MixerStrip::set_packed (bool yn)
749 set_gui_property ("visible", true);
751 set_gui_property ("visible", false);
756 struct RouteCompareByName {
757 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
758 return a->name().compare (b->name()) < 0;
763 MixerStrip::output_release (GdkEventButton *ev)
765 switch (ev->button) {
767 edit_output_configuration ();
775 MixerStrip::output_press (GdkEventButton *ev)
777 using namespace Menu_Helpers;
778 if (!_session->engine().connected()) {
779 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
784 MenuList& citems = output_menu.items();
785 switch (ev->button) {
788 return false; //wait for the mouse-up to pop the dialog
792 output_menu.set_name ("ArdourContextMenu");
794 output_menu_bundles.clear ();
796 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
798 citems.push_back (SeparatorElem());
799 uint32_t const n_with_separator = citems.size ();
801 ARDOUR::BundleList current = _route->output()->bundles_connected ();
803 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
805 /* give user bundles first chance at being in the menu */
807 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
808 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
809 maybe_add_bundle_to_output_menu (*i, current);
813 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
814 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
815 maybe_add_bundle_to_output_menu (*i, current);
819 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
820 RouteList copy = *routes;
821 copy.sort (RouteCompareByName ());
822 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
823 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
826 if (citems.size() == n_with_separator) {
827 /* no routes added; remove the separator */
831 citems.push_back (SeparatorElem());
833 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
836 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
837 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
842 citems.push_back (SeparatorElem());
843 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
845 output_menu.popup (1, ev->time);
856 MixerStrip::input_release (GdkEventButton *ev)
858 switch (ev->button) {
861 edit_input_configuration ();
873 MixerStrip::input_press (GdkEventButton *ev)
875 using namespace Menu_Helpers;
877 MenuList& citems = input_menu.items();
878 input_menu.set_name ("ArdourContextMenu");
881 if (!_session->engine().connected()) {
882 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
887 if (_session->actively_recording() && _route->record_enabled())
890 switch (ev->button) {
893 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
897 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
899 citems.push_back (SeparatorElem());
900 uint32_t const n_with_separator = citems.size ();
902 input_menu_bundles.clear ();
904 ARDOUR::BundleList current = _route->input()->bundles_connected ();
906 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
908 /* give user bundles first chance at being in the menu */
910 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
911 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
912 maybe_add_bundle_to_input_menu (*i, current);
916 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
917 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
918 maybe_add_bundle_to_input_menu (*i, current);
922 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
923 RouteList copy = *routes;
924 copy.sort (RouteCompareByName ());
925 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
926 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
929 if (citems.size() == n_with_separator) {
930 /* no routes added; remove the separator */
934 citems.push_back (SeparatorElem());
935 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
938 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
939 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
944 citems.push_back (SeparatorElem());
945 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
947 input_menu.popup (1, ev->time);
958 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
964 ARDOUR::BundleList current = _route->input()->bundles_connected ();
966 if (std::find (current.begin(), current.end(), c) == current.end()) {
967 _route->input()->connect_ports_to_bundle (c, true, this);
969 _route->input()->disconnect_ports_from_bundle (c, this);
974 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
980 ARDOUR::BundleList current = _route->output()->bundles_connected ();
982 if (std::find (current.begin(), current.end(), c) == current.end()) {
983 _route->output()->connect_ports_to_bundle (c, true, this);
985 _route->output()->disconnect_ports_from_bundle (c, this);
990 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
992 using namespace Menu_Helpers;
994 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
998 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
999 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1003 if (i != input_menu_bundles.end()) {
1007 input_menu_bundles.push_back (b);
1009 MenuList& citems = input_menu.items();
1011 std::string n = b->name ();
1012 replace_all (n, "_", " ");
1014 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1018 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1020 using namespace Menu_Helpers;
1022 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1026 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1027 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1031 if (i != output_menu_bundles.end()) {
1035 output_menu_bundles.push_back (b);
1037 MenuList& citems = output_menu.items();
1039 std::string n = b->name ();
1040 replace_all (n, "_", " ");
1042 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1046 MixerStrip::update_diskstream_display ()
1048 if (is_track() && input_selector) {
1049 input_selector->hide_all ();
1052 route_color_changed ();
1056 MixerStrip::connect_to_pan ()
1058 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1060 panstate_connection.disconnect ();
1061 panstyle_connection.disconnect ();
1063 if (!_route->panner()) {
1067 boost::shared_ptr<Pannable> p = _route->pannable ();
1069 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1070 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1072 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1073 * However, that only works a panner was previously set.
1075 * PannerUI must remain subscribed to _panshell->Changed() in case
1076 * we switch the panner eg. AUX-Send and back
1077 * _route->panner_shell()->Changed() vs _panshell->Changed
1079 if (panners._panner == 0) {
1080 panners.panshell_changed ();
1082 update_panner_choices();
1086 MixerStrip::update_panner_choices ()
1088 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1089 if (!_route->panner_shell()) { return; }
1091 uint32_t in = _route->output()->n_ports().n_audio();
1093 if (_route->panner()) {
1094 in = _route->panner()->in().n_audio();
1097 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1101 * Output port labelling
1102 * =====================
1104 * Case 1: Each output has one connection, all connections are to system:playback_%i
1105 * out 1 -> system:playback_1
1106 * out 2 -> system:playback_2
1107 * out 3 -> system:playback_3
1110 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1111 * out 1 -> ardour:track_x/in 1
1112 * out 2 -> ardour:track_x/in 2
1113 * Display as: track_x
1115 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1116 * out 1 -> program x:foo
1117 * out 2 -> program x:foo
1118 * Display as: program x
1120 * Case 4: No connections (Disconnected)
1123 * Default case (unusual routing):
1124 * Display as: *number of connections*
1128 * .-----------------------------------------------.
1130 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1131 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1132 * '-----------------------------------------------'
1133 * .-----------------------------------------------.
1136 * '-----------------------------------------------'
1140 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1144 boost::shared_ptr<Port> port;
1145 vector<string> port_connections;
1147 uint32_t total_connection_count = 0;
1148 uint32_t io_connection_count = 0;
1149 uint32_t ardour_connection_count = 0;
1150 uint32_t system_connection_count = 0;
1151 uint32_t other_connection_count = 0;
1153 ostringstream label;
1155 bool have_label = false;
1156 bool each_io_has_one_connection = true;
1158 string connection_name;
1159 string ardour_track_name;
1160 string other_connection_type;
1161 string system_ports;
1164 ostringstream tooltip;
1165 char * tooltip_cstr;
1167 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1168 DataType dt = DataType::AUDIO;
1169 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 )
1170 dt = DataType::MIDI;
1173 io_count = route->n_inputs().n_total();
1174 tooltip << string_compose (_("<b>INPUT</b> to %1"), Glib::Markup::escape_text(route->name()));
1176 io_count = route->n_outputs().n_total();
1177 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(route->name()));
1181 for (io_index = 0; io_index < io_count; ++io_index) {
1183 port = route->input()->nth (io_index);
1185 port = route->output()->nth (io_index);
1188 //ignore any port connections that don't match our DataType
1189 if (port->type() != dt)
1192 port_connections.clear ();
1193 port->get_connections(port_connections);
1194 io_connection_count = 0;
1196 if (!port_connections.empty()) {
1197 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1199 string& connection_name (*i);
1201 if (connection_name.find("system:") == 0) {
1202 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1205 if (io_connection_count == 0) {
1206 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1208 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1211 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1214 if (connection_name.find("ardour:") == 0) {
1215 if (ardour_track_name.empty()) {
1216 // "ardour:Master/in 1" -> "ardour:Master/"
1217 string::size_type slash = connection_name.find("/");
1218 if (slash != string::npos) {
1219 ardour_track_name = connection_name.substr(0, slash + 1);
1223 if (connection_name.find(ardour_track_name) == 0) {
1224 ++ardour_connection_count;
1226 } else if (!pn.empty()) {
1227 if (system_ports.empty()) {
1230 system_ports += "/" + pn;
1232 if (connection_name.find("system:") == 0) {
1233 ++system_connection_count;
1235 } else if (connection_name.find("system:midi_") == 0) {
1237 // "system:midi_capture_123" -> "123"
1238 system_port = "M " + connection_name.substr(20);
1240 // "system:midi_playback_123" -> "123"
1241 system_port = "M " + connection_name.substr(21);
1244 if (system_ports.empty()) {
1245 system_ports += system_port;
1247 system_ports += "/" + system_port;
1250 ++system_connection_count;
1252 } else if (connection_name.find("system:") == 0) {
1254 // "system:capture_123" -> "123"
1255 system_port = connection_name.substr(15);
1257 // "system:playback_123" -> "123"
1258 system_port = connection_name.substr(16);
1261 if (system_ports.empty()) {
1262 system_ports += system_port;
1264 system_ports += "/" + system_port;
1267 ++system_connection_count;
1269 if (other_connection_type.empty()) {
1270 // "jamin:in 1" -> "jamin:"
1271 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1274 if (connection_name.find(other_connection_type) == 0) {
1275 ++other_connection_count;
1279 ++total_connection_count;
1280 ++io_connection_count;
1284 if (io_connection_count != 1) {
1285 each_io_has_one_connection = false;
1289 if (total_connection_count == 0) {
1290 tooltip << endl << _("Disconnected");
1293 tooltip_cstr = new char[tooltip.str().size() + 1];
1294 strcpy(tooltip_cstr, tooltip.str().c_str());
1297 ARDOUR_UI::instance()->set_tip (&input_button, tooltip_cstr, "");
1299 ARDOUR_UI::instance()->set_tip (&output_button, tooltip_cstr, "");
1302 if (each_io_has_one_connection) {
1303 if (total_connection_count == ardour_connection_count) {
1304 // all connections are to the same track in ardour
1305 // "ardour:Master/" -> "Master"
1306 string::size_type slash = ardour_track_name.find("/");
1307 if (slash != string::npos) {
1308 label << ardour_track_name.substr(7, slash - 7);
1312 else if (total_connection_count == system_connection_count) {
1313 // all connections are to system ports
1314 label << system_ports;
1317 else if (total_connection_count == other_connection_count) {
1318 // all connections are to the same external program eg jamin
1319 // "jamin:" -> "jamin"
1320 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1326 if (total_connection_count == 0) {
1330 // Odd configuration
1331 label << "*" << total_connection_count << "*";
1336 input_button.set_text (label.str());
1338 output_button.set_text (label.str());
1343 MixerStrip::update_input_display ()
1345 update_io_button (_route, _width, true);
1346 panners.setup_pan ();
1348 if (has_audio_outputs ()) {
1349 panners.show_all ();
1351 panners.hide_all ();
1357 MixerStrip::update_output_display ()
1359 update_io_button (_route, _width, false);
1360 gpm.setup_meters ();
1361 panners.setup_pan ();
1363 if (has_audio_outputs ()) {
1364 panners.show_all ();
1366 panners.hide_all ();
1371 MixerStrip::fast_update ()
1373 gpm.update_meters ();
1377 MixerStrip::diskstream_changed ()
1379 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1383 MixerStrip::io_changed_proxy ()
1385 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1389 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1391 boost::shared_ptr<Port> a = wa.lock ();
1392 boost::shared_ptr<Port> b = wb.lock ();
1394 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1395 update_input_display ();
1396 set_width_enum (_width, this);
1399 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1400 update_output_display ();
1401 set_width_enum (_width, this);
1406 MixerStrip::setup_comment_button ()
1411 if (_route->comment().empty ()) {
1412 _comment_button.unset_bg (STATE_NORMAL);
1413 _comment_button.set_text (_("Comments"));
1415 _comment_button.modify_bg (STATE_NORMAL, color ());
1416 _comment_button.set_text (_("*Comments*"));
1421 if (_route->comment().empty ()) {
1422 _comment_button.unset_bg (STATE_NORMAL);
1423 _comment_button.set_text (_("Cmt"));
1425 _comment_button.modify_bg (STATE_NORMAL, color ());
1426 _comment_button.set_text (_("*Cmt*"));
1431 ARDOUR_UI::instance()->set_tip (
1432 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1438 MixerStrip::select_route_group (GdkEventButton *ev)
1440 using namespace Menu_Helpers;
1442 if (ev->button == 1) {
1444 if (group_menu == 0) {
1446 PropertyList* plist = new PropertyList();
1448 plist->add (Properties::gain, true);
1449 plist->add (Properties::mute, true);
1450 plist->add (Properties::solo, true);
1452 group_menu = new RouteGroupMenu (_session, plist);
1456 r.push_back (route ());
1457 group_menu->build (r);
1458 group_menu->menu()->popup (1, ev->time);
1465 MixerStrip::route_group_changed ()
1467 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1469 RouteGroup *rg = _route->route_group();
1472 group_button.set_text (PBD::short_version (rg->name(), 5));
1476 group_button.set_text (_("Grp"));
1479 group_button.set_text (_("~G"));
1486 MixerStrip::route_color_changed ()
1488 name_button.modify_bg (STATE_NORMAL, color());
1489 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1490 reset_strip_style ();
1494 MixerStrip::show_passthru_color ()
1496 reset_strip_style ();
1500 MixerStrip::build_route_ops_menu ()
1502 using namespace Menu_Helpers;
1503 route_ops_menu = new Menu;
1504 route_ops_menu->set_name ("ArdourContextMenu");
1506 MenuList& items = route_ops_menu->items();
1508 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1510 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1512 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1514 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1516 items.push_back (SeparatorElem());
1518 if (!_route->is_master()) {
1519 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1521 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1522 rename_menu_item = &items.back();
1524 items.push_back (SeparatorElem());
1525 items.push_back (CheckMenuElem (_("Active")));
1526 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1527 i->set_active (_route->active());
1528 i->set_sensitive(! _session->transport_rolling());
1529 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1531 items.push_back (SeparatorElem());
1533 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1535 items.push_back (SeparatorElem());
1536 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1537 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1538 denormal_menu_item->set_active (_route->denormal_protection());
1540 if (!Profile->get_sae()) {
1541 items.push_back (SeparatorElem());
1542 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1545 items.push_back (SeparatorElem());
1548 /* note that this relies on selection being shared across editor and
1549 mixer (or global to the backend, in the future), which is the only
1550 sane thing for users anyway.
1553 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1555 Selection& selection (PublicEditor::instance().get_selection());
1556 if (!selection.selected (rtav)) {
1557 selection.set (rtav);
1560 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1566 MixerStrip::name_button_button_press (GdkEventButton* ev)
1568 if (ev->button == 3) {
1569 list_route_operations ();
1571 /* do not allow rename if the track is record-enabled */
1572 rename_menu_item->set_sensitive (!_route->record_enabled());
1573 route_ops_menu->popup (1, ev->time);
1582 MixerStrip::name_button_button_release (GdkEventButton* ev)
1584 if (ev->button == 1) {
1585 list_route_operations ();
1587 /* do not allow rename if the track is record-enabled */
1588 rename_menu_item->set_sensitive (!_route->record_enabled());
1589 route_ops_menu->popup (1, ev->time);
1596 MixerStrip::number_button_button_press (GdkEventButton* ev)
1598 if ( ev->button == 3 ) {
1599 list_route_operations ();
1601 /* do not allow rename if the track is record-enabled */
1602 rename_menu_item->set_sensitive (!_route->record_enabled());
1603 route_ops_menu->popup (1, ev->time);
1612 MixerStrip::list_route_operations ()
1614 delete route_ops_menu;
1615 build_route_ops_menu ();
1619 MixerStrip::set_selected (bool yn)
1621 AxisView::set_selected (yn);
1623 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1624 global_frame.set_name ("MixerStripSelectedFrame");
1626 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1627 global_frame.set_name ("MixerStripFrame");
1629 global_frame.queue_draw ();
1632 // processor_box.deselect_all_processors();
1636 MixerStrip::property_changed (const PropertyChange& what_changed)
1638 RouteUI::property_changed (what_changed);
1640 if (what_changed.contains (ARDOUR::Properties::name)) {
1646 MixerStrip::name_changed ()
1650 name_button.set_text (_route->name());
1653 name_button.set_text (PBD::short_version (_route->name(), 5));
1657 ARDOUR_UI::instance()->set_tip (name_button, _route->name());
1659 if (_session->config.get_track_name_number()) {
1660 const int64_t track_number = _route->track_number ();
1661 if (track_number == 0) {
1662 number_label.set_text ("-");
1664 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1667 number_label.set_text ("");
1672 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1674 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1678 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1680 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1684 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1686 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1690 MixerStrip::width_button_pressed (GdkEventButton* ev)
1692 if (ev->button != 1) {
1696 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1699 _mixer.set_strip_width (Narrow, true);
1703 _mixer.set_strip_width (Wide, true);
1709 set_width_enum (Narrow, this);
1712 set_width_enum (Wide, this);
1721 MixerStrip::hide_clicked ()
1723 // LAME fix to reset the button status for when it is redisplayed (part 1)
1724 hide_button.set_sensitive(false);
1727 Hiding(); /* EMIT_SIGNAL */
1729 _mixer.hide_strip (this);
1733 hide_button.set_sensitive(true);
1737 MixerStrip::set_embedded (bool yn)
1743 MixerStrip::map_frozen ()
1745 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1747 boost::shared_ptr<AudioTrack> at = audio_track();
1750 switch (at->freeze_state()) {
1751 case AudioTrack::Frozen:
1752 processor_box.set_sensitive (false);
1753 hide_redirect_editors ();
1756 processor_box.set_sensitive (true);
1757 // XXX need some way, maybe, to retoggle redirect editors
1764 MixerStrip::hide_redirect_editors ()
1766 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1770 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1772 boost::shared_ptr<Processor> processor (p.lock ());
1777 Gtk::Window* w = processor_box.get_processor_ui (processor);
1785 MixerStrip::reset_strip_style ()
1787 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1789 gpm.set_fader_name ("SendStripBase");
1793 if (is_midi_track()) {
1794 if (_route->active()) {
1795 set_name ("MidiTrackStripBase");
1797 set_name ("MidiTrackStripBaseInactive");
1799 gpm.set_fader_name ("MidiTrackFader");
1800 } else if (is_audio_track()) {
1801 if (_route->active()) {
1802 set_name ("AudioTrackStripBase");
1804 set_name ("AudioTrackStripBaseInactive");
1806 gpm.set_fader_name ("AudioTrackFader");
1808 if (_route->active()) {
1809 set_name ("AudioBusStripBase");
1811 set_name ("AudioBusStripBaseInactive");
1813 gpm.set_fader_name ("AudioBusFader");
1815 /* (no MIDI busses yet) */
1822 MixerStrip::engine_stopped ()
1827 MixerStrip::engine_running ()
1832 MixerStrip::meter_point_string (MeterPoint mp)
1845 case MeterPostFader:
1862 return S_("Meter|In");
1866 return S_("Meter|Pr");
1869 case MeterPostFader:
1870 return S_("Meter|Po");
1874 return S_("Meter|O");
1879 return S_("Meter|C");
1888 /** Called when the metering point has changed */
1890 MixerStrip::meter_changed ()
1892 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1893 gpm.setup_meters ();
1894 // reset peak when meter point changes
1895 gpm.reset_peak_display();
1898 /** The bus that we are displaying sends to has changed, or been turned off.
1899 * @param send_to New bus that we are displaying sends to, or 0.
1902 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1904 RouteUI::bus_send_display_changed (send_to);
1907 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1912 revert_to_default_display ();
1915 revert_to_default_display ();
1920 MixerStrip::drop_send ()
1922 boost::shared_ptr<Send> current_send;
1924 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1925 current_send->set_metering (false);
1928 send_gone_connection.disconnect ();
1929 input_button.set_sensitive (true);
1930 output_button.set_sensitive (true);
1931 group_button.set_sensitive (true);
1932 set_invert_sensitive (true);
1933 meter_point_button.set_sensitive (true);
1934 mute_button->set_sensitive (true);
1935 solo_button->set_sensitive (true);
1936 rec_enable_button->set_sensitive (true);
1937 solo_isolated_led->set_sensitive (true);
1938 solo_safe_led->set_sensitive (true);
1939 monitor_input_button->set_sensitive (true);
1940 monitor_disk_button->set_sensitive (true);
1941 _comment_button.set_sensitive (true);
1945 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1947 _current_delivery = d;
1948 DeliveryChanged (_current_delivery);
1952 MixerStrip::show_send (boost::shared_ptr<Send> send)
1958 set_current_delivery (send);
1960 send->meter()->set_type(_route->shared_peak_meter()->get_type());
1961 send->set_metering (true);
1962 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
1964 gain_meter().set_controls (_route, send->meter(), send->amp());
1965 gain_meter().setup_meters ();
1967 uint32_t const in = _current_delivery->pans_required();
1968 uint32_t const out = _current_delivery->pan_outs();
1970 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
1971 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1972 panner_ui().setup_pan ();
1973 panner_ui().set_send_drawing_mode (true);
1974 panner_ui().show_all ();
1976 input_button.set_sensitive (false);
1977 group_button.set_sensitive (false);
1978 set_invert_sensitive (false);
1979 meter_point_button.set_sensitive (false);
1980 mute_button->set_sensitive (false);
1981 solo_button->set_sensitive (false);
1982 rec_enable_button->set_sensitive (false);
1983 solo_isolated_led->set_sensitive (false);
1984 solo_safe_led->set_sensitive (false);
1985 monitor_input_button->set_sensitive (false);
1986 monitor_disk_button->set_sensitive (false);
1987 _comment_button.set_sensitive (false);
1989 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
1990 output_button.set_sensitive (false);
1993 reset_strip_style ();
1997 MixerStrip::revert_to_default_display ()
2001 set_current_delivery (_route->main_outs ());
2003 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
2004 gain_meter().setup_meters ();
2006 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2007 update_panner_choices();
2008 panner_ui().setup_pan ();
2009 panner_ui().set_send_drawing_mode (false);
2011 if (has_audio_outputs ()) {
2012 panners.show_all ();
2014 panners.hide_all ();
2017 reset_strip_style ();
2021 MixerStrip::set_button_names ()
2025 mute_button->set_text (_("Mute"));
2026 monitor_input_button->set_text (_("In"));
2027 monitor_disk_button->set_text (_("Disk"));
2029 if (_route && _route->solo_safe()) {
2030 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2032 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2034 if (!Config->get_solo_control_is_listen_control()) {
2035 solo_button->set_text (_("Solo"));
2037 switch (Config->get_listen_position()) {
2038 case AfterFaderListen:
2039 solo_button->set_text (_("AFL"));
2041 case PreFaderListen:
2042 solo_button->set_text (_("PFL"));
2046 solo_isolated_led->set_text (_("Iso"));
2047 solo_safe_led->set_text (S_("SoloLock|Lock"));
2051 mute_button->set_text (S_("Mute|M"));
2052 monitor_input_button->set_text (S_("MonitorInput|I"));
2053 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2055 if (_route && _route->solo_safe()) {
2056 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2058 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2060 if (!Config->get_solo_control_is_listen_control()) {
2061 solo_button->set_text (S_("Solo|S"));
2063 switch (Config->get_listen_position()) {
2064 case AfterFaderListen:
2065 solo_button->set_text (S_("AfterFader|A"));
2067 case PreFaderListen:
2068 solo_button->set_text (S_("Prefader|P"));
2073 solo_isolated_led->set_text (S_("SoloIso|I"));
2074 solo_safe_led->set_text (S_("SoloLock|L"));
2079 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2081 meter_point_button.set_text ("");
2086 MixerStrip::plugin_selector()
2088 return _mixer.plugin_selector();
2092 MixerStrip::hide_things ()
2094 processor_box.hide_things ();
2098 MixerStrip::input_active_button_press (GdkEventButton*)
2100 /* nothing happens on press */
2105 MixerStrip::input_active_button_release (GdkEventButton* ev)
2107 boost::shared_ptr<MidiTrack> mt = midi_track ();
2113 boost::shared_ptr<RouteList> rl (new RouteList);
2115 rl->push_back (route());
2117 _session->set_exclusive_input_active (rl, !mt->input_active(),
2118 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2124 MixerStrip::midi_input_status_changed ()
2126 if (midi_input_enable_button) {
2127 boost::shared_ptr<MidiTrack> mt = midi_track ();
2129 midi_input_enable_button->set_active (mt->input_active ());
2134 MixerStrip::state_id () const
2136 return string_compose ("strip %1", _route->id().to_s());
2140 MixerStrip::parameter_changed (string p)
2142 if (p == _visibility.get_state_name()) {
2143 /* The user has made changes to the mixer strip visibility, so get
2144 our VisibilityGroup to reflect these changes in our widgets.
2146 _visibility.set_state (ARDOUR_UI::config()->get_mixer_strip_visibility ());
2148 else if (p == "track-name-number") {
2153 /** Called to decide whether the solo isolate / solo lock button visibility should
2154 * be overridden from that configured by the user. We do this for the master bus.
2156 * @return optional value that is present if visibility state should be overridden.
2158 boost::optional<bool>
2159 MixerStrip::override_solo_visibility () const
2161 if (_route && _route->is_master ()) {
2162 return boost::optional<bool> (false);
2165 return boost::optional<bool> ();
2169 MixerStrip::add_input_port (DataType t)
2171 _route->input()->add_port ("", this, t);
2175 MixerStrip::add_output_port (DataType t)
2177 _route->output()->add_port ("", this, t);
2181 MixerStrip::route_active_changed ()
2183 reset_strip_style ();
2187 MixerStrip::copy_processors ()
2189 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2193 MixerStrip::cut_processors ()
2195 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2199 MixerStrip::paste_processors ()
2201 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2205 MixerStrip::select_all_processors ()
2207 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2211 MixerStrip::deselect_all_processors ()
2213 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2217 MixerStrip::delete_processors ()
2219 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2223 MixerStrip::toggle_processors ()
2225 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2229 MixerStrip::ab_plugins ()
2231 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2235 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2237 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2240 if (ev->button == 3) {
2241 popup_level_meter_menu (ev);
2249 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2251 using namespace Gtk::Menu_Helpers;
2253 Gtk::Menu* m = manage (new Menu);
2254 MenuList& items = m->items ();
2256 RadioMenuItem::Group group;
2258 _suspend_menu_callbacks = true;
2259 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2260 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2261 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2262 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2263 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2265 if (gpm.meter_channels().n_audio() == 0) {
2266 m->popup (ev->button, ev->time);
2267 _suspend_menu_callbacks = false;
2271 RadioMenuItem::Group tgroup;
2272 items.push_back (SeparatorElem());
2274 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2275 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2276 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2277 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2278 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2279 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2280 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2281 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2282 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2283 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2286 if (_route->is_master()) {
2289 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2290 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2291 /* non-master bus */
2294 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2301 MeterType cmt = _route->meter_type();
2302 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2304 items.push_back (SeparatorElem());
2305 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2306 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2307 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2308 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2309 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2310 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2312 m->popup (ev->button, ev->time);
2313 _suspend_menu_callbacks = false;
2317 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2318 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2320 using namespace Menu_Helpers;
2322 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2323 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2324 i->set_active (_route->meter_point() == point);
2328 MixerStrip::set_meter_point (MeterPoint p)
2330 if (_suspend_menu_callbacks) return;
2331 _route->set_meter_point (p);
2335 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2336 RadioMenuItem::Group& group, string const & name, MeterType type)
2338 using namespace Menu_Helpers;
2340 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2341 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2342 i->set_active (_route->meter_type() == type);
2346 MixerStrip::set_meter_type (MeterType t)
2348 if (_suspend_menu_callbacks) return;