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::StripWidth));
155 ARDOUR_UI::instance()->set_tip (width_button, t);
157 hide_button.set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::CloseCross));
158 ARDOUR_UI::instance()->set_tip (&hide_button, _("Hide this mixer strip"));
160 input_button_box.set_spacing(2);
162 input_button.set_text (_("Input"));
163 input_button.set_name ("mixer strip button");
164 input_button_box.pack_start (input_button, true, true);
166 output_button.set_text (_("Output"));
167 output_button.set_name ("mixer strip button");
169 ARDOUR_UI::instance()->set_tip (&meter_point_button, _("Click to select metering point"), "");
170 meter_point_button.set_name ("mixer strip button");
172 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
174 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
175 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
177 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
179 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
180 solo_isolated_led->show ();
181 solo_isolated_led->set_no_show_all (true);
182 solo_isolated_led->set_name (X_("solo isolate"));
183 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
184 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
185 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
187 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
188 solo_safe_led->show ();
189 solo_safe_led->set_no_show_all (true);
190 solo_safe_led->set_name (X_("solo safe"));
191 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
192 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
193 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
195 solo_safe_led->set_text (S_("SoloLock|Lock"));
196 solo_isolated_led->set_text (_("Iso"));
198 solo_iso_table.set_homogeneous (true);
199 solo_iso_table.set_spacings (2);
200 if (!ARDOUR::Profile->get_trx()) {
201 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
202 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
204 solo_iso_table.show ();
206 rec_mon_table.set_homogeneous (true);
207 rec_mon_table.set_row_spacings (2);
208 rec_mon_table.set_col_spacings (2);
209 if (ARDOUR::Profile->get_mixbus()) {
210 rec_mon_table.resize (1, 3);
211 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
212 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
213 } else if (!ARDOUR::Profile->get_trx()) {
214 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
215 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
217 rec_mon_table.show ();
219 if (solo_isolated_led) {
220 button_size_group->add_widget (*solo_isolated_led);
223 button_size_group->add_widget (*solo_safe_led);
226 if (!ARDOUR::Profile->get_mixbus()) {
227 if (rec_enable_button) {
228 button_size_group->add_widget (*rec_enable_button);
230 if (monitor_disk_button) {
231 button_size_group->add_widget (*monitor_disk_button);
233 if (monitor_input_button) {
234 button_size_group->add_widget (*monitor_input_button);
238 mute_solo_table.set_homogeneous (true);
239 mute_solo_table.set_spacings (2);
241 bottom_button_table.set_spacings (2);
242 bottom_button_table.set_homogeneous (true);
243 bottom_button_table.attach (group_button, 1, 2, 0, 1);
244 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
246 name_button.set_name ("mixer strip button");
247 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
248 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
250 ARDOUR_UI::instance()->set_tip (&group_button, _("Mix group"), "");
251 group_button.set_name ("mixer strip button");
253 _comment_button.set_name (X_("mixer strip button"));
254 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
256 global_vpacker.set_border_width (1);
257 global_vpacker.set_spacing (0);
259 width_button.set_name ("mixer strip button");
260 hide_button.set_name ("mixer strip button");
262 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
263 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
265 // width_hide_box.set_border_width (1);
266 width_hide_box.set_spacing (2);
267 width_hide_box.pack_start (width_button, false, true);
268 width_hide_box.pack_start (number_label, true, true);
269 width_hide_box.pack_end (hide_button, false, true);
271 number_label.set_text ("-");
272 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
273 number_label.set_no_show_all ();
274 number_label.set_name ("tracknumber label");
275 number_label.set_fixed_colors (0x80808080, 0x80808080);
276 number_label.set_alignment (.5, .5);
277 number_label.set_fallthrough_to_parent (true);
279 global_vpacker.set_spacing (2);
280 if (!ARDOUR::Profile->get_trx()) {
281 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
282 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
283 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
284 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
285 global_vpacker.pack_start (processor_box, true, true);
287 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
288 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
289 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
290 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
291 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
292 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
293 if (!ARDOUR::Profile->get_trx()) {
294 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
295 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
300 global_frame.add (global_vpacker);
301 global_frame.set_shadow_type (Gtk::SHADOW_IN);
302 global_frame.set_name ("BaseFrame");
306 /* force setting of visible selected status */
309 set_selected (false);
314 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
315 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
317 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
318 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
319 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
321 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
322 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
324 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
325 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
326 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
328 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
330 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
331 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
333 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
337 /* start off as a passthru strip. we'll correct this, if necessary,
338 in update_diskstream_display().
341 /* start off as a passthru strip. we'll correct this, if necessary,
342 in update_diskstream_display().
345 if (is_midi_track()) {
346 set_name ("MidiTrackStripBase");
348 set_name ("AudioTrackStripBase");
351 add_events (Gdk::BUTTON_RELEASE_MASK|
352 Gdk::ENTER_NOTIFY_MASK|
353 Gdk::LEAVE_NOTIFY_MASK|
355 Gdk::KEY_RELEASE_MASK);
357 set_flags (get_flags() | Gtk::CAN_FOCUS);
359 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
360 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
363 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
364 must be the same as those used in RCOptionEditor so that the configuration changes
365 are recognised when they occur.
367 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
368 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
369 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
370 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
371 _visibility.add (&output_button, X_("Output"), _("Output"), false);
372 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
374 parameter_changed (X_("mixer-element-visibility"));
375 ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
376 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
377 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
379 //watch for mouse enter/exit so we can do some stuff
380 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
381 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
383 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
386 MixerStrip::~MixerStrip ()
388 CatchDeletion (this);
390 if (this ==_entered_mixer_strip)
391 _entered_mixer_strip = NULL;
395 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
397 _entered_mixer_strip = this;
399 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
400 //because the mixerstrip control is a parent that encompasses the strip
401 deselect_all_processors();
407 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
409 //if we have moved outside our strip, but not into a child view, then deselect ourselves
410 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
411 _entered_mixer_strip= 0;
413 //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
414 gpm.gain_display.set_sensitive(false);
416 gpm.gain_display.set_sensitive(true);
418 //if we leave this mixer strip we need to clear out any selections
419 //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
426 MixerStrip::set_route (boost::shared_ptr<Route> rt)
428 //the rec/monitor stuff only shows up for tracks.
429 //the show_sends only shows up for buses.
430 //remove them all here, and we may add them back later
431 if (show_sends_button->get_parent()) {
432 rec_mon_table.remove (*show_sends_button);
434 if (rec_enable_button->get_parent()) {
435 rec_mon_table.remove (*rec_enable_button);
437 if (monitor_input_button->get_parent()) {
438 rec_mon_table.remove (*monitor_input_button);
440 if (monitor_disk_button->get_parent()) {
441 rec_mon_table.remove (*monitor_disk_button);
443 if (group_button.get_parent()) {
444 bottom_button_table.remove (group_button);
447 RouteUI::set_route (rt);
449 /* ProcessorBox needs access to _route so that it can read
452 processor_box.set_route (rt);
454 revert_to_default_display ();
456 /* unpack these from the parent and stuff them into our own
460 if (gpm.peak_display.get_parent()) {
461 gpm.peak_display.get_parent()->remove (gpm.peak_display);
463 if (gpm.gain_display.get_parent()) {
464 gpm.gain_display.get_parent()->remove (gpm.gain_display);
467 gpm.set_type (rt->meter_type());
469 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
470 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
472 if (solo_button->get_parent()) {
473 mute_solo_table.remove (*solo_button);
476 if (mute_button->get_parent()) {
477 mute_solo_table.remove (*mute_button);
480 if (route()->is_master()) {
481 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
482 solo_button->hide ();
483 mute_button->show ();
484 rec_mon_table.hide ();
485 if (solo_iso_table.get_parent()) {
486 solo_iso_table.get_parent()->remove(solo_iso_table);
489 bottom_button_table.attach (group_button, 1, 2, 0, 1);
490 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
491 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
492 mute_button->show ();
493 solo_button->show ();
494 rec_mon_table.show ();
497 if (_mixer_owned && route()->is_master() ) {
499 HScrollbar scrollbar;
500 Gtk::Requisition requisition(scrollbar.size_request ());
501 int scrollbar_height = requisition.height;
503 spacer = manage (new EventBox);
504 spacer->set_size_request (-1, scrollbar_height+2);
505 global_vpacker.pack_start (*spacer, false, false);
510 monitor_input_button->show ();
511 monitor_disk_button->show ();
513 monitor_input_button->hide();
514 monitor_disk_button->hide ();
517 if (is_midi_track()) {
518 if (midi_input_enable_button == 0) {
519 midi_input_enable_button = manage (new ArdourButton);
520 midi_input_enable_button->set_name ("midi input button");
521 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::DinMidi));
522 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
523 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
524 ARDOUR_UI::instance()->set_tip (midi_input_enable_button, _("Enable/Disable MIDI input"));
526 input_button_box.remove (*midi_input_enable_button);
528 /* get current state */
529 midi_input_status_changed ();
530 input_button_box.pack_start (*midi_input_enable_button, false, false);
532 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
534 if (midi_input_enable_button) {
535 /* removal from the container will delete it */
536 input_button_box.remove (*midi_input_enable_button);
537 midi_input_enable_button = 0;
541 if (is_audio_track()) {
542 boost::shared_ptr<AudioTrack> at = audio_track();
543 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
548 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
549 rec_enable_button->set_sensitive (_session->writable());
550 rec_enable_button->show();
552 if (ARDOUR::Profile->get_mixbus()) {
553 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
554 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
555 } else if (ARDOUR::Profile->get_trx()) {
556 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
558 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
559 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
566 if (!_route->is_master()) {
567 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
568 show_sends_button->show();
572 meter_point_button.set_text (meter_point_string (_route->meter_point()));
574 delete route_ops_menu;
577 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
578 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
579 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
580 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
582 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
584 if (_route->panner_shell()) {
585 update_panner_choices();
586 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
589 if (is_audio_track()) {
590 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
593 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
594 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
596 set_stuff_from_route ();
598 /* now force an update of all the various elements */
600 update_mute_display ();
601 update_solo_display ();
604 route_group_changed ();
607 panners.setup_pan ();
609 if (has_audio_outputs ()) {
615 update_diskstream_display ();
616 update_input_display ();
617 update_output_display ();
619 add_events (Gdk::BUTTON_RELEASE_MASK);
621 processor_box.show ();
623 if (!route()->is_master() && !route()->is_monitor()) {
624 /* we don't allow master or control routes to be hidden */
629 gpm.reset_peak_display ();
630 gpm.gain_display.show ();
631 gpm.peak_display.show ();
634 width_hide_box.show();
636 global_vpacker.show();
637 mute_solo_table.show();
638 bottom_button_table.show();
640 meter_point_button.show();
641 input_button_box.show_all();
642 output_button.show();
644 _comment_button.show();
646 gpm.gain_automation_state_button.show();
648 parameter_changed ("mixer-element-visibility");
654 MixerStrip::set_stuff_from_route ()
656 /* if width is not set, it will be set by the MixerUI or editor */
658 string str = gui_property ("strip-width");
660 set_width_enum (Width (string_2_enum (str, _width)), this);
665 MixerStrip::set_width_enum (Width w, void* owner)
667 /* always set the gpm width again, things may be hidden */
670 panners.set_width (w);
672 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
674 _width_owner = owner;
678 if (_width_owner == this) {
679 set_gui_property ("strip-width", enum_2_string (_width));
687 if (show_sends_button) {
688 show_sends_button->set_text (_("Aux"));
691 gpm.gain_automation_style_button.set_text (
692 gpm.astyle_string(gain_automation->automation_style()));
693 gpm.gain_automation_state_button.set_text (
694 gpm.astate_string(gain_automation->automation_state()));
696 if (_route->panner()) {
697 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
698 panners.astyle_string(_route->panner()->automation_style()));
699 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
700 panners.astate_string(_route->panner()->automation_state()));
704 set_size_request (max (110, gpm.get_gm_width()+5), -1);
709 if (show_sends_button) {
710 show_sends_button->set_text (_("Snd"));
713 gpm.gain_automation_style_button.set_text (
714 gpm.short_astyle_string(gain_automation->automation_style()));
715 gpm.gain_automation_state_button.set_text (
716 gpm.short_astate_string(gain_automation->automation_state()));
717 gain_meter().setup_meters (); // recalc meter width
719 if (_route->panner()) {
720 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
721 panners.short_astyle_string(_route->panner()->automation_style()));
722 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
723 panners.short_astate_string(_route->panner()->automation_state()));
726 set_size_request (max (60, gpm.get_gm_width() + 10), -1);
730 processor_box.set_width (w);
732 update_input_display ();
733 update_output_display ();
734 setup_comment_button ();
735 route_group_changed ();
741 MixerStrip::set_packed (bool yn)
746 set_gui_property ("visible", true);
748 set_gui_property ("visible", false);
753 struct RouteCompareByName {
754 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
755 return a->name().compare (b->name()) < 0;
760 MixerStrip::output_release (GdkEventButton *ev)
762 switch (ev->button) {
764 edit_output_configuration ();
772 MixerStrip::output_press (GdkEventButton *ev)
774 using namespace Menu_Helpers;
775 if (!_session->engine().connected()) {
776 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
781 MenuList& citems = output_menu.items();
782 switch (ev->button) {
785 return false; //wait for the mouse-up to pop the dialog
789 output_menu.set_name ("ArdourContextMenu");
791 output_menu_bundles.clear ();
793 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
795 citems.push_back (SeparatorElem());
796 uint32_t const n_with_separator = citems.size ();
798 ARDOUR::BundleList current = _route->output()->bundles_connected ();
800 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
802 /* give user bundles first chance at being in the menu */
804 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
805 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
806 maybe_add_bundle_to_output_menu (*i, current);
810 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
811 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
812 maybe_add_bundle_to_output_menu (*i, current);
816 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
817 RouteList copy = *routes;
818 copy.sort (RouteCompareByName ());
819 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
820 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
823 if (citems.size() == n_with_separator) {
824 /* no routes added; remove the separator */
828 citems.push_back (SeparatorElem());
830 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
833 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
834 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
839 citems.push_back (SeparatorElem());
840 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
842 output_menu.popup (1, ev->time);
853 MixerStrip::input_release (GdkEventButton *ev)
855 switch (ev->button) {
858 edit_input_configuration ();
870 MixerStrip::input_press (GdkEventButton *ev)
872 using namespace Menu_Helpers;
874 MenuList& citems = input_menu.items();
875 input_menu.set_name ("ArdourContextMenu");
878 if (!_session->engine().connected()) {
879 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
884 if (_session->actively_recording() && _route->record_enabled())
887 switch (ev->button) {
890 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
894 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
896 citems.push_back (SeparatorElem());
897 uint32_t const n_with_separator = citems.size ();
899 input_menu_bundles.clear ();
901 ARDOUR::BundleList current = _route->input()->bundles_connected ();
903 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
905 /* give user bundles first chance at being in the menu */
907 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
908 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
909 maybe_add_bundle_to_input_menu (*i, current);
913 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
914 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
915 maybe_add_bundle_to_input_menu (*i, current);
919 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
920 RouteList copy = *routes;
921 copy.sort (RouteCompareByName ());
922 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
923 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
926 if (citems.size() == n_with_separator) {
927 /* no routes added; remove the separator */
931 citems.push_back (SeparatorElem());
932 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
935 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
936 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
941 citems.push_back (SeparatorElem());
942 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
944 input_menu.popup (1, ev->time);
955 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
961 ARDOUR::BundleList current = _route->input()->bundles_connected ();
963 if (std::find (current.begin(), current.end(), c) == current.end()) {
964 _route->input()->connect_ports_to_bundle (c, true, this);
966 _route->input()->disconnect_ports_from_bundle (c, this);
971 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
977 ARDOUR::BundleList current = _route->output()->bundles_connected ();
979 if (std::find (current.begin(), current.end(), c) == current.end()) {
980 _route->output()->connect_ports_to_bundle (c, true, this);
982 _route->output()->disconnect_ports_from_bundle (c, this);
987 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
989 using namespace Menu_Helpers;
991 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
995 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
996 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1000 if (i != input_menu_bundles.end()) {
1004 input_menu_bundles.push_back (b);
1006 MenuList& citems = input_menu.items();
1008 std::string n = b->name ();
1009 replace_all (n, "_", " ");
1011 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1015 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1017 using namespace Menu_Helpers;
1019 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1023 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1024 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1028 if (i != output_menu_bundles.end()) {
1032 output_menu_bundles.push_back (b);
1034 MenuList& citems = output_menu.items();
1036 std::string n = b->name ();
1037 replace_all (n, "_", " ");
1039 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1043 MixerStrip::update_diskstream_display ()
1045 if (is_track() && input_selector) {
1046 input_selector->hide_all ();
1049 route_color_changed ();
1053 MixerStrip::connect_to_pan ()
1055 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1057 panstate_connection.disconnect ();
1058 panstyle_connection.disconnect ();
1060 if (!_route->panner()) {
1064 boost::shared_ptr<Pannable> p = _route->pannable ();
1066 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1067 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1069 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1070 * However, that only works a panner was previously set.
1072 * PannerUI must remain subscribed to _panshell->Changed() in case
1073 * we switch the panner eg. AUX-Send and back
1074 * _route->panner_shell()->Changed() vs _panshell->Changed
1076 if (panners._panner == 0) {
1077 panners.panshell_changed ();
1079 update_panner_choices();
1083 MixerStrip::update_panner_choices ()
1085 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1086 if (!_route->panner_shell()) { return; }
1088 uint32_t in = _route->output()->n_ports().n_audio();
1090 if (_route->panner()) {
1091 in = _route->panner()->in().n_audio();
1094 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1098 * Output port labelling
1099 * =====================
1101 * Case 1: Each output has one connection, all connections are to system:playback_%i
1102 * out 1 -> system:playback_1
1103 * out 2 -> system:playback_2
1104 * out 3 -> system:playback_3
1107 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1108 * out 1 -> ardour:track_x/in 1
1109 * out 2 -> ardour:track_x/in 2
1110 * Display as: track_x
1112 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1113 * out 1 -> program x:foo
1114 * out 2 -> program x:foo
1115 * Display as: program x
1117 * Case 4: No connections (Disconnected)
1120 * Default case (unusual routing):
1121 * Display as: *number of connections*
1125 * .-----------------------------------------------.
1127 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1128 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1129 * '-----------------------------------------------'
1130 * .-----------------------------------------------.
1133 * '-----------------------------------------------'
1137 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1141 boost::shared_ptr<Port> port;
1142 vector<string> port_connections;
1144 uint32_t total_connection_count = 0;
1145 uint32_t io_connection_count = 0;
1146 uint32_t ardour_connection_count = 0;
1147 uint32_t system_connection_count = 0;
1148 uint32_t other_connection_count = 0;
1150 ostringstream label;
1152 bool have_label = false;
1153 bool each_io_has_one_connection = true;
1155 string connection_name;
1156 string ardour_track_name;
1157 string other_connection_type;
1158 string system_ports;
1161 ostringstream tooltip;
1162 char * tooltip_cstr;
1164 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1165 DataType dt = DataType::AUDIO;
1166 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 )
1167 dt = DataType::MIDI;
1170 io_count = route->n_inputs().n_total();
1171 tooltip << string_compose (_("<b>INPUT</b> to %1"), Glib::Markup::escape_text(route->name()));
1173 io_count = route->n_outputs().n_total();
1174 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Glib::Markup::escape_text(route->name()));
1178 for (io_index = 0; io_index < io_count; ++io_index) {
1180 port = route->input()->nth (io_index);
1182 port = route->output()->nth (io_index);
1185 //ignore any port connections that don't match our DataType
1186 if (port->type() != dt)
1189 port_connections.clear ();
1190 port->get_connections(port_connections);
1191 io_connection_count = 0;
1193 if (!port_connections.empty()) {
1194 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1196 string& connection_name (*i);
1198 if (connection_name.find("system:") == 0) {
1199 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1202 if (io_connection_count == 0) {
1203 tooltip << endl << Glib::Markup::escape_text(port->name().substr(port->name().find("/") + 1))
1205 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1208 << Glib::Markup::escape_text( pn.empty() ? connection_name : pn );
1211 if (connection_name.find("ardour:") == 0) {
1212 if (ardour_track_name.empty()) {
1213 // "ardour:Master/in 1" -> "ardour:Master/"
1214 string::size_type slash = connection_name.find("/");
1215 if (slash != string::npos) {
1216 ardour_track_name = connection_name.substr(0, slash + 1);
1220 if (connection_name.find(ardour_track_name) == 0) {
1221 ++ardour_connection_count;
1223 } else if (!pn.empty()) {
1224 if (system_ports.empty()) {
1227 system_ports += "/" + pn;
1229 if (connection_name.find("system:") == 0) {
1230 ++system_connection_count;
1232 } else if (connection_name.find("system:midi_") == 0) {
1234 // "system:midi_capture_123" -> "123"
1235 system_port = "M " + connection_name.substr(20);
1237 // "system:midi_playback_123" -> "123"
1238 system_port = "M " + connection_name.substr(21);
1241 if (system_ports.empty()) {
1242 system_ports += system_port;
1244 system_ports += "/" + system_port;
1247 ++system_connection_count;
1249 } else if (connection_name.find("system:") == 0) {
1251 // "system:capture_123" -> "123"
1252 system_port = connection_name.substr(15);
1254 // "system:playback_123" -> "123"
1255 system_port = connection_name.substr(16);
1258 if (system_ports.empty()) {
1259 system_ports += system_port;
1261 system_ports += "/" + system_port;
1264 ++system_connection_count;
1266 if (other_connection_type.empty()) {
1267 // "jamin:in 1" -> "jamin:"
1268 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1271 if (connection_name.find(other_connection_type) == 0) {
1272 ++other_connection_count;
1276 ++total_connection_count;
1277 ++io_connection_count;
1281 if (io_connection_count != 1) {
1282 each_io_has_one_connection = false;
1286 if (total_connection_count == 0) {
1287 tooltip << endl << _("Disconnected");
1290 tooltip_cstr = new char[tooltip.str().size() + 1];
1291 strcpy(tooltip_cstr, tooltip.str().c_str());
1294 ARDOUR_UI::instance()->set_tip (&input_button, tooltip_cstr, "");
1296 ARDOUR_UI::instance()->set_tip (&output_button, tooltip_cstr, "");
1299 if (each_io_has_one_connection) {
1300 if (total_connection_count == ardour_connection_count) {
1301 // all connections are to the same track in ardour
1302 // "ardour:Master/" -> "Master"
1303 string::size_type slash = ardour_track_name.find("/");
1304 if (slash != string::npos) {
1305 label << ardour_track_name.substr(7, slash - 7);
1309 else if (total_connection_count == system_connection_count) {
1310 // all connections are to system ports
1311 label << system_ports;
1314 else if (total_connection_count == other_connection_count) {
1315 // all connections are to the same external program eg jamin
1316 // "jamin:" -> "jamin"
1317 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1323 if (total_connection_count == 0) {
1327 // Odd configuration
1328 label << "*" << total_connection_count << "*";
1333 input_button.set_text (label.str());
1335 output_button.set_text (label.str());
1340 MixerStrip::update_input_display ()
1342 update_io_button (_route, _width, true);
1343 panners.setup_pan ();
1345 if (has_audio_outputs ()) {
1346 panners.show_all ();
1348 panners.hide_all ();
1354 MixerStrip::update_output_display ()
1356 update_io_button (_route, _width, false);
1357 gpm.setup_meters ();
1358 panners.setup_pan ();
1360 if (has_audio_outputs ()) {
1361 panners.show_all ();
1363 panners.hide_all ();
1368 MixerStrip::fast_update ()
1370 gpm.update_meters ();
1374 MixerStrip::diskstream_changed ()
1376 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1380 MixerStrip::io_changed_proxy ()
1382 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1386 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1388 boost::shared_ptr<Port> a = wa.lock ();
1389 boost::shared_ptr<Port> b = wb.lock ();
1391 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1392 update_input_display ();
1393 set_width_enum (_width, this);
1396 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1397 update_output_display ();
1398 set_width_enum (_width, this);
1403 MixerStrip::setup_comment_button ()
1408 if (_route->comment().empty ()) {
1409 _comment_button.unset_bg (STATE_NORMAL);
1410 _comment_button.set_text (_("Comments"));
1412 _comment_button.modify_bg (STATE_NORMAL, color ());
1413 _comment_button.set_text (_("*Comments*"));
1418 if (_route->comment().empty ()) {
1419 _comment_button.unset_bg (STATE_NORMAL);
1420 _comment_button.set_text (_("Cmt"));
1422 _comment_button.modify_bg (STATE_NORMAL, color ());
1423 _comment_button.set_text (_("*Cmt*"));
1428 ARDOUR_UI::instance()->set_tip (
1429 _comment_button, _route->comment().empty() ? _("Click to Add/Edit Comments") : _route->comment()
1435 MixerStrip::select_route_group (GdkEventButton *ev)
1437 using namespace Menu_Helpers;
1439 if (ev->button == 1) {
1441 if (group_menu == 0) {
1443 PropertyList* plist = new PropertyList();
1445 plist->add (Properties::gain, true);
1446 plist->add (Properties::mute, true);
1447 plist->add (Properties::solo, true);
1449 group_menu = new RouteGroupMenu (_session, plist);
1453 r.push_back (route ());
1454 group_menu->build (r);
1455 group_menu->menu()->popup (1, ev->time);
1462 MixerStrip::route_group_changed ()
1464 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1466 RouteGroup *rg = _route->route_group();
1469 group_button.set_text (PBD::short_version (rg->name(), 5));
1473 group_button.set_text (_("Grp"));
1476 group_button.set_text (_("~G"));
1483 MixerStrip::route_color_changed ()
1485 name_button.modify_bg (STATE_NORMAL, color());
1486 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1487 reset_strip_style ();
1491 MixerStrip::show_passthru_color ()
1493 reset_strip_style ();
1497 MixerStrip::build_route_ops_menu ()
1499 using namespace Menu_Helpers;
1500 route_ops_menu = new Menu;
1501 route_ops_menu->set_name ("ArdourContextMenu");
1503 MenuList& items = route_ops_menu->items();
1505 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1507 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1509 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1511 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1513 items.push_back (SeparatorElem());
1515 if (!_route->is_master()) {
1516 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1518 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1519 rename_menu_item = &items.back();
1521 items.push_back (SeparatorElem());
1522 items.push_back (CheckMenuElem (_("Active")));
1523 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1524 i->set_active (_route->active());
1525 i->set_sensitive(! _session->transport_rolling());
1526 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1528 items.push_back (SeparatorElem());
1530 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1532 items.push_back (SeparatorElem());
1533 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1534 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1535 denormal_menu_item->set_active (_route->denormal_protection());
1537 if (!Profile->get_sae()) {
1538 items.push_back (SeparatorElem());
1539 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1542 items.push_back (SeparatorElem());
1543 /* note that this relies on selection being shared across editor and
1544 mixer (or global to the backend, in the future), which is the only
1545 sane thing for users anyway.
1547 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1551 MixerStrip::name_button_button_press (GdkEventButton* ev)
1553 if (ev->button == 3) {
1554 list_route_operations ();
1556 /* do not allow rename if the track is record-enabled */
1557 rename_menu_item->set_sensitive (!_route->record_enabled());
1558 route_ops_menu->popup (1, ev->time);
1567 MixerStrip::name_button_button_release (GdkEventButton* ev)
1569 if (ev->button == 1) {
1570 list_route_operations ();
1572 /* do not allow rename if the track is record-enabled */
1573 rename_menu_item->set_sensitive (!_route->record_enabled());
1574 route_ops_menu->popup (1, ev->time);
1581 MixerStrip::number_button_button_press (GdkEventButton* ev)
1583 if ( ev->button == 3 ) {
1584 list_route_operations ();
1586 /* do not allow rename if the track is record-enabled */
1587 rename_menu_item->set_sensitive (!_route->record_enabled());
1588 route_ops_menu->popup (1, ev->time);
1597 MixerStrip::list_route_operations ()
1599 delete route_ops_menu;
1600 build_route_ops_menu ();
1604 MixerStrip::set_selected (bool yn)
1606 AxisView::set_selected (yn);
1608 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1609 global_frame.set_name ("MixerStripSelectedFrame");
1611 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1612 global_frame.set_name ("MixerStripFrame");
1614 global_frame.queue_draw ();
1617 // processor_box.deselect_all_processors();
1621 MixerStrip::property_changed (const PropertyChange& what_changed)
1623 RouteUI::property_changed (what_changed);
1625 if (what_changed.contains (ARDOUR::Properties::name)) {
1631 MixerStrip::name_changed ()
1635 name_button.set_text (_route->name());
1638 name_button.set_text (PBD::short_version (_route->name(), 5));
1642 ARDOUR_UI::instance()->set_tip (name_button, _route->name());
1644 if (_session->config.get_track_name_number()) {
1645 const int64_t track_number = _route->track_number ();
1646 if (track_number == 0) {
1647 number_label.set_text ("-");
1649 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1652 number_label.set_text ("");
1657 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1659 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1663 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1665 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1669 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1671 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1675 MixerStrip::width_button_pressed (GdkEventButton* ev)
1677 if (ev->button != 1) {
1681 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1684 _mixer.set_strip_width (Narrow, true);
1688 _mixer.set_strip_width (Wide, true);
1694 set_width_enum (Narrow, this);
1697 set_width_enum (Wide, this);
1706 MixerStrip::hide_clicked ()
1708 // LAME fix to reset the button status for when it is redisplayed (part 1)
1709 hide_button.set_sensitive(false);
1712 Hiding(); /* EMIT_SIGNAL */
1714 _mixer.hide_strip (this);
1718 hide_button.set_sensitive(true);
1722 MixerStrip::set_embedded (bool yn)
1728 MixerStrip::map_frozen ()
1730 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1732 boost::shared_ptr<AudioTrack> at = audio_track();
1735 switch (at->freeze_state()) {
1736 case AudioTrack::Frozen:
1737 processor_box.set_sensitive (false);
1738 hide_redirect_editors ();
1741 processor_box.set_sensitive (true);
1742 // XXX need some way, maybe, to retoggle redirect editors
1749 MixerStrip::hide_redirect_editors ()
1751 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1755 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1757 boost::shared_ptr<Processor> processor (p.lock ());
1762 Gtk::Window* w = processor_box.get_processor_ui (processor);
1770 MixerStrip::reset_strip_style ()
1772 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1774 gpm.set_fader_name ("SendStripBase");
1778 if (is_midi_track()) {
1779 if (_route->active()) {
1780 set_name ("MidiTrackStripBase");
1782 set_name ("MidiTrackStripBaseInactive");
1784 gpm.set_fader_name ("MidiTrackFader");
1785 } else if (is_audio_track()) {
1786 if (_route->active()) {
1787 set_name ("AudioTrackStripBase");
1789 set_name ("AudioTrackStripBaseInactive");
1791 gpm.set_fader_name ("AudioTrackFader");
1793 if (_route->active()) {
1794 set_name ("AudioBusStripBase");
1796 set_name ("AudioBusStripBaseInactive");
1798 gpm.set_fader_name ("AudioBusFader");
1800 /* (no MIDI busses yet) */
1807 MixerStrip::engine_stopped ()
1812 MixerStrip::engine_running ()
1817 MixerStrip::meter_point_string (MeterPoint mp)
1830 case MeterPostFader:
1847 return S_("Meter|In");
1851 return S_("Meter|Pr");
1854 case MeterPostFader:
1855 return S_("Meter|Po");
1859 return S_("Meter|O");
1864 return S_("Meter|C");
1873 /** Called when the metering point has changed */
1875 MixerStrip::meter_changed ()
1877 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1878 gpm.setup_meters ();
1879 // reset peak when meter point changes
1880 gpm.reset_peak_display();
1883 /** The bus that we are displaying sends to has changed, or been turned off.
1884 * @param send_to New bus that we are displaying sends to, or 0.
1887 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1889 RouteUI::bus_send_display_changed (send_to);
1892 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
1897 revert_to_default_display ();
1900 revert_to_default_display ();
1905 MixerStrip::drop_send ()
1907 boost::shared_ptr<Send> current_send;
1909 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
1910 current_send->set_metering (false);
1913 send_gone_connection.disconnect ();
1914 input_button.set_sensitive (true);
1915 output_button.set_sensitive (true);
1916 group_button.set_sensitive (true);
1917 set_invert_sensitive (true);
1918 meter_point_button.set_sensitive (true);
1919 mute_button->set_sensitive (true);
1920 solo_button->set_sensitive (true);
1921 rec_enable_button->set_sensitive (true);
1922 solo_isolated_led->set_sensitive (true);
1923 solo_safe_led->set_sensitive (true);
1924 monitor_input_button->set_sensitive (true);
1925 monitor_disk_button->set_sensitive (true);
1926 _comment_button.set_sensitive (true);
1930 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
1932 _current_delivery = d;
1933 DeliveryChanged (_current_delivery);
1937 MixerStrip::show_send (boost::shared_ptr<Send> send)
1943 set_current_delivery (send);
1945 send->meter()->set_type(_route->shared_peak_meter()->get_type());
1946 send->set_metering (true);
1947 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
1949 gain_meter().set_controls (_route, send->meter(), send->amp());
1950 gain_meter().setup_meters ();
1952 uint32_t const in = _current_delivery->pans_required();
1953 uint32_t const out = _current_delivery->pan_outs();
1955 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
1956 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1957 panner_ui().setup_pan ();
1958 panner_ui().set_send_drawing_mode (true);
1959 panner_ui().show_all ();
1961 input_button.set_sensitive (false);
1962 group_button.set_sensitive (false);
1963 set_invert_sensitive (false);
1964 meter_point_button.set_sensitive (false);
1965 mute_button->set_sensitive (false);
1966 solo_button->set_sensitive (false);
1967 rec_enable_button->set_sensitive (false);
1968 solo_isolated_led->set_sensitive (false);
1969 solo_safe_led->set_sensitive (false);
1970 monitor_input_button->set_sensitive (false);
1971 monitor_disk_button->set_sensitive (false);
1972 _comment_button.set_sensitive (false);
1974 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
1975 output_button.set_sensitive (false);
1978 reset_strip_style ();
1982 MixerStrip::revert_to_default_display ()
1986 set_current_delivery (_route->main_outs ());
1988 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp());
1989 gain_meter().setup_meters ();
1991 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
1992 update_panner_choices();
1993 panner_ui().setup_pan ();
1994 panner_ui().set_send_drawing_mode (false);
1996 if (has_audio_outputs ()) {
1997 panners.show_all ();
1999 panners.hide_all ();
2002 reset_strip_style ();
2006 MixerStrip::set_button_names ()
2010 mute_button->set_text (_("Mute"));
2011 monitor_input_button->set_text (_("In"));
2012 monitor_disk_button->set_text (_("Disk"));
2014 if (_route && _route->solo_safe()) {
2015 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2017 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2019 if (!Config->get_solo_control_is_listen_control()) {
2020 solo_button->set_text (_("Solo"));
2022 switch (Config->get_listen_position()) {
2023 case AfterFaderListen:
2024 solo_button->set_text (_("AFL"));
2026 case PreFaderListen:
2027 solo_button->set_text (_("PFL"));
2031 solo_isolated_led->set_text (_("Iso"));
2032 solo_safe_led->set_text (S_("SoloLock|Lock"));
2036 mute_button->set_text (S_("Mute|M"));
2037 monitor_input_button->set_text (S_("MonitorInput|I"));
2038 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2040 if (_route && _route->solo_safe()) {
2041 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2043 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2045 if (!Config->get_solo_control_is_listen_control()) {
2046 solo_button->set_text (S_("Solo|S"));
2048 switch (Config->get_listen_position()) {
2049 case AfterFaderListen:
2050 solo_button->set_text (S_("AfterFader|A"));
2052 case PreFaderListen:
2053 solo_button->set_text (S_("Prefader|P"));
2058 solo_isolated_led->set_text (S_("SoloIso|I"));
2059 solo_safe_led->set_text (S_("SoloLock|L"));
2064 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2066 meter_point_button.set_text ("");
2071 MixerStrip::plugin_selector()
2073 return _mixer.plugin_selector();
2077 MixerStrip::hide_things ()
2079 processor_box.hide_things ();
2083 MixerStrip::input_active_button_press (GdkEventButton*)
2085 /* nothing happens on press */
2090 MixerStrip::input_active_button_release (GdkEventButton* ev)
2092 boost::shared_ptr<MidiTrack> mt = midi_track ();
2098 boost::shared_ptr<RouteList> rl (new RouteList);
2100 rl->push_back (route());
2102 _session->set_exclusive_input_active (rl, !mt->input_active(),
2103 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2109 MixerStrip::midi_input_status_changed ()
2111 if (midi_input_enable_button) {
2112 boost::shared_ptr<MidiTrack> mt = midi_track ();
2114 midi_input_enable_button->set_active (mt->input_active ());
2119 MixerStrip::state_id () const
2121 return string_compose ("strip %1", _route->id().to_s());
2125 MixerStrip::parameter_changed (string p)
2127 if (p == _visibility.get_state_name()) {
2128 /* The user has made changes to the mixer strip visibility, so get
2129 our VisibilityGroup to reflect these changes in our widgets.
2131 _visibility.set_state (ARDOUR_UI::config()->get_mixer_strip_visibility ());
2133 else if (p == "track-name-number") {
2138 /** Called to decide whether the solo isolate / solo lock button visibility should
2139 * be overridden from that configured by the user. We do this for the master bus.
2141 * @return optional value that is present if visibility state should be overridden.
2143 boost::optional<bool>
2144 MixerStrip::override_solo_visibility () const
2146 if (_route && _route->is_master ()) {
2147 return boost::optional<bool> (false);
2150 return boost::optional<bool> ();
2154 MixerStrip::add_input_port (DataType t)
2156 _route->input()->add_port ("", this, t);
2160 MixerStrip::add_output_port (DataType t)
2162 _route->output()->add_port ("", this, t);
2166 MixerStrip::route_active_changed ()
2168 reset_strip_style ();
2172 MixerStrip::copy_processors ()
2174 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2178 MixerStrip::cut_processors ()
2180 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2184 MixerStrip::paste_processors ()
2186 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2190 MixerStrip::select_all_processors ()
2192 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2196 MixerStrip::deselect_all_processors ()
2198 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2202 MixerStrip::delete_processors ()
2204 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2208 MixerStrip::toggle_processors ()
2210 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2214 MixerStrip::ab_plugins ()
2216 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2220 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2222 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2225 if (ev->button == 3) {
2226 popup_level_meter_menu (ev);
2234 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2236 using namespace Gtk::Menu_Helpers;
2238 Gtk::Menu* m = manage (new Menu);
2239 MenuList& items = m->items ();
2241 RadioMenuItem::Group group;
2243 _suspend_menu_callbacks = true;
2244 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2245 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2246 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2247 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2248 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2250 if (gpm.meter_channels().n_audio() == 0) {
2251 m->popup (ev->button, ev->time);
2252 _suspend_menu_callbacks = false;
2256 RadioMenuItem::Group tgroup;
2257 items.push_back (SeparatorElem());
2259 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2260 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2261 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2262 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2263 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2264 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2265 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2266 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2267 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2268 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2271 if (_route->is_master()) {
2274 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2275 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2276 /* non-master bus */
2279 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2286 MeterType cmt = _route->meter_type();
2287 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2289 items.push_back (SeparatorElem());
2290 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2291 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2292 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2293 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2294 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2295 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2297 m->popup (ev->button, ev->time);
2298 _suspend_menu_callbacks = false;
2302 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2303 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2305 using namespace Menu_Helpers;
2307 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2308 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2309 i->set_active (_route->meter_point() == point);
2313 MixerStrip::set_meter_point (MeterPoint p)
2315 if (_suspend_menu_callbacks) return;
2316 _route->set_meter_point (p);
2320 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2321 RadioMenuItem::Group& group, string const & name, MeterType type)
2323 using namespace Menu_Helpers;
2325 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2326 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2327 i->set_active (_route->meter_type() == type);
2331 MixerStrip::set_meter_type (MeterType t)
2333 if (_suspend_menu_callbacks) return;