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/amp.h"
38 #include "ardour/audio_track.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/internal_send.h"
41 #include "ardour/meter.h"
42 #include "ardour/midi_track.h"
43 #include "ardour/pannable.h"
44 #include "ardour/panner.h"
45 #include "ardour/panner_shell.h"
46 #include "ardour/panner_manager.h"
47 #include "ardour/port.h"
48 #include "ardour/profile.h"
49 #include "ardour/route.h"
50 #include "ardour/route_group.h"
51 #include "ardour/send.h"
52 #include "ardour/session.h"
53 #include "ardour/types.h"
54 #include "ardour/user_bundle.h"
55 #include "ardour/vca.h"
56 #include "ardour/vca_manager.h"
58 #include "ardour_window.h"
59 #include "mixer_strip.h"
62 #include "ardour_button.h"
63 #include "public_editor.h"
65 #include "io_selector.h"
67 #include "gui_thread.h"
68 #include "route_group_menu.h"
69 #include "meter_patterns.h"
71 #include "ui_config.h"
75 using namespace ARDOUR;
76 using namespace ARDOUR_UI_UTILS;
79 using namespace Gtkmm2ext;
81 using namespace ArdourMeter;
83 MixerStrip* MixerStrip::_entered_mixer_strip;
84 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
86 static const uint32_t n_vca_buttons = 4;
88 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
92 , _mixer_owned (in_mixer)
93 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
96 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
97 , rec_mon_table (2, 2)
98 , solo_iso_table (1, 2)
99 , mute_solo_table (1, 2)
100 , bottom_button_table (1, 3)
101 , meter_point_button (_("pre"))
102 , monitor_section_button (0)
103 , midi_input_enable_button (0)
104 , _comment_button (_("Comments"))
105 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
106 , _visibility (X_("mixer-element-visibility"))
111 /* the editor mixer strip: don't destroy it every time
112 the underlying route goes away.
115 self_destruct = false;
119 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
123 , _mixer_owned (in_mixer)
124 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
127 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
128 , rec_mon_table (2, 2)
129 , solo_iso_table (1, 2)
130 , mute_solo_table (1, 2)
131 , bottom_button_table (1, 3)
132 , meter_point_button (_("pre"))
133 , monitor_section_button (0)
134 , midi_input_enable_button (0)
135 , _comment_button (_("Comments"))
136 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
137 , _visibility (X_("mixer-element-visibility"))
146 _entered_mixer_strip= 0;
149 ignore_comment_edit = false;
150 ignore_toggle = false;
155 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
156 longest_label = "longest label";
158 string t = _("Click to toggle the width of this mixer strip.");
160 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
163 width_button.set_icon (ArdourIcon::StripWidth);
164 set_tooltip (width_button, t);
166 hide_button.set_icon (ArdourIcon::CloseCross);
167 set_tooltip (&hide_button, _("Hide this mixer strip"));
169 input_button_box.set_spacing(2);
171 input_button.set_text (_("Input"));
172 input_button.set_name ("mixer strip button");
173 input_button_box.pack_start (input_button, true, true);
175 output_button.set_text (_("Output"));
176 output_button.set_name ("mixer strip button");
178 set_tooltip (&meter_point_button, _("Click to select metering point"));
179 meter_point_button.set_name ("mixer strip button");
181 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
183 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
184 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
186 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
188 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
189 solo_isolated_led->show ();
190 solo_isolated_led->set_no_show_all (true);
191 solo_isolated_led->set_name (X_("solo isolate"));
192 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
193 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
194 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
196 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
197 solo_safe_led->show ();
198 solo_safe_led->set_no_show_all (true);
199 solo_safe_led->set_name (X_("solo safe"));
200 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
201 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
202 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
204 solo_safe_led->set_text (S_("SoloLock|Lock"));
205 solo_isolated_led->set_text (_("Iso"));
207 solo_iso_table.set_homogeneous (true);
208 solo_iso_table.set_spacings (2);
209 if (!ARDOUR::Profile->get_trx()) {
210 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
211 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
213 solo_iso_table.show ();
215 vca_button = manage (new ArdourButton (ArdourButton::default_elements));
216 vca_button->set_no_show_all (true);
217 vca_button->set_name (X_("vca assign"));
218 vca_button->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
219 vca_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::vca_button_release), false);
220 UI::instance()->set_tip (*vca_button, _("VCA assignments"));
221 vca_button->set_text (_("-vca-"));
224 rec_mon_table.set_homogeneous (true);
225 rec_mon_table.set_row_spacings (2);
226 rec_mon_table.set_col_spacings (2);
227 if (ARDOUR::Profile->get_mixbus()) {
228 rec_mon_table.resize (1, 3);
229 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
230 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
231 } else if (!ARDOUR::Profile->get_trx()) {
232 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
233 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
235 rec_mon_table.show ();
237 if (solo_isolated_led) {
238 button_size_group->add_widget (*solo_isolated_led);
241 button_size_group->add_widget (*solo_safe_led);
244 if (!ARDOUR::Profile->get_mixbus()) {
245 if (rec_enable_button) {
246 button_size_group->add_widget (*rec_enable_button);
248 if (monitor_disk_button) {
249 button_size_group->add_widget (*monitor_disk_button);
251 if (monitor_input_button) {
252 button_size_group->add_widget (*monitor_input_button);
256 mute_solo_table.set_homogeneous (true);
257 mute_solo_table.set_spacings (2);
259 bottom_button_table.set_spacings (2);
260 bottom_button_table.set_homogeneous (true);
261 bottom_button_table.attach (group_button, 1, 2, 0, 1);
262 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
264 name_button.set_name ("mixer strip button");
265 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
266 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
268 set_tooltip (&group_button, _("Mix group"));
269 group_button.set_name ("mixer strip button");
271 _comment_button.set_name (X_("mixer strip button"));
272 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
274 // TODO implement ArdourKnob::on_size_request properly
275 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
276 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
278 trim_control.set_tooltip_prefix (_("Trim: "));
279 trim_control.set_name ("trim knob");
280 trim_control.set_no_show_all (true);
281 input_button_box.pack_start (trim_control, false, false);
283 global_vpacker.set_border_width (1);
284 global_vpacker.set_spacing (0);
286 width_button.set_name ("mixer strip button");
287 hide_button.set_name ("mixer strip button");
289 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
290 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
292 // width_hide_box.set_border_width (1);
293 width_hide_box.set_spacing (2);
294 width_hide_box.pack_start (width_button, false, true);
295 width_hide_box.pack_start (number_label, true, true);
296 width_hide_box.pack_end (hide_button, false, true);
298 number_label.set_text ("-");
299 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
300 number_label.set_no_show_all ();
301 number_label.set_name ("tracknumber label");
302 number_label.set_fixed_colors (0x80808080, 0x80808080);
303 number_label.set_alignment (.5, .5);
304 number_label.set_fallthrough_to_parent (true);
306 global_vpacker.set_spacing (2);
307 if (!ARDOUR::Profile->get_trx()) {
308 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
311 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (processor_box, true, true);
314 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
315 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
316 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
317 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
318 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
319 global_vpacker.pack_start (*vca_button, Gtk::PACK_SHRINK);
320 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
321 if (!ARDOUR::Profile->get_trx()) {
322 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
323 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
325 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
328 global_frame.add (global_vpacker);
329 global_frame.set_shadow_type (Gtk::SHADOW_IN);
330 global_frame.set_name ("BaseFrame");
334 /* force setting of visible selected status */
337 set_selected (false);
342 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
343 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
345 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
346 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
347 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
349 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
350 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
352 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
353 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
354 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
356 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
358 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
359 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
361 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
365 /* start off as a passthru strip. we'll correct this, if necessary,
366 in update_diskstream_display().
369 /* start off as a passthru strip. we'll correct this, if necessary,
370 in update_diskstream_display().
373 if (is_midi_track()) {
374 set_name ("MidiTrackStripBase");
376 set_name ("AudioTrackStripBase");
379 add_events (Gdk::BUTTON_RELEASE_MASK|
380 Gdk::ENTER_NOTIFY_MASK|
381 Gdk::LEAVE_NOTIFY_MASK|
383 Gdk::KEY_RELEASE_MASK);
385 set_flags (get_flags() | Gtk::CAN_FOCUS);
387 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
388 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
391 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
392 must be the same as those used in RCOptionEditor so that the configuration changes
393 are recognised when they occur.
395 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
396 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
397 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
398 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
399 _visibility.add (&output_button, X_("Output"), _("Output"), false);
400 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
401 _visibility.add (vca_button, X_("VCA"), _("VCA Assigns"), false);
403 parameter_changed (X_("mixer-element-visibility"));
404 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
405 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
406 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
408 //watch for mouse enter/exit so we can do some stuff
409 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
410 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
412 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
415 MixerStrip::~MixerStrip ()
417 CatchDeletion (this);
419 if (this ==_entered_mixer_strip)
420 _entered_mixer_strip = NULL;
424 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
426 _entered_mixer_strip = this;
428 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
429 //because the mixerstrip control is a parent that encompasses the strip
430 deselect_all_processors();
436 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
438 //if we have moved outside our strip, but not into a child view, then deselect ourselves
439 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
440 _entered_mixer_strip= 0;
442 //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
443 gpm.gain_display.set_sensitive(false);
445 gpm.gain_display.set_sensitive(true);
447 //if we leave this mixer strip we need to clear out any selections
448 //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
455 MixerStrip::set_route (boost::shared_ptr<Route> rt)
457 //the rec/monitor stuff only shows up for tracks.
458 //the show_sends only shows up for buses.
459 //remove them all here, and we may add them back later
460 if (show_sends_button->get_parent()) {
461 rec_mon_table.remove (*show_sends_button);
463 if (rec_enable_button->get_parent()) {
464 rec_mon_table.remove (*rec_enable_button);
466 if (monitor_input_button->get_parent()) {
467 rec_mon_table.remove (*monitor_input_button);
469 if (monitor_disk_button->get_parent()) {
470 rec_mon_table.remove (*monitor_disk_button);
472 if (group_button.get_parent()) {
473 bottom_button_table.remove (group_button);
476 RouteUI::set_route (rt);
478 /* ProcessorBox needs access to _route so that it can read
481 processor_box.set_route (rt);
483 revert_to_default_display ();
485 /* unpack these from the parent and stuff them into our own
489 if (gpm.peak_display.get_parent()) {
490 gpm.peak_display.get_parent()->remove (gpm.peak_display);
492 if (gpm.gain_display.get_parent()) {
493 gpm.gain_display.get_parent()->remove (gpm.gain_display);
496 gpm.set_type (rt->meter_type());
498 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
499 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
501 if (solo_button->get_parent()) {
502 mute_solo_table.remove (*solo_button);
505 if (mute_button->get_parent()) {
506 mute_solo_table.remove (*mute_button);
509 if (route()->is_master()) {
510 solo_button->hide ();
511 mute_button->show ();
512 rec_mon_table.hide ();
513 if (solo_iso_table.get_parent()) {
514 solo_iso_table.get_parent()->remove(solo_iso_table);
516 if (monitor_section_button == 0) {
517 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
518 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
520 monitor_section_button = manage (new ArdourButton);
522 monitor_section_button->set_related_action (act);
523 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
524 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
525 monitor_section_button->show();
526 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
528 parameter_changed ("use-monitor-bus");
530 bottom_button_table.attach (group_button, 1, 2, 0, 1);
531 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
532 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
533 mute_button->show ();
534 solo_button->show ();
535 rec_mon_table.show ();
538 if (_mixer_owned && route()->is_master() ) {
540 HScrollbar scrollbar;
541 Gtk::Requisition requisition(scrollbar.size_request ());
542 int scrollbar_height = requisition.height;
544 spacer = manage (new EventBox);
545 spacer->set_size_request (-1, scrollbar_height+2);
546 global_vpacker.pack_start (*spacer, false, false);
551 monitor_input_button->show ();
552 monitor_disk_button->show ();
554 monitor_input_button->hide();
555 monitor_disk_button->hide ();
558 if (route()->trim() && route()->trim()->active()) {
559 trim_control.show ();
560 trim_control.set_controllable (route()->trim()->gain_control());
562 trim_control.hide ();
563 boost::shared_ptr<Controllable> none;
564 trim_control.set_controllable (none);
567 if (is_midi_track()) {
568 if (midi_input_enable_button == 0) {
569 midi_input_enable_button = manage (new ArdourButton);
570 midi_input_enable_button->set_name ("midi input button");
571 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
572 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
573 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
574 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
575 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
577 input_button_box.remove (*midi_input_enable_button);
579 /* get current state */
580 midi_input_status_changed ();
581 input_button_box.pack_start (*midi_input_enable_button, false, false);
583 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
585 if (midi_input_enable_button) {
586 /* removal from the container will delete it */
587 input_button_box.remove (*midi_input_enable_button);
588 midi_input_enable_button = 0;
592 if (is_audio_track()) {
593 boost::shared_ptr<AudioTrack> at = audio_track();
594 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
599 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
600 rec_enable_button->show();
602 if (ARDOUR::Profile->get_mixbus()) {
603 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
604 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
605 } else if (ARDOUR::Profile->get_trx()) {
606 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
608 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
609 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
616 if (!_route->is_master()) {
617 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
618 show_sends_button->show();
622 meter_point_button.set_text (meter_point_string (_route->meter_point()));
624 delete route_ops_menu;
627 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
628 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
629 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
630 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
632 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
634 if (_route->panner_shell()) {
635 update_panner_choices();
636 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
639 if (is_audio_track()) {
640 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
643 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
644 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
646 set_stuff_from_route ();
648 /* now force an update of all the various elements */
650 update_mute_display ();
651 update_solo_display ();
654 route_group_changed ();
657 panners.setup_pan ();
659 if (has_audio_outputs ()) {
665 update_diskstream_display ();
666 update_input_display ();
667 update_output_display ();
669 add_events (Gdk::BUTTON_RELEASE_MASK);
671 processor_box.show ();
673 if (!route()->is_master() && !route()->is_monitor()) {
674 /* we don't allow master or control routes to be hidden */
679 gpm.reset_peak_display ();
680 gpm.gain_display.show ();
681 gpm.peak_display.show ();
684 width_hide_box.show();
686 global_vpacker.show();
687 mute_solo_table.show();
688 bottom_button_table.show();
690 meter_point_button.show();
691 input_button_box.show_all();
692 output_button.show();
694 _comment_button.show();
696 gpm.gain_automation_state_button.show();
698 parameter_changed ("mixer-element-visibility");
705 MixerStrip::set_stuff_from_route ()
707 /* if width is not set, it will be set by the MixerUI or editor */
709 string str = gui_property ("strip-width");
711 set_width_enum (Width (string_2_enum (str, _width)), this);
716 MixerStrip::set_width_enum (Width w, void* owner)
718 /* always set the gpm width again, things may be hidden */
721 panners.set_width (w);
723 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
725 _width_owner = owner;
729 if (_width_owner == this) {
730 set_gui_property ("strip-width", enum_2_string (_width));
735 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
740 if (show_sends_button) {
741 show_sends_button->set_text (_("Aux"));
744 gpm.gain_automation_style_button.set_text (
745 gpm.astyle_string(gain_automation->automation_style()));
746 gpm.gain_automation_state_button.set_text (
747 gpm.astate_string(gain_automation->automation_state()));
749 if (_route->panner()) {
750 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
751 panners.astyle_string(_route->panner()->automation_style()));
752 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
753 panners.astate_string(_route->panner()->automation_state()));
757 // panners expect an even number of horiz. pixels
758 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
760 set_size_request (width, -1);
766 if (show_sends_button) {
767 show_sends_button->set_text (_("Snd"));
770 gpm.gain_automation_style_button.set_text (
771 gpm.short_astyle_string(gain_automation->automation_style()));
772 gpm.gain_automation_state_button.set_text (
773 gpm.short_astate_string(gain_automation->automation_state()));
774 gain_meter().setup_meters (); // recalc meter width
776 if (_route->panner()) {
777 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
778 panners.short_astyle_string(_route->panner()->automation_style()));
779 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
780 panners.short_astate_string(_route->panner()->automation_state()));
784 // panners expect an even number of horiz. pixels
785 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
787 set_size_request (width, -1);
792 processor_box.set_width (w);
794 update_input_display ();
795 update_output_display ();
796 setup_comment_button ();
797 route_group_changed ();
803 MixerStrip::set_packed (bool yn)
808 set_gui_property ("visible", true);
810 set_gui_property ("visible", false);
815 struct RouteCompareByName {
816 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
817 return a->name().compare (b->name()) < 0;
822 MixerStrip::output_release (GdkEventButton *ev)
824 switch (ev->button) {
826 edit_output_configuration ();
834 MixerStrip::output_press (GdkEventButton *ev)
836 using namespace Menu_Helpers;
837 if (!_session->engine().connected()) {
838 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
843 MenuList& citems = output_menu.items();
844 switch (ev->button) {
847 return false; //wait for the mouse-up to pop the dialog
851 output_menu.set_name ("ArdourContextMenu");
853 output_menu_bundles.clear ();
855 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
857 citems.push_back (SeparatorElem());
858 uint32_t const n_with_separator = citems.size ();
860 ARDOUR::BundleList current = _route->output()->bundles_connected ();
862 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
864 /* give user bundles first chance at being in the menu */
866 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
867 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
868 maybe_add_bundle_to_output_menu (*i, current);
872 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
873 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
874 maybe_add_bundle_to_output_menu (*i, current);
878 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
879 RouteList copy = *routes;
880 copy.sort (RouteCompareByName ());
881 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
882 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
885 if (citems.size() == n_with_separator) {
886 /* no routes added; remove the separator */
890 if (!ARDOUR::Profile->get_mixbus()) {
891 citems.push_back (SeparatorElem());
893 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
896 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
897 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
903 citems.push_back (SeparatorElem());
904 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
906 output_menu.popup (1, ev->time);
917 MixerStrip::input_release (GdkEventButton *ev)
919 switch (ev->button) {
922 edit_input_configuration ();
934 MixerStrip::input_press (GdkEventButton *ev)
936 using namespace Menu_Helpers;
938 MenuList& citems = input_menu.items();
939 input_menu.set_name ("ArdourContextMenu");
942 if (!_session->engine().connected()) {
943 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
948 if (_session->actively_recording() && _route->record_enabled())
951 switch (ev->button) {
954 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
958 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
960 citems.push_back (SeparatorElem());
961 uint32_t const n_with_separator = citems.size ();
963 input_menu_bundles.clear ();
965 ARDOUR::BundleList current = _route->input()->bundles_connected ();
967 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
969 /* give user bundles first chance at being in the menu */
971 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
972 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
973 maybe_add_bundle_to_input_menu (*i, current);
977 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
978 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
979 maybe_add_bundle_to_input_menu (*i, current);
983 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
984 RouteList copy = *routes;
985 copy.sort (RouteCompareByName ());
986 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
987 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
990 if (citems.size() == n_with_separator) {
991 /* no routes added; remove the separator */
995 citems.push_back (SeparatorElem());
996 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
999 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1000 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1005 citems.push_back (SeparatorElem());
1006 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1008 input_menu.popup (1, ev->time);
1019 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1021 if (ignore_toggle) {
1025 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1027 if (std::find (current.begin(), current.end(), c) == current.end()) {
1028 _route->input()->connect_ports_to_bundle (c, true, this);
1030 _route->input()->disconnect_ports_from_bundle (c, this);
1035 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1037 if (ignore_toggle) {
1041 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1043 if (std::find (current.begin(), current.end(), c) == current.end()) {
1044 _route->output()->connect_ports_to_bundle (c, true, this);
1046 _route->output()->disconnect_ports_from_bundle (c, this);
1051 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1053 using namespace Menu_Helpers;
1055 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1059 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1060 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1064 if (i != input_menu_bundles.end()) {
1068 input_menu_bundles.push_back (b);
1070 MenuList& citems = input_menu.items();
1072 std::string n = b->name ();
1073 replace_all (n, "_", " ");
1075 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1079 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1081 using namespace Menu_Helpers;
1083 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1087 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1088 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1092 if (i != output_menu_bundles.end()) {
1096 output_menu_bundles.push_back (b);
1098 MenuList& citems = output_menu.items();
1100 std::string n = b->name ();
1101 replace_all (n, "_", " ");
1103 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1107 MixerStrip::update_diskstream_display ()
1109 if (is_track() && input_selector) {
1110 input_selector->hide_all ();
1113 route_color_changed ();
1117 MixerStrip::connect_to_pan ()
1119 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1121 panstate_connection.disconnect ();
1122 panstyle_connection.disconnect ();
1124 if (!_route->panner()) {
1128 boost::shared_ptr<Pannable> p = _route->pannable ();
1130 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1131 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1133 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1134 * However, that only works a panner was previously set.
1136 * PannerUI must remain subscribed to _panshell->Changed() in case
1137 * we switch the panner eg. AUX-Send and back
1138 * _route->panner_shell()->Changed() vs _panshell->Changed
1140 if (panners._panner == 0) {
1141 panners.panshell_changed ();
1143 update_panner_choices();
1147 MixerStrip::update_panner_choices ()
1149 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1150 if (!_route->panner_shell()) { return; }
1152 uint32_t in = _route->output()->n_ports().n_audio();
1154 if (_route->panner()) {
1155 in = _route->panner()->in().n_audio();
1158 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1162 * Output port labelling
1163 * =====================
1165 * Case 1: Each output has one connection, all connections are to system:playback_%i
1166 * out 1 -> system:playback_1
1167 * out 2 -> system:playback_2
1168 * out 3 -> system:playback_3
1171 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1172 * out 1 -> ardour:track_x/in 1
1173 * out 2 -> ardour:track_x/in 2
1174 * Display as: track_x
1176 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1177 * out 1 -> program x:foo
1178 * out 2 -> program x:foo
1179 * Display as: program x
1181 * Case 4: No connections (Disconnected)
1184 * Default case (unusual routing):
1185 * Display as: *number of connections*
1189 * .-----------------------------------------------.
1191 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1192 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1193 * '-----------------------------------------------'
1194 * .-----------------------------------------------.
1197 * '-----------------------------------------------'
1201 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1205 boost::shared_ptr<Port> port;
1206 vector<string> port_connections;
1208 uint32_t total_connection_count = 0;
1209 uint32_t io_connection_count = 0;
1210 uint32_t ardour_connection_count = 0;
1211 uint32_t system_connection_count = 0;
1212 uint32_t other_connection_count = 0;
1213 uint32_t typed_connection_count = 0;
1215 ostringstream label;
1217 bool have_label = false;
1218 bool each_io_has_one_connection = true;
1220 string connection_name;
1221 string ardour_track_name;
1222 string other_connection_type;
1223 string system_ports;
1226 ostringstream tooltip;
1227 char * tooltip_cstr;
1229 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1230 DataType dt = DataType::AUDIO;
1231 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1232 dt = DataType::MIDI;
1233 // avoid further confusion with Midi-tracks that have a synth.
1234 // Audio-ports may be connected, but button says "Disconnected"
1235 tooltip << _("MIDI ");
1239 io_count = route->n_inputs().n_total();
1240 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1242 io_count = route->n_outputs().n_total();
1243 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1247 for (io_index = 0; io_index < io_count; ++io_index) {
1249 port = route->input()->nth (io_index);
1251 port = route->output()->nth (io_index);
1254 port_connections.clear ();
1255 port->get_connections(port_connections);
1257 //ignore any port connections that don't match our DataType
1258 if (port->type() != dt) {
1259 if (!port_connections.empty()) {
1260 ++typed_connection_count;
1265 io_connection_count = 0;
1267 if (!port_connections.empty()) {
1268 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1270 string& connection_name (*i);
1272 if (connection_name.find("system:") == 0) {
1273 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1276 if (io_connection_count == 0) {
1277 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1279 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1282 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1285 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1286 if (ardour_track_name.empty()) {
1287 // "ardour:Master/in 1" -> "ardour:Master/"
1288 string::size_type slash = connection_name.find("/");
1289 if (slash != string::npos) {
1290 ardour_track_name = connection_name.substr(0, slash + 1);
1294 if (connection_name.find(ardour_track_name) == 0) {
1295 ++ardour_connection_count;
1297 } else if (!pn.empty()) {
1298 if (system_ports.empty()) {
1301 system_ports += "/" + pn;
1303 if (connection_name.find("system:") == 0) {
1304 ++system_connection_count;
1306 } else if (connection_name.find("system:midi_") == 0) {
1308 // "system:midi_capture_123" -> "123"
1309 system_port = "M " + connection_name.substr(20);
1311 // "system:midi_playback_123" -> "123"
1312 system_port = "M " + connection_name.substr(21);
1315 if (system_ports.empty()) {
1316 system_ports += system_port;
1318 system_ports += "/" + system_port;
1321 ++system_connection_count;
1323 } else if (connection_name.find("system:") == 0) {
1325 // "system:capture_123" -> "123"
1326 system_port = connection_name.substr(15);
1328 // "system:playback_123" -> "123"
1329 system_port = connection_name.substr(16);
1332 if (system_ports.empty()) {
1333 system_ports += system_port;
1335 system_ports += "/" + system_port;
1338 ++system_connection_count;
1340 if (other_connection_type.empty()) {
1341 // "jamin:in 1" -> "jamin:"
1342 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1345 if (connection_name.find(other_connection_type) == 0) {
1346 ++other_connection_count;
1350 ++total_connection_count;
1351 ++io_connection_count;
1355 if (io_connection_count != 1) {
1356 each_io_has_one_connection = false;
1360 if (total_connection_count == 0) {
1361 tooltip << endl << _("Disconnected");
1364 tooltip_cstr = new char[tooltip.str().size() + 1];
1365 strcpy(tooltip_cstr, tooltip.str().c_str());
1368 set_tooltip (&input_button, tooltip_cstr);
1370 set_tooltip (&output_button, tooltip_cstr);
1373 delete [] tooltip_cstr;
1375 if (each_io_has_one_connection) {
1376 if (total_connection_count == ardour_connection_count) {
1377 // all connections are to the same track in ardour
1378 // "ardour:Master/" -> "Master"
1379 string::size_type slash = ardour_track_name.find("/");
1380 if (slash != string::npos) {
1381 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1382 label << ardour_track_name.substr (ppps, slash - ppps);
1386 else if (total_connection_count == system_connection_count) {
1387 // all connections are to system ports
1388 label << system_ports;
1391 else if (total_connection_count == other_connection_count) {
1392 // all connections are to the same external program eg jamin
1393 // "jamin:" -> "jamin"
1394 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1400 if (total_connection_count == 0) {
1404 // Odd configuration
1405 label << "*" << total_connection_count << "*";
1407 if (typed_connection_count > 0) {
1408 label << "\u2295"; // circled plus
1413 input_button.set_text (label.str());
1415 output_button.set_text (label.str());
1420 MixerStrip::update_input_display ()
1422 update_io_button (_route, _width, true);
1423 panners.setup_pan ();
1425 if (has_audio_outputs ()) {
1426 panners.show_all ();
1428 panners.hide_all ();
1434 MixerStrip::update_output_display ()
1436 update_io_button (_route, _width, false);
1437 gpm.setup_meters ();
1438 panners.setup_pan ();
1440 if (has_audio_outputs ()) {
1441 panners.show_all ();
1443 panners.hide_all ();
1448 MixerStrip::fast_update ()
1450 gpm.update_meters ();
1454 MixerStrip::diskstream_changed ()
1456 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1460 MixerStrip::io_changed_proxy ()
1462 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1466 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1468 boost::shared_ptr<Port> a = wa.lock ();
1469 boost::shared_ptr<Port> b = wb.lock ();
1471 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1472 update_input_display ();
1473 set_width_enum (_width, this);
1476 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1477 update_output_display ();
1478 set_width_enum (_width, this);
1483 MixerStrip::setup_comment_button ()
1488 if (_route->comment().empty ()) {
1489 _comment_button.unset_bg (STATE_NORMAL);
1490 _comment_button.set_text (_("Comments"));
1492 _comment_button.modify_bg (STATE_NORMAL, color ());
1493 _comment_button.set_text (_("*Comments*"));
1498 if (_route->comment().empty ()) {
1499 _comment_button.unset_bg (STATE_NORMAL);
1500 _comment_button.set_text (_("Cmt"));
1502 _comment_button.modify_bg (STATE_NORMAL, color ());
1503 _comment_button.set_text (_("*Cmt*"));
1509 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1515 MixerStrip::select_route_group (GdkEventButton *ev)
1517 using namespace Menu_Helpers;
1519 if (ev->button == 1) {
1521 if (group_menu == 0) {
1523 PropertyList* plist = new PropertyList();
1525 plist->add (Properties::gain, true);
1526 plist->add (Properties::mute, true);
1527 plist->add (Properties::solo, true);
1529 group_menu = new RouteGroupMenu (_session, plist);
1533 r.push_back (route ());
1534 group_menu->build (r);
1535 group_menu->menu()->popup (1, ev->time);
1542 MixerStrip::route_group_changed ()
1544 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1546 RouteGroup *rg = _route->route_group();
1549 group_button.set_text (PBD::short_version (rg->name(), 5));
1553 group_button.set_text (_("Grp"));
1556 group_button.set_text (_("~G"));
1563 MixerStrip::route_color_changed ()
1565 name_button.modify_bg (STATE_NORMAL, color());
1566 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1567 reset_strip_style ();
1571 MixerStrip::show_passthru_color ()
1573 reset_strip_style ();
1577 MixerStrip::build_route_ops_menu ()
1579 using namespace Menu_Helpers;
1580 route_ops_menu = new Menu;
1581 route_ops_menu->set_name ("ArdourContextMenu");
1583 MenuList& items = route_ops_menu->items();
1585 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1587 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1589 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1591 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1593 items.push_back (SeparatorElem());
1595 if (!_route->is_master()) {
1596 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1598 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1599 rename_menu_item = &items.back();
1601 items.push_back (SeparatorElem());
1602 items.push_back (CheckMenuElem (_("Active")));
1603 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1604 i->set_active (_route->active());
1605 i->set_sensitive(! _session->transport_rolling());
1606 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1608 if (!Profile->get_mixbus ()) {
1609 items.push_back (SeparatorElem());
1610 items.push_back (CheckMenuElem (_("Strict I/O")));
1611 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1612 i->set_active (_route->strict_io());
1613 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1616 if (1 /* TODO IFF >= 1 plugin-insert */) {
1617 items.push_back (SeparatorElem());
1618 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1621 items.push_back (SeparatorElem());
1622 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1624 items.push_back (SeparatorElem());
1625 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1626 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1627 denormal_menu_item->set_active (_route->denormal_protection());
1629 items.push_back (SeparatorElem());
1630 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1633 /* note that this relies on selection being shared across editor and
1634 mixer (or global to the backend, in the future), which is the only
1635 sane thing for users anyway.
1638 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1640 Selection& selection (PublicEditor::instance().get_selection());
1641 if (!selection.selected (rtav)) {
1642 selection.set (rtav);
1645 if (!_route->is_master()) {
1646 items.push_back (SeparatorElem());
1647 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1650 items.push_back (SeparatorElem());
1651 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1657 MixerStrip::name_button_button_press (GdkEventButton* ev)
1659 if (ev->button == 3) {
1660 list_route_operations ();
1662 /* do not allow rename if the track is record-enabled */
1663 rename_menu_item->set_sensitive (!_route->record_enabled());
1664 route_ops_menu->popup (1, ev->time);
1673 MixerStrip::name_button_button_release (GdkEventButton* ev)
1675 if (ev->button == 1) {
1676 list_route_operations ();
1678 /* do not allow rename if the track is record-enabled */
1679 rename_menu_item->set_sensitive (!_route->record_enabled());
1680 route_ops_menu->popup (1, ev->time);
1687 MixerStrip::number_button_button_press (GdkEventButton* ev)
1689 if ( ev->button == 3 ) {
1690 list_route_operations ();
1692 /* do not allow rename if the track is record-enabled */
1693 rename_menu_item->set_sensitive (!_route->record_enabled());
1694 route_ops_menu->popup (1, ev->time);
1703 MixerStrip::list_route_operations ()
1705 delete route_ops_menu;
1706 build_route_ops_menu ();
1710 MixerStrip::set_selected (bool yn)
1712 AxisView::set_selected (yn);
1714 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1715 global_frame.set_name ("MixerStripSelectedFrame");
1717 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1718 global_frame.set_name ("MixerStripFrame");
1720 global_frame.queue_draw ();
1723 // processor_box.deselect_all_processors();
1727 MixerStrip::property_changed (const PropertyChange& what_changed)
1729 RouteUI::property_changed (what_changed);
1731 if (what_changed.contains (ARDOUR::Properties::name)) {
1737 MixerStrip::name_changed ()
1741 name_button.set_text (_route->name());
1744 name_button.set_text (PBD::short_version (_route->name(), 5));
1748 set_tooltip (name_button, _route->name());
1750 if (_session->config.get_track_name_number()) {
1751 const int64_t track_number = _route->track_number ();
1752 if (track_number == 0) {
1753 number_label.set_text ("-");
1755 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1758 number_label.set_text ("");
1763 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1765 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1769 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1771 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1775 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1777 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1781 MixerStrip::width_button_pressed (GdkEventButton* ev)
1783 if (ev->button != 1) {
1787 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1790 _mixer.set_strip_width (Narrow, true);
1794 _mixer.set_strip_width (Wide, true);
1800 set_width_enum (Narrow, this);
1803 set_width_enum (Wide, this);
1812 MixerStrip::hide_clicked ()
1814 // LAME fix to reset the button status for when it is redisplayed (part 1)
1815 hide_button.set_sensitive(false);
1818 Hiding(); /* EMIT_SIGNAL */
1820 _mixer.hide_strip (this);
1824 hide_button.set_sensitive(true);
1828 MixerStrip::set_embedded (bool yn)
1834 MixerStrip::map_frozen ()
1836 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1838 boost::shared_ptr<AudioTrack> at = audio_track();
1841 switch (at->freeze_state()) {
1842 case AudioTrack::Frozen:
1843 processor_box.set_sensitive (false);
1844 hide_redirect_editors ();
1847 processor_box.set_sensitive (true);
1848 // XXX need some way, maybe, to retoggle redirect editors
1852 processor_box.set_sensitive (true);
1854 RouteUI::map_frozen ();
1858 MixerStrip::hide_redirect_editors ()
1860 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1864 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1866 boost::shared_ptr<Processor> processor (p.lock ());
1871 Gtk::Window* w = processor_box.get_processor_ui (processor);
1879 MixerStrip::reset_strip_style ()
1881 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1883 gpm.set_fader_name ("SendStripBase");
1887 if (is_midi_track()) {
1888 if (_route->active()) {
1889 set_name ("MidiTrackStripBase");
1891 set_name ("MidiTrackStripBaseInactive");
1893 gpm.set_fader_name ("MidiTrackFader");
1894 } else if (is_audio_track()) {
1895 if (_route->active()) {
1896 set_name ("AudioTrackStripBase");
1898 set_name ("AudioTrackStripBaseInactive");
1900 gpm.set_fader_name ("AudioTrackFader");
1902 if (_route->active()) {
1903 set_name ("AudioBusStripBase");
1905 set_name ("AudioBusStripBaseInactive");
1907 gpm.set_fader_name ("AudioBusFader");
1909 /* (no MIDI busses yet) */
1916 MixerStrip::engine_stopped ()
1921 MixerStrip::engine_running ()
1926 MixerStrip::meter_point_string (MeterPoint mp)
1939 case MeterPostFader:
1956 return S_("Meter|In");
1960 return S_("Meter|Pr");
1963 case MeterPostFader:
1964 return S_("Meter|Po");
1968 return S_("Meter|O");
1973 return S_("Meter|C");
1982 /** Called when the monitor-section state */
1984 MixerStrip::monitor_changed ()
1986 assert (monitor_section_button);
1987 if (_session->monitor_active()) {
1988 monitor_section_button->set_name ("master monitor section button active");
1990 monitor_section_button->set_name ("master monitor section button normal");
1994 /** Called when the metering point has changed */
1996 MixerStrip::meter_changed ()
1998 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1999 gpm.setup_meters ();
2000 // reset peak when meter point changes
2001 gpm.reset_peak_display();
2004 /** The bus that we are displaying sends to has changed, or been turned off.
2005 * @param send_to New bus that we are displaying sends to, or 0.
2008 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2010 RouteUI::bus_send_display_changed (send_to);
2013 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2018 revert_to_default_display ();
2021 revert_to_default_display ();
2026 MixerStrip::drop_send ()
2028 boost::shared_ptr<Send> current_send;
2030 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2031 current_send->set_metering (false);
2034 send_gone_connection.disconnect ();
2035 input_button.set_sensitive (true);
2036 output_button.set_sensitive (true);
2037 group_button.set_sensitive (true);
2038 set_invert_sensitive (true);
2039 meter_point_button.set_sensitive (true);
2040 mute_button->set_sensitive (true);
2041 solo_button->set_sensitive (true);
2042 solo_isolated_led->set_sensitive (true);
2043 solo_safe_led->set_sensitive (true);
2044 monitor_input_button->set_sensitive (true);
2045 monitor_disk_button->set_sensitive (true);
2046 _comment_button.set_sensitive (true);
2047 RouteUI::check_rec_enable_sensitivity ();
2048 set_button_names (); // update solo button visual state
2052 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2054 _current_delivery = d;
2055 DeliveryChanged (_current_delivery);
2059 MixerStrip::show_send (boost::shared_ptr<Send> send)
2065 set_current_delivery (send);
2067 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2068 send->set_metering (true);
2069 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2071 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2072 gain_meter().setup_meters ();
2074 uint32_t const in = _current_delivery->pans_required();
2075 uint32_t const out = _current_delivery->pan_outs();
2077 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2078 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2079 panner_ui().setup_pan ();
2080 panner_ui().set_send_drawing_mode (true);
2081 panner_ui().show_all ();
2083 input_button.set_sensitive (false);
2084 group_button.set_sensitive (false);
2085 set_invert_sensitive (false);
2086 meter_point_button.set_sensitive (false);
2087 mute_button->set_sensitive (false);
2088 solo_button->set_sensitive (false);
2089 rec_enable_button->set_sensitive (false);
2090 solo_isolated_led->set_sensitive (false);
2091 solo_safe_led->set_sensitive (false);
2092 monitor_input_button->set_sensitive (false);
2093 monitor_disk_button->set_sensitive (false);
2094 _comment_button.set_sensitive (false);
2096 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2097 output_button.set_sensitive (false);
2100 reset_strip_style ();
2104 MixerStrip::revert_to_default_display ()
2108 set_current_delivery (_route->main_outs ());
2110 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2111 gain_meter().setup_meters ();
2113 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2114 update_panner_choices();
2115 panner_ui().setup_pan ();
2116 panner_ui().set_send_drawing_mode (false);
2118 if (has_audio_outputs ()) {
2119 panners.show_all ();
2121 panners.hide_all ();
2124 reset_strip_style ();
2128 MixerStrip::set_button_names ()
2132 mute_button->set_text (_("Mute"));
2133 monitor_input_button->set_text (_("In"));
2134 monitor_disk_button->set_text (_("Disk"));
2135 if (monitor_section_button) {
2136 monitor_section_button->set_text (_("Mon"));
2139 if (_route && _route->solo_safe()) {
2140 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2142 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2144 if (!Config->get_solo_control_is_listen_control()) {
2145 solo_button->set_text (_("Solo"));
2147 switch (Config->get_listen_position()) {
2148 case AfterFaderListen:
2149 solo_button->set_text (_("AFL"));
2151 case PreFaderListen:
2152 solo_button->set_text (_("PFL"));
2156 solo_isolated_led->set_text (_("Iso"));
2157 solo_safe_led->set_text (S_("SoloLock|Lock"));
2161 mute_button->set_text (S_("Mute|M"));
2162 monitor_input_button->set_text (S_("MonitorInput|I"));
2163 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2164 if (monitor_section_button) {
2165 monitor_section_button->set_text (S_("Mon|O"));
2168 if (_route && _route->solo_safe()) {
2169 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2171 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2173 if (!Config->get_solo_control_is_listen_control()) {
2174 solo_button->set_text (S_("Solo|S"));
2176 switch (Config->get_listen_position()) {
2177 case AfterFaderListen:
2178 solo_button->set_text (S_("AfterFader|A"));
2180 case PreFaderListen:
2181 solo_button->set_text (S_("Prefader|P"));
2186 solo_isolated_led->set_text (S_("SoloIso|I"));
2187 solo_safe_led->set_text (S_("SoloLock|L"));
2192 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2194 meter_point_button.set_text ("");
2199 MixerStrip::plugin_selector()
2201 return _mixer.plugin_selector();
2205 MixerStrip::hide_things ()
2207 processor_box.hide_things ();
2211 MixerStrip::input_active_button_press (GdkEventButton*)
2213 /* nothing happens on press */
2218 MixerStrip::input_active_button_release (GdkEventButton* ev)
2220 boost::shared_ptr<MidiTrack> mt = midi_track ();
2226 boost::shared_ptr<RouteList> rl (new RouteList);
2228 rl->push_back (route());
2230 _session->set_exclusive_input_active (rl, !mt->input_active(),
2231 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2237 MixerStrip::midi_input_status_changed ()
2239 if (midi_input_enable_button) {
2240 boost::shared_ptr<MidiTrack> mt = midi_track ();
2242 midi_input_enable_button->set_active (mt->input_active ());
2247 MixerStrip::state_id () const
2249 return string_compose ("strip %1", _route->id().to_s());
2253 MixerStrip::parameter_changed (string p)
2255 if (p == _visibility.get_state_name()) {
2256 /* The user has made changes to the mixer strip visibility, so get
2257 our VisibilityGroup to reflect these changes in our widgets.
2259 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2261 else if (p == "track-name-number") {
2264 else if (p == "use-monitor-bus") {
2265 if (monitor_section_button) {
2266 if (mute_button->get_parent()) {
2267 mute_button->get_parent()->remove(*mute_button);
2269 if (monitor_section_button->get_parent()) {
2270 monitor_section_button->get_parent()->remove(*monitor_section_button);
2272 if (Config->get_use_monitor_bus ()) {
2273 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2274 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2275 mute_button->show();
2276 monitor_section_button->show();
2278 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2279 mute_button->show();
2285 /** Called to decide whether the solo isolate / solo lock button visibility should
2286 * be overridden from that configured by the user. We do this for the master bus.
2288 * @return optional value that is present if visibility state should be overridden.
2290 boost::optional<bool>
2291 MixerStrip::override_solo_visibility () const
2293 if (_route && _route->is_master ()) {
2294 return boost::optional<bool> (false);
2297 return boost::optional<bool> ();
2301 MixerStrip::add_input_port (DataType t)
2303 _route->input()->add_port ("", this, t);
2307 MixerStrip::add_output_port (DataType t)
2309 _route->output()->add_port ("", this, t);
2313 MixerStrip::route_active_changed ()
2315 reset_strip_style ();
2319 MixerStrip::copy_processors ()
2321 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2325 MixerStrip::cut_processors ()
2327 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2331 MixerStrip::paste_processors ()
2333 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2337 MixerStrip::select_all_processors ()
2339 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2343 MixerStrip::deselect_all_processors ()
2345 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2349 MixerStrip::delete_processors ()
2351 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2355 MixerStrip::toggle_processors ()
2357 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2361 MixerStrip::ab_plugins ()
2363 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2367 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2369 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2372 if (ev->button == 3) {
2373 popup_level_meter_menu (ev);
2381 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2383 using namespace Gtk::Menu_Helpers;
2385 Gtk::Menu* m = manage (new Menu);
2386 MenuList& items = m->items ();
2388 RadioMenuItem::Group group;
2390 _suspend_menu_callbacks = true;
2391 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2392 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2393 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2394 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2395 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2397 if (gpm.meter_channels().n_audio() == 0) {
2398 m->popup (ev->button, ev->time);
2399 _suspend_menu_callbacks = false;
2403 RadioMenuItem::Group tgroup;
2404 items.push_back (SeparatorElem());
2406 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2407 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2408 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2409 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2410 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2411 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2412 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2413 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2414 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2415 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2416 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2419 if (_route->is_master()) {
2422 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2423 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2424 /* non-master bus */
2427 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2434 MeterType cmt = _route->meter_type();
2435 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2437 items.push_back (SeparatorElem());
2438 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2439 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2440 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2441 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2442 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2443 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2445 m->popup (ev->button, ev->time);
2446 _suspend_menu_callbacks = false;
2450 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2451 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2453 using namespace Menu_Helpers;
2455 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2456 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2457 i->set_active (_route->meter_point() == point);
2461 MixerStrip::set_meter_point (MeterPoint p)
2463 if (_suspend_menu_callbacks) return;
2464 _route->set_meter_point (p);
2468 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2469 RadioMenuItem::Group& group, string const & name, MeterType type)
2471 using namespace Menu_Helpers;
2473 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2474 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2475 i->set_active (_route->meter_type() == type);
2479 MixerStrip::set_meter_type (MeterType t)
2481 if (_suspend_menu_callbacks) return;
2486 MixerStrip::vca_menu_toggle (CheckMenuItem* menuitem, uint32_t n)
2492 boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2498 if (!menuitem->get_active()) {
2499 cerr << "Unassign from " << n << endl;
2500 _mixer.do_vca_unassign (vca);
2502 cerr << "Assign to " << n << endl;
2503 _mixer.do_vca_assign (vca);
2508 MixerStrip::vca_assign (boost::shared_ptr<VCA> vca)
2510 if (!vca || !_route) {
2518 MixerStrip::vca_unassign (boost::shared_ptr<VCA> vca)
2525 /* null VCA means drop all VCA assignments */
2526 _route->gain_control()->clear_masters ();
2528 vca->remove (_route);
2533 MixerStrip::vca_button_release (GdkEventButton* ev)
2535 using namespace Gtk::Menu_Helpers;
2541 /* primary click only */
2543 if (ev->button != 1) {
2548 /* no route - nothing to do */
2552 VCAList vcas (_session->vca_manager().vcas());
2555 /* XXX should probably show a message saying "No VCA masters" */
2559 Menu* menu = new Menu;
2560 MenuList& items = menu->items();
2562 items.push_back (MenuElem (_("Unassign"), sigc::bind (sigc::mem_fun (_mixer, &Mixer_UI::do_vca_unassign), boost::shared_ptr<VCA>())));
2564 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2565 items.push_back (CheckMenuElem ((*v)->name()));
2566 CheckMenuItem* item = dynamic_cast<CheckMenuItem*> (&items.back());
2567 if (_route->slaved_to (*v)) {
2568 cerr << "Yes, slaved to " << (*v)->name() << " aka " << (*v)->number() << endl;
2569 item->set_active (true);
2571 item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), item, (*v)->number()));
2574 menu->popup (1, ev->time);