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());
1546 /* note that this relies on selection being shared across editor and
1547 mixer (or global to the backend, in the future), which is the only
1548 sane thing for users anyway.
1550 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1554 MixerStrip::name_button_button_press (GdkEventButton* ev)
1556 if (ev->button == 3) {
1557 list_route_operations ();
1559 /* do not allow rename if the track is record-enabled */
1560 rename_menu_item->set_sensitive (!_route->record_enabled());
1561 route_ops_menu->popup (1, ev->time);
1570 MixerStrip::name_button_button_release (GdkEventButton* ev)
1572 if (ev->button == 1) {
1573 list_route_operations ();
1575 /* do not allow rename if the track is record-enabled */
1576 rename_menu_item->set_sensitive (!_route->record_enabled());
1577 route_ops_menu->popup (1, ev->time);
1584 MixerStrip::number_button_button_press (GdkEventButton* ev)
1586 if ( ev->button == 3 ) {
1587 list_route_operations ();
1589 /* do not allow rename if the track is record-enabled */
1590 rename_menu_item->set_sensitive (!_route->record_enabled());
1591 route_ops_menu->popup (1, ev->time);
1600 MixerStrip::list_route_operations ()
1602 delete route_ops_menu;
1603 build_route_ops_menu ();
1607 MixerStrip::set_selected (bool yn)
1609 AxisView::set_selected (yn);
1611 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1612 global_frame.set_name ("MixerStripSelectedFrame");
1614 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1615 global_frame.set_name ("MixerStripFrame");
1617 global_frame.queue_draw ();
1620 // processor_box.deselect_all_processors();
1624 MixerStrip::property_changed (const PropertyChange& what_changed)
1626 RouteUI::property_changed (what_changed);
1628 if (what_changed.contains (ARDOUR::Properties::name)) {
1634 MixerStrip::name_changed ()
1638 name_button.set_text (_route->name());
1641 name_button.set_text (PBD::short_version (_route->name(), 5));
1645 ARDOUR_UI::instance()->set_tip (name_button, _route->name());
1647 if (_session->config.get_track_name_number()) {
1648 const int64_t track_number = _route->track_number ();
1649 if (track_number == 0) {
1650 number_label.set_text ("-");
1652 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1655 number_label.set_text ("");
1660 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1662 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1666 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1668 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1672 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1674 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1678 MixerStrip::width_button_pressed (GdkEventButton* ev)
1680 if (ev->button != 1) {
1684 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1687 _mixer.set_strip_width (Narrow, true);
1691 _mixer.set_strip_width (Wide, true);
1697 set_width_enum (Narrow, this);
1700 set_width_enum (Wide, this);
1709 MixerStrip::hide_clicked ()
1711 // LAME fix to reset the button status for when it is redisplayed (part 1)
1712 hide_button.set_sensitive(false);
1715 Hiding(); /* EMIT_SIGNAL */
1717 _mixer.hide_strip (this);
1721 hide_button.set_sensitive(true);
1725 MixerStrip::set_embedded (bool yn)
1731 MixerStrip::map_frozen ()
1733 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1735 boost::shared_ptr<AudioTrack> at = audio_track();
1738 switch (at->freeze_state()) {
1739 case AudioTrack::Frozen:
1740 processor_box.set_sensitive (false);
1741 hide_redirect_editors ();
1744 processor_box.set_sensitive (true);
1745 // XXX need some way, maybe, to retoggle redirect editors
1752 MixerStrip::hide_redirect_editors ()
1754 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1758 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1760 boost::shared_ptr<Processor> processor (p.lock ());
1765 Gtk::Window* w = processor_box.get_processor_ui (processor);
1773 MixerStrip::reset_strip_style ()
1775 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1777 gpm.set_fader_name ("SendStripBase");
1781 if (is_midi_track()) {
1782 if (_route->active()) {
1783 set_name ("MidiTrackStripBase");
1785 set_name ("MidiTrackStripBaseInactive");
1787 gpm.set_fader_name ("MidiTrackFader");
1788 } else if (is_audio_track()) {
1789 if (_route->active()) {
1790 set_name ("AudioTrackStripBase");
1792 set_name ("AudioTrackStripBaseInactive");
1794 gpm.set_fader_name ("AudioTrackFader");
1796 if (_route->active()) {
1797 set_name ("AudioBusStripBase");
1799 set_name ("AudioBusStripBaseInactive");
1801 gpm.set_fader_name ("AudioBusFader");
1803 /* (no MIDI busses yet) */
1810 MixerStrip::engine_stopped ()
1815 MixerStrip::engine_running ()
1820 MixerStrip::meter_point_string (MeterPoint mp)
1833 case MeterPostFader:
1850 return S_("Meter|In");
1854 return S_("Meter|Pr");
1857 case MeterPostFader:
1858 return S_("Meter|Po");
1862 return S_("Meter|O");
1867 return S_("Meter|C");
1876 /** Called when the metering point has changed */
1878 MixerStrip::meter_changed ()
1880 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1881 gpm.setup_meters ();
1882 // reset peak when meter point changes
1883 gpm.reset_peak_display();
1886 /** The bus that we are displaying sends to has changed, or been turned off.
1887 * @param send_to New bus that we are displaying sends to, or 0.
1890 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1892 RouteUI::bus_send_display_changed (send_to);
1895 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1900 revert_to_default_display ();
1903 revert_to_default_display ();
1908 MixerStrip::drop_send ()
1910 boost::shared_ptr<Send> current_send;
1912 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1913 current_send->set_metering (false);
1916 send_gone_connection.disconnect ();
1917 input_button.set_sensitive (true);
1918 output_button.set_sensitive (true);
1919 group_button.set_sensitive (true);
1920 set_invert_sensitive (true);
1921 meter_point_button.set_sensitive (true);
1922 mute_button->set_sensitive (true);
1923 solo_button->set_sensitive (true);
1924 rec_enable_button->set_sensitive (true);
1925 solo_isolated_led->set_sensitive (true);
1926 solo_safe_led->set_sensitive (true);
1927 monitor_input_button->set_sensitive (true);
1928 monitor_disk_button->set_sensitive (true);
1929 _comment_button.set_sensitive (true);
1933 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1935 _current_delivery = d;
1936 DeliveryChanged (_current_delivery);
1940 MixerStrip::show_send (boost::shared_ptr<Send> send)
1946 set_current_delivery (send);
1948 send->meter()->set_type(_route->shared_peak_meter()->get_type());
1949 send->set_metering (true);
1950 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
1952 gain_meter().set_controls (_route, send->meter(), send->amp());
1953 gain_meter().setup_meters ();
1955 uint32_t const in = _current_delivery->pans_required();
1956 uint32_t const out = _current_delivery->pan_outs();
1958 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
1959 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1960 panner_ui().setup_pan ();
1961 panner_ui().set_send_drawing_mode (true);
1962 panner_ui().show_all ();
1964 input_button.set_sensitive (false);
1965 group_button.set_sensitive (false);
1966 set_invert_sensitive (false);
1967 meter_point_button.set_sensitive (false);
1968 mute_button->set_sensitive (false);
1969 solo_button->set_sensitive (false);
1970 rec_enable_button->set_sensitive (false);
1971 solo_isolated_led->set_sensitive (false);
1972 solo_safe_led->set_sensitive (false);
1973 monitor_input_button->set_sensitive (false);
1974 monitor_disk_button->set_sensitive (false);
1975 _comment_button.set_sensitive (false);
1977 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
1978 output_button.set_sensitive (false);
1981 reset_strip_style ();
1985 MixerStrip::revert_to_default_display ()
1989 set_current_delivery (_route->main_outs ());
1991 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
1992 gain_meter().setup_meters ();
1994 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
1995 update_panner_choices();
1996 panner_ui().setup_pan ();
1997 panner_ui().set_send_drawing_mode (false);
1999 if (has_audio_outputs ()) {
2000 panners.show_all ();
2002 panners.hide_all ();
2005 reset_strip_style ();
2009 MixerStrip::set_button_names ()
2013 mute_button->set_text (_("Mute"));
2014 monitor_input_button->set_text (_("In"));
2015 monitor_disk_button->set_text (_("Disk"));
2017 if (_route && _route->solo_safe()) {
2018 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2020 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2022 if (!Config->get_solo_control_is_listen_control()) {
2023 solo_button->set_text (_("Solo"));
2025 switch (Config->get_listen_position()) {
2026 case AfterFaderListen:
2027 solo_button->set_text (_("AFL"));
2029 case PreFaderListen:
2030 solo_button->set_text (_("PFL"));
2034 solo_isolated_led->set_text (_("Iso"));
2035 solo_safe_led->set_text (S_("SoloLock|Lock"));
2039 mute_button->set_text (S_("Mute|M"));
2040 monitor_input_button->set_text (S_("MonitorInput|I"));
2041 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2043 if (_route && _route->solo_safe()) {
2044 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2046 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2048 if (!Config->get_solo_control_is_listen_control()) {
2049 solo_button->set_text (S_("Solo|S"));
2051 switch (Config->get_listen_position()) {
2052 case AfterFaderListen:
2053 solo_button->set_text (S_("AfterFader|A"));
2055 case PreFaderListen:
2056 solo_button->set_text (S_("Prefader|P"));
2061 solo_isolated_led->set_text (S_("SoloIso|I"));
2062 solo_safe_led->set_text (S_("SoloLock|L"));
2067 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2069 meter_point_button.set_text ("");
2074 MixerStrip::plugin_selector()
2076 return _mixer.plugin_selector();
2080 MixerStrip::hide_things ()
2082 processor_box.hide_things ();
2086 MixerStrip::input_active_button_press (GdkEventButton*)
2088 /* nothing happens on press */
2093 MixerStrip::input_active_button_release (GdkEventButton* ev)
2095 boost::shared_ptr<MidiTrack> mt = midi_track ();
2101 boost::shared_ptr<RouteList> rl (new RouteList);
2103 rl->push_back (route());
2105 _session->set_exclusive_input_active (rl, !mt->input_active(),
2106 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2112 MixerStrip::midi_input_status_changed ()
2114 if (midi_input_enable_button) {
2115 boost::shared_ptr<MidiTrack> mt = midi_track ();
2117 midi_input_enable_button->set_active (mt->input_active ());
2122 MixerStrip::state_id () const
2124 return string_compose ("strip %1", _route->id().to_s());
2128 MixerStrip::parameter_changed (string p)
2130 if (p == _visibility.get_state_name()) {
2131 /* The user has made changes to the mixer strip visibility, so get
2132 our VisibilityGroup to reflect these changes in our widgets.
2134 _visibility.set_state (ARDOUR_UI::config()->get_mixer_strip_visibility ());
2136 else if (p == "track-name-number") {
2141 /** Called to decide whether the solo isolate / solo lock button visibility should
2142 * be overridden from that configured by the user. We do this for the master bus.
2144 * @return optional value that is present if visibility state should be overridden.
2146 boost::optional<bool>
2147 MixerStrip::override_solo_visibility () const
2149 if (_route && _route->is_master ()) {
2150 return boost::optional<bool> (false);
2153 return boost::optional<bool> ();
2157 MixerStrip::add_input_port (DataType t)
2159 _route->input()->add_port ("", this, t);
2163 MixerStrip::add_output_port (DataType t)
2165 _route->output()->add_port ("", this, t);
2169 MixerStrip::route_active_changed ()
2171 reset_strip_style ();
2175 MixerStrip::copy_processors ()
2177 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2181 MixerStrip::cut_processors ()
2183 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2187 MixerStrip::paste_processors ()
2189 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2193 MixerStrip::select_all_processors ()
2195 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2199 MixerStrip::deselect_all_processors ()
2201 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2205 MixerStrip::delete_processors ()
2207 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2211 MixerStrip::toggle_processors ()
2213 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2217 MixerStrip::ab_plugins ()
2219 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2223 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2225 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2228 if (ev->button == 3) {
2229 popup_level_meter_menu (ev);
2237 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2239 using namespace Gtk::Menu_Helpers;
2241 Gtk::Menu* m = manage (new Menu);
2242 MenuList& items = m->items ();
2244 RadioMenuItem::Group group;
2246 _suspend_menu_callbacks = true;
2247 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2248 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2249 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2250 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2251 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2253 if (gpm.meter_channels().n_audio() == 0) {
2254 m->popup (ev->button, ev->time);
2255 _suspend_menu_callbacks = false;
2259 RadioMenuItem::Group tgroup;
2260 items.push_back (SeparatorElem());
2262 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2263 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2264 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2265 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2266 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2267 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2268 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2269 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2270 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2271 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2274 if (_route->is_master()) {
2277 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2278 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2279 /* non-master bus */
2282 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2289 MeterType cmt = _route->meter_type();
2290 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2292 items.push_back (SeparatorElem());
2293 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2294 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2295 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2296 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2297 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2298 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2300 m->popup (ev->button, ev->time);
2301 _suspend_menu_callbacks = false;
2305 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2306 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2308 using namespace Menu_Helpers;
2310 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2311 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2312 i->set_active (_route->meter_point() == point);
2316 MixerStrip::set_meter_point (MeterPoint p)
2318 if (_suspend_menu_callbacks) return;
2319 _route->set_meter_point (p);
2323 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2324 RadioMenuItem::Group& group, string const & name, MeterType type)
2326 using namespace Menu_Helpers;
2328 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2329 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2330 i->set_active (_route->meter_type() == type);
2334 MixerStrip::set_meter_type (MeterType t)
2336 if (_suspend_menu_callbacks) return;