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 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
90 , _mixer_owned (in_mixer)
91 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
94 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
95 , rec_mon_table (2, 2)
96 , solo_iso_table (1, 2)
97 , mute_solo_table (1, 2)
98 , bottom_button_table (1, 3)
99 , meter_point_button (_("pre"))
100 , monitor_section_button (0)
101 , midi_input_enable_button (0)
102 , _comment_button (_("Comments"))
103 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
104 , _visibility (X_("mixer-element-visibility"))
109 /* the editor mixer strip: don't destroy it every time
110 the underlying route goes away.
113 self_destruct = false;
117 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
121 , _mixer_owned (in_mixer)
122 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
125 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
126 , rec_mon_table (2, 2)
127 , solo_iso_table (1, 2)
128 , mute_solo_table (1, 2)
129 , bottom_button_table (1, 3)
130 , meter_point_button (_("pre"))
131 , monitor_section_button (0)
132 , midi_input_enable_button (0)
133 , _comment_button (_("Comments"))
134 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
135 , _visibility (X_("mixer-element-visibility"))
144 _entered_mixer_strip= 0;
147 ignore_comment_edit = false;
148 ignore_toggle = false;
153 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
154 longest_label = "longest label";
156 string t = _("Click to toggle the width of this mixer strip.");
158 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
161 width_button.set_icon (ArdourIcon::StripWidth);
162 set_tooltip (width_button, t);
164 hide_button.set_icon (ArdourIcon::CloseCross);
165 set_tooltip (&hide_button, _("Hide this mixer strip"));
167 input_button_box.set_spacing(2);
169 input_button.set_text (_("Input"));
170 input_button.set_name ("mixer strip button");
171 input_button_box.pack_start (input_button, true, true);
173 output_button.set_text (_("Output"));
174 output_button.set_name ("mixer strip button");
176 set_tooltip (&meter_point_button, _("Click to select metering point"));
177 meter_point_button.set_name ("mixer strip button");
179 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
181 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
182 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
184 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
186 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
187 solo_isolated_led->show ();
188 solo_isolated_led->set_no_show_all (true);
189 solo_isolated_led->set_name (X_("solo isolate"));
190 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
191 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
192 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
194 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
195 solo_safe_led->show ();
196 solo_safe_led->set_no_show_all (true);
197 solo_safe_led->set_name (X_("solo safe"));
198 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
199 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
200 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
202 solo_safe_led->set_text (S_("SoloLock|Lock"));
203 solo_isolated_led->set_text (_("Iso"));
205 solo_iso_table.set_homogeneous (true);
206 solo_iso_table.set_spacings (2);
207 if (!ARDOUR::Profile->get_trx()) {
208 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
209 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
211 solo_iso_table.show ();
213 vca_button = manage (new ArdourButton (ArdourButton::default_elements));
214 vca_button->set_no_show_all (true);
215 vca_button->set_name (X_("vca assign"));
216 vca_button->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
217 vca_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::vca_button_release), false);
218 UI::instance()->set_tip (*vca_button, _("VCA assignments"));
219 vca_button->set_text (_("-vca-"));
222 rec_mon_table.set_homogeneous (true);
223 rec_mon_table.set_row_spacings (2);
224 rec_mon_table.set_col_spacings (2);
225 if (ARDOUR::Profile->get_mixbus()) {
226 rec_mon_table.resize (1, 3);
227 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
228 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
229 } else if (!ARDOUR::Profile->get_trx()) {
230 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
231 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
233 rec_mon_table.show ();
235 if (solo_isolated_led) {
236 button_size_group->add_widget (*solo_isolated_led);
239 button_size_group->add_widget (*solo_safe_led);
242 if (!ARDOUR::Profile->get_mixbus()) {
243 if (rec_enable_button) {
244 button_size_group->add_widget (*rec_enable_button);
246 if (monitor_disk_button) {
247 button_size_group->add_widget (*monitor_disk_button);
249 if (monitor_input_button) {
250 button_size_group->add_widget (*monitor_input_button);
254 mute_solo_table.set_homogeneous (true);
255 mute_solo_table.set_spacings (2);
257 bottom_button_table.set_spacings (2);
258 bottom_button_table.set_homogeneous (true);
259 bottom_button_table.attach (group_button, 1, 2, 0, 1);
260 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
262 name_button.set_name ("mixer strip button");
263 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
264 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
266 set_tooltip (&group_button, _("Mix group"));
267 group_button.set_name ("mixer strip button");
269 _comment_button.set_name (X_("mixer strip button"));
270 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
272 // TODO implement ArdourKnob::on_size_request properly
273 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
274 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
276 trim_control.set_tooltip_prefix (_("Trim: "));
277 trim_control.set_name ("trim knob");
278 trim_control.set_no_show_all (true);
279 input_button_box.pack_start (trim_control, false, false);
281 global_vpacker.set_border_width (1);
282 global_vpacker.set_spacing (0);
284 width_button.set_name ("mixer strip button");
285 hide_button.set_name ("mixer strip button");
287 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
288 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
290 // width_hide_box.set_border_width (1);
291 width_hide_box.set_spacing (2);
292 width_hide_box.pack_start (width_button, false, true);
293 width_hide_box.pack_start (number_label, true, true);
294 width_hide_box.pack_end (hide_button, false, true);
296 number_label.set_text ("-");
297 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
298 number_label.set_no_show_all ();
299 number_label.set_name ("tracknumber label");
300 number_label.set_fixed_colors (0x80808080, 0x80808080);
301 number_label.set_alignment (.5, .5);
302 number_label.set_fallthrough_to_parent (true);
304 global_vpacker.set_spacing (2);
305 if (!ARDOUR::Profile->get_trx()) {
306 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
308 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (processor_box, true, true);
312 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
313 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
314 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
315 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
316 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
317 global_vpacker.pack_start (*vca_button, Gtk::PACK_SHRINK);
318 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
319 if (!ARDOUR::Profile->get_trx()) {
320 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
321 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
323 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
326 global_frame.add (global_vpacker);
327 global_frame.set_shadow_type (Gtk::SHADOW_IN);
328 global_frame.set_name ("BaseFrame");
332 /* force setting of visible selected status */
335 set_selected (false);
340 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
341 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
343 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
344 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
345 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
347 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
348 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
350 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
351 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
352 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
354 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
356 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
357 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
359 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
363 /* start off as a passthru strip. we'll correct this, if necessary,
364 in update_diskstream_display().
367 /* start off as a passthru strip. we'll correct this, if necessary,
368 in update_diskstream_display().
371 if (is_midi_track()) {
372 set_name ("MidiTrackStripBase");
374 set_name ("AudioTrackStripBase");
377 add_events (Gdk::BUTTON_RELEASE_MASK|
378 Gdk::ENTER_NOTIFY_MASK|
379 Gdk::LEAVE_NOTIFY_MASK|
381 Gdk::KEY_RELEASE_MASK);
383 set_flags (get_flags() | Gtk::CAN_FOCUS);
385 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
386 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
389 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
390 must be the same as those used in RCOptionEditor so that the configuration changes
391 are recognised when they occur.
393 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
394 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
395 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
396 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
397 _visibility.add (&output_button, X_("Output"), _("Output"), false);
398 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
399 _visibility.add (vca_button, X_("VCA"), _("VCA Assigns"), false);
401 parameter_changed (X_("mixer-element-visibility"));
402 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
403 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
404 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
406 //watch for mouse enter/exit so we can do some stuff
407 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
408 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
410 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
413 MixerStrip::~MixerStrip ()
415 CatchDeletion (this);
417 if (this ==_entered_mixer_strip)
418 _entered_mixer_strip = NULL;
422 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
424 _entered_mixer_strip = this;
426 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
427 //because the mixerstrip control is a parent that encompasses the strip
428 deselect_all_processors();
434 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
436 //if we have moved outside our strip, but not into a child view, then deselect ourselves
437 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
438 _entered_mixer_strip= 0;
440 //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
441 gpm.gain_display.set_sensitive(false);
443 gpm.gain_display.set_sensitive(true);
445 //if we leave this mixer strip we need to clear out any selections
446 //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
453 MixerStrip::set_route (boost::shared_ptr<Route> rt)
455 //the rec/monitor stuff only shows up for tracks.
456 //the show_sends only shows up for buses.
457 //remove them all here, and we may add them back later
458 if (show_sends_button->get_parent()) {
459 rec_mon_table.remove (*show_sends_button);
461 if (rec_enable_button->get_parent()) {
462 rec_mon_table.remove (*rec_enable_button);
464 if (monitor_input_button->get_parent()) {
465 rec_mon_table.remove (*monitor_input_button);
467 if (monitor_disk_button->get_parent()) {
468 rec_mon_table.remove (*monitor_disk_button);
470 if (group_button.get_parent()) {
471 bottom_button_table.remove (group_button);
474 RouteUI::set_route (rt);
476 /* ProcessorBox needs access to _route so that it can read
479 processor_box.set_route (rt);
481 revert_to_default_display ();
483 /* unpack these from the parent and stuff them into our own
487 if (gpm.peak_display.get_parent()) {
488 gpm.peak_display.get_parent()->remove (gpm.peak_display);
490 if (gpm.gain_display.get_parent()) {
491 gpm.gain_display.get_parent()->remove (gpm.gain_display);
494 gpm.set_type (rt->meter_type());
496 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
497 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
499 if (solo_button->get_parent()) {
500 mute_solo_table.remove (*solo_button);
503 if (mute_button->get_parent()) {
504 mute_solo_table.remove (*mute_button);
507 if (route()->is_master()) {
508 solo_button->hide ();
509 mute_button->show ();
510 rec_mon_table.hide ();
511 if (solo_iso_table.get_parent()) {
512 solo_iso_table.get_parent()->remove(solo_iso_table);
514 if (monitor_section_button == 0) {
515 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
516 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
518 monitor_section_button = manage (new ArdourButton);
520 monitor_section_button->set_related_action (act);
521 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
522 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
523 monitor_section_button->show();
524 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
526 parameter_changed ("use-monitor-bus");
528 bottom_button_table.attach (group_button, 1, 2, 0, 1);
529 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
530 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
531 mute_button->show ();
532 solo_button->show ();
533 rec_mon_table.show ();
536 if (_mixer_owned && route()->is_master() ) {
538 HScrollbar scrollbar;
539 Gtk::Requisition requisition(scrollbar.size_request ());
540 int scrollbar_height = requisition.height;
542 spacer = manage (new EventBox);
543 spacer->set_size_request (-1, scrollbar_height+2);
544 global_vpacker.pack_start (*spacer, false, false);
549 monitor_input_button->show ();
550 monitor_disk_button->show ();
552 monitor_input_button->hide();
553 monitor_disk_button->hide ();
556 if (route()->trim() && route()->trim()->active()) {
557 trim_control.show ();
558 trim_control.set_controllable (route()->trim()->gain_control());
560 trim_control.hide ();
561 boost::shared_ptr<Controllable> none;
562 trim_control.set_controllable (none);
565 if (is_midi_track()) {
566 if (midi_input_enable_button == 0) {
567 midi_input_enable_button = manage (new ArdourButton);
568 midi_input_enable_button->set_name ("midi input button");
569 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
570 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
571 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
572 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
573 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
575 input_button_box.remove (*midi_input_enable_button);
577 /* get current state */
578 midi_input_status_changed ();
579 input_button_box.pack_start (*midi_input_enable_button, false, false);
581 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
583 if (midi_input_enable_button) {
584 /* removal from the container will delete it */
585 input_button_box.remove (*midi_input_enable_button);
586 midi_input_enable_button = 0;
590 if (is_audio_track()) {
591 boost::shared_ptr<AudioTrack> at = audio_track();
592 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
597 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
598 rec_enable_button->show();
600 if (ARDOUR::Profile->get_mixbus()) {
601 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
602 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
603 } else if (ARDOUR::Profile->get_trx()) {
604 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
606 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
607 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
614 if (!_route->is_master()) {
615 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
616 show_sends_button->show();
620 meter_point_button.set_text (meter_point_string (_route->meter_point()));
622 delete route_ops_menu;
625 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
626 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
627 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
628 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
630 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
632 if (_route->panner_shell()) {
633 update_panner_choices();
634 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
637 if (is_audio_track()) {
638 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
641 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
642 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
644 set_stuff_from_route ();
646 /* now force an update of all the various elements */
648 update_mute_display ();
649 update_solo_display ();
650 update_vca_display ();
653 route_group_changed ();
656 panners.setup_pan ();
658 if (has_audio_outputs ()) {
664 update_diskstream_display ();
665 update_input_display ();
666 update_output_display ();
668 add_events (Gdk::BUTTON_RELEASE_MASK);
670 processor_box.show ();
672 if (!route()->is_master() && !route()->is_monitor()) {
673 /* we don't allow master or control routes to be hidden */
678 gpm.reset_peak_display ();
679 gpm.gain_display.show ();
680 gpm.peak_display.show ();
683 width_hide_box.show();
685 global_vpacker.show();
686 mute_solo_table.show();
687 bottom_button_table.show();
689 meter_point_button.show();
690 input_button_box.show_all();
691 output_button.show();
693 _comment_button.show();
695 gpm.gain_automation_state_button.show();
697 parameter_changed ("mixer-element-visibility");
704 MixerStrip::set_stuff_from_route ()
706 /* if width is not set, it will be set by the MixerUI or editor */
708 string str = gui_property ("strip-width");
710 set_width_enum (Width (string_2_enum (str, _width)), this);
715 MixerStrip::set_width_enum (Width w, void* owner)
717 /* always set the gpm width again, things may be hidden */
720 panners.set_width (w);
722 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
724 _width_owner = owner;
728 if (_width_owner == this) {
729 set_gui_property ("strip-width", enum_2_string (_width));
734 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
739 if (show_sends_button) {
740 show_sends_button->set_text (_("Aux"));
743 gpm.gain_automation_style_button.set_text (
744 gpm.astyle_string(gain_automation->automation_style()));
745 gpm.gain_automation_state_button.set_text (
746 gpm.astate_string(gain_automation->automation_state()));
748 if (_route->panner()) {
749 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
750 panners.astyle_string(_route->panner()->automation_style()));
751 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
752 panners.astate_string(_route->panner()->automation_state()));
756 // panners expect an even number of horiz. pixels
757 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
759 set_size_request (width, -1);
765 if (show_sends_button) {
766 show_sends_button->set_text (_("Snd"));
769 gpm.gain_automation_style_button.set_text (
770 gpm.short_astyle_string(gain_automation->automation_style()));
771 gpm.gain_automation_state_button.set_text (
772 gpm.short_astate_string(gain_automation->automation_state()));
773 gain_meter().setup_meters (); // recalc meter width
775 if (_route->panner()) {
776 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
777 panners.short_astyle_string(_route->panner()->automation_style()));
778 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
779 panners.short_astate_string(_route->panner()->automation_state()));
783 // panners expect an even number of horiz. pixels
784 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
786 set_size_request (width, -1);
791 processor_box.set_width (w);
793 update_input_display ();
794 update_output_display ();
795 setup_comment_button ();
796 route_group_changed ();
802 MixerStrip::set_packed (bool yn)
807 set_gui_property ("visible", true);
809 set_gui_property ("visible", false);
814 struct RouteCompareByName {
815 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
816 return a->name().compare (b->name()) < 0;
821 MixerStrip::output_release (GdkEventButton *ev)
823 switch (ev->button) {
825 edit_output_configuration ();
833 MixerStrip::output_press (GdkEventButton *ev)
835 using namespace Menu_Helpers;
836 if (!_session->engine().connected()) {
837 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
842 MenuList& citems = output_menu.items();
843 switch (ev->button) {
846 return false; //wait for the mouse-up to pop the dialog
850 output_menu.set_name ("ArdourContextMenu");
852 output_menu_bundles.clear ();
854 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
856 citems.push_back (SeparatorElem());
857 uint32_t const n_with_separator = citems.size ();
859 ARDOUR::BundleList current = _route->output()->bundles_connected ();
861 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
863 /* give user bundles first chance at being in the menu */
865 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
866 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
867 maybe_add_bundle_to_output_menu (*i, current);
871 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
872 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
873 maybe_add_bundle_to_output_menu (*i, current);
877 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
878 RouteList copy = *routes;
879 copy.sort (RouteCompareByName ());
880 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
881 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
884 if (citems.size() == n_with_separator) {
885 /* no routes added; remove the separator */
889 if (!ARDOUR::Profile->get_mixbus()) {
890 citems.push_back (SeparatorElem());
892 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
895 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
896 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
902 citems.push_back (SeparatorElem());
903 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
905 output_menu.popup (1, ev->time);
916 MixerStrip::input_release (GdkEventButton *ev)
918 switch (ev->button) {
921 edit_input_configuration ();
933 MixerStrip::input_press (GdkEventButton *ev)
935 using namespace Menu_Helpers;
937 MenuList& citems = input_menu.items();
938 input_menu.set_name ("ArdourContextMenu");
941 if (!_session->engine().connected()) {
942 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
947 if (_session->actively_recording() && _route->record_enabled())
950 switch (ev->button) {
953 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
957 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
959 citems.push_back (SeparatorElem());
960 uint32_t const n_with_separator = citems.size ();
962 input_menu_bundles.clear ();
964 ARDOUR::BundleList current = _route->input()->bundles_connected ();
966 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
968 /* give user bundles first chance at being in the menu */
970 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
971 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
972 maybe_add_bundle_to_input_menu (*i, current);
976 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
977 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
978 maybe_add_bundle_to_input_menu (*i, current);
982 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
983 RouteList copy = *routes;
984 copy.sort (RouteCompareByName ());
985 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
986 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
989 if (citems.size() == n_with_separator) {
990 /* no routes added; remove the separator */
994 citems.push_back (SeparatorElem());
995 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
998 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
999 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1004 citems.push_back (SeparatorElem());
1005 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1007 input_menu.popup (1, ev->time);
1018 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1020 if (ignore_toggle) {
1024 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1026 if (std::find (current.begin(), current.end(), c) == current.end()) {
1027 _route->input()->connect_ports_to_bundle (c, true, this);
1029 _route->input()->disconnect_ports_from_bundle (c, this);
1034 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1036 if (ignore_toggle) {
1040 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1042 if (std::find (current.begin(), current.end(), c) == current.end()) {
1043 _route->output()->connect_ports_to_bundle (c, true, this);
1045 _route->output()->disconnect_ports_from_bundle (c, this);
1050 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1052 using namespace Menu_Helpers;
1054 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1058 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1059 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1063 if (i != input_menu_bundles.end()) {
1067 input_menu_bundles.push_back (b);
1069 MenuList& citems = input_menu.items();
1071 std::string n = b->name ();
1072 replace_all (n, "_", " ");
1074 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1078 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1080 using namespace Menu_Helpers;
1082 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1086 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1087 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1091 if (i != output_menu_bundles.end()) {
1095 output_menu_bundles.push_back (b);
1097 MenuList& citems = output_menu.items();
1099 std::string n = b->name ();
1100 replace_all (n, "_", " ");
1102 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1106 MixerStrip::update_diskstream_display ()
1108 if (is_track() && input_selector) {
1109 input_selector->hide_all ();
1112 route_color_changed ();
1116 MixerStrip::connect_to_pan ()
1118 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1120 panstate_connection.disconnect ();
1121 panstyle_connection.disconnect ();
1123 if (!_route->panner()) {
1127 boost::shared_ptr<Pannable> p = _route->pannable ();
1129 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1130 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1132 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1133 * However, that only works a panner was previously set.
1135 * PannerUI must remain subscribed to _panshell->Changed() in case
1136 * we switch the panner eg. AUX-Send and back
1137 * _route->panner_shell()->Changed() vs _panshell->Changed
1139 if (panners._panner == 0) {
1140 panners.panshell_changed ();
1142 update_panner_choices();
1146 MixerStrip::update_panner_choices ()
1148 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1149 if (!_route->panner_shell()) { return; }
1151 uint32_t in = _route->output()->n_ports().n_audio();
1153 if (_route->panner()) {
1154 in = _route->panner()->in().n_audio();
1157 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1161 * Output port labelling
1162 * =====================
1164 * Case 1: Each output has one connection, all connections are to system:playback_%i
1165 * out 1 -> system:playback_1
1166 * out 2 -> system:playback_2
1167 * out 3 -> system:playback_3
1170 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1171 * out 1 -> ardour:track_x/in 1
1172 * out 2 -> ardour:track_x/in 2
1173 * Display as: track_x
1175 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1176 * out 1 -> program x:foo
1177 * out 2 -> program x:foo
1178 * Display as: program x
1180 * Case 4: No connections (Disconnected)
1183 * Default case (unusual routing):
1184 * Display as: *number of connections*
1188 * .-----------------------------------------------.
1190 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1191 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1192 * '-----------------------------------------------'
1193 * .-----------------------------------------------.
1196 * '-----------------------------------------------'
1200 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1204 boost::shared_ptr<Port> port;
1205 vector<string> port_connections;
1207 uint32_t total_connection_count = 0;
1208 uint32_t io_connection_count = 0;
1209 uint32_t ardour_connection_count = 0;
1210 uint32_t system_connection_count = 0;
1211 uint32_t other_connection_count = 0;
1212 uint32_t typed_connection_count = 0;
1214 ostringstream label;
1216 bool have_label = false;
1217 bool each_io_has_one_connection = true;
1219 string connection_name;
1220 string ardour_track_name;
1221 string other_connection_type;
1222 string system_ports;
1225 ostringstream tooltip;
1226 char * tooltip_cstr;
1228 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1229 DataType dt = DataType::AUDIO;
1230 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1231 dt = DataType::MIDI;
1232 // avoid further confusion with Midi-tracks that have a synth.
1233 // Audio-ports may be connected, but button says "Disconnected"
1234 tooltip << _("MIDI ");
1238 io_count = route->n_inputs().n_total();
1239 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1241 io_count = route->n_outputs().n_total();
1242 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1246 for (io_index = 0; io_index < io_count; ++io_index) {
1248 port = route->input()->nth (io_index);
1250 port = route->output()->nth (io_index);
1253 port_connections.clear ();
1254 port->get_connections(port_connections);
1256 //ignore any port connections that don't match our DataType
1257 if (port->type() != dt) {
1258 if (!port_connections.empty()) {
1259 ++typed_connection_count;
1264 io_connection_count = 0;
1266 if (!port_connections.empty()) {
1267 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1269 string& connection_name (*i);
1271 if (connection_name.find("system:") == 0) {
1272 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1275 if (io_connection_count == 0) {
1276 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1278 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1281 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1284 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1285 if (ardour_track_name.empty()) {
1286 // "ardour:Master/in 1" -> "ardour:Master/"
1287 string::size_type slash = connection_name.find("/");
1288 if (slash != string::npos) {
1289 ardour_track_name = connection_name.substr(0, slash + 1);
1293 if (connection_name.find(ardour_track_name) == 0) {
1294 ++ardour_connection_count;
1296 } else if (!pn.empty()) {
1297 if (system_ports.empty()) {
1300 system_ports += "/" + pn;
1302 if (connection_name.find("system:") == 0) {
1303 ++system_connection_count;
1305 } else if (connection_name.find("system:midi_") == 0) {
1307 // "system:midi_capture_123" -> "123"
1308 system_port = "M " + connection_name.substr(20);
1310 // "system:midi_playback_123" -> "123"
1311 system_port = "M " + connection_name.substr(21);
1314 if (system_ports.empty()) {
1315 system_ports += system_port;
1317 system_ports += "/" + system_port;
1320 ++system_connection_count;
1322 } else if (connection_name.find("system:") == 0) {
1324 // "system:capture_123" -> "123"
1325 system_port = connection_name.substr(15);
1327 // "system:playback_123" -> "123"
1328 system_port = connection_name.substr(16);
1331 if (system_ports.empty()) {
1332 system_ports += system_port;
1334 system_ports += "/" + system_port;
1337 ++system_connection_count;
1339 if (other_connection_type.empty()) {
1340 // "jamin:in 1" -> "jamin:"
1341 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1344 if (connection_name.find(other_connection_type) == 0) {
1345 ++other_connection_count;
1349 ++total_connection_count;
1350 ++io_connection_count;
1354 if (io_connection_count != 1) {
1355 each_io_has_one_connection = false;
1359 if (total_connection_count == 0) {
1360 tooltip << endl << _("Disconnected");
1363 tooltip_cstr = new char[tooltip.str().size() + 1];
1364 strcpy(tooltip_cstr, tooltip.str().c_str());
1367 set_tooltip (&input_button, tooltip_cstr);
1369 set_tooltip (&output_button, tooltip_cstr);
1372 delete [] tooltip_cstr;
1374 if (each_io_has_one_connection) {
1375 if (total_connection_count == ardour_connection_count) {
1376 // all connections are to the same track in ardour
1377 // "ardour:Master/" -> "Master"
1378 string::size_type slash = ardour_track_name.find("/");
1379 if (slash != string::npos) {
1380 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1381 label << ardour_track_name.substr (ppps, slash - ppps);
1385 else if (total_connection_count == system_connection_count) {
1386 // all connections are to system ports
1387 label << system_ports;
1390 else if (total_connection_count == other_connection_count) {
1391 // all connections are to the same external program eg jamin
1392 // "jamin:" -> "jamin"
1393 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1399 if (total_connection_count == 0) {
1403 // Odd configuration
1404 label << "*" << total_connection_count << "*";
1406 if (typed_connection_count > 0) {
1407 label << "\u2295"; // circled plus
1412 input_button.set_text (label.str());
1414 output_button.set_text (label.str());
1419 MixerStrip::update_input_display ()
1421 update_io_button (_route, _width, true);
1422 panners.setup_pan ();
1424 if (has_audio_outputs ()) {
1425 panners.show_all ();
1427 panners.hide_all ();
1433 MixerStrip::update_output_display ()
1435 update_io_button (_route, _width, false);
1436 gpm.setup_meters ();
1437 panners.setup_pan ();
1439 if (has_audio_outputs ()) {
1440 panners.show_all ();
1442 panners.hide_all ();
1447 MixerStrip::fast_update ()
1449 gpm.update_meters ();
1453 MixerStrip::diskstream_changed ()
1455 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1459 MixerStrip::io_changed_proxy ()
1461 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1465 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1467 boost::shared_ptr<Port> a = wa.lock ();
1468 boost::shared_ptr<Port> b = wb.lock ();
1470 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1471 update_input_display ();
1472 set_width_enum (_width, this);
1475 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1476 update_output_display ();
1477 set_width_enum (_width, this);
1482 MixerStrip::setup_comment_button ()
1487 if (_route->comment().empty ()) {
1488 _comment_button.unset_bg (STATE_NORMAL);
1489 _comment_button.set_text (_("Comments"));
1491 _comment_button.modify_bg (STATE_NORMAL, color ());
1492 _comment_button.set_text (_("*Comments*"));
1497 if (_route->comment().empty ()) {
1498 _comment_button.unset_bg (STATE_NORMAL);
1499 _comment_button.set_text (_("Cmt"));
1501 _comment_button.modify_bg (STATE_NORMAL, color ());
1502 _comment_button.set_text (_("*Cmt*"));
1508 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1514 MixerStrip::select_route_group (GdkEventButton *ev)
1516 using namespace Menu_Helpers;
1518 if (ev->button == 1) {
1520 if (group_menu == 0) {
1522 PropertyList* plist = new PropertyList();
1524 plist->add (Properties::gain, true);
1525 plist->add (Properties::mute, true);
1526 plist->add (Properties::solo, true);
1528 group_menu = new RouteGroupMenu (_session, plist);
1532 r.push_back (route ());
1533 group_menu->build (r);
1534 group_menu->menu()->popup (1, ev->time);
1541 MixerStrip::route_group_changed ()
1543 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1545 RouteGroup *rg = _route->route_group();
1548 group_button.set_text (PBD::short_version (rg->name(), 5));
1552 group_button.set_text (_("Grp"));
1555 group_button.set_text (_("~G"));
1562 MixerStrip::route_color_changed ()
1564 name_button.modify_bg (STATE_NORMAL, color());
1565 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1566 reset_strip_style ();
1570 MixerStrip::show_passthru_color ()
1572 reset_strip_style ();
1576 MixerStrip::build_route_ops_menu ()
1578 using namespace Menu_Helpers;
1579 route_ops_menu = new Menu;
1580 route_ops_menu->set_name ("ArdourContextMenu");
1582 MenuList& items = route_ops_menu->items();
1584 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1586 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1588 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1590 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1592 items.push_back (SeparatorElem());
1594 if (!_route->is_master()) {
1595 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1597 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1598 rename_menu_item = &items.back();
1600 items.push_back (SeparatorElem());
1601 items.push_back (CheckMenuElem (_("Active")));
1602 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1603 i->set_active (_route->active());
1604 i->set_sensitive(! _session->transport_rolling());
1605 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1607 if (!Profile->get_mixbus ()) {
1608 items.push_back (SeparatorElem());
1609 items.push_back (CheckMenuElem (_("Strict I/O")));
1610 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1611 i->set_active (_route->strict_io());
1612 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1615 if (1 /* TODO IFF >= 1 plugin-insert */) {
1616 items.push_back (SeparatorElem());
1617 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1620 items.push_back (SeparatorElem());
1621 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1623 items.push_back (SeparatorElem());
1624 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1625 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1626 denormal_menu_item->set_active (_route->denormal_protection());
1628 items.push_back (SeparatorElem());
1629 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1632 /* note that this relies on selection being shared across editor and
1633 mixer (or global to the backend, in the future), which is the only
1634 sane thing for users anyway.
1637 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1639 Selection& selection (PublicEditor::instance().get_selection());
1640 if (!selection.selected (rtav)) {
1641 selection.set (rtav);
1644 if (!_route->is_master()) {
1645 items.push_back (SeparatorElem());
1646 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1649 items.push_back (SeparatorElem());
1650 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1656 MixerStrip::name_button_button_press (GdkEventButton* ev)
1658 if (ev->button == 3) {
1659 list_route_operations ();
1661 /* do not allow rename if the track is record-enabled */
1662 rename_menu_item->set_sensitive (!_route->record_enabled());
1663 route_ops_menu->popup (1, ev->time);
1672 MixerStrip::name_button_button_release (GdkEventButton* ev)
1674 if (ev->button == 1) {
1675 list_route_operations ();
1677 /* do not allow rename if the track is record-enabled */
1678 rename_menu_item->set_sensitive (!_route->record_enabled());
1679 route_ops_menu->popup (1, ev->time);
1686 MixerStrip::number_button_button_press (GdkEventButton* ev)
1688 if ( ev->button == 3 ) {
1689 list_route_operations ();
1691 /* do not allow rename if the track is record-enabled */
1692 rename_menu_item->set_sensitive (!_route->record_enabled());
1693 route_ops_menu->popup (1, ev->time);
1702 MixerStrip::list_route_operations ()
1704 delete route_ops_menu;
1705 build_route_ops_menu ();
1709 MixerStrip::set_selected (bool yn)
1711 AxisView::set_selected (yn);
1713 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1714 global_frame.set_name ("MixerStripSelectedFrame");
1716 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1717 global_frame.set_name ("MixerStripFrame");
1719 global_frame.queue_draw ();
1722 // processor_box.deselect_all_processors();
1726 MixerStrip::property_changed (const PropertyChange& what_changed)
1728 RouteUI::property_changed (what_changed);
1730 if (what_changed.contains (ARDOUR::Properties::name)) {
1736 MixerStrip::name_changed ()
1740 name_button.set_text (_route->name());
1743 name_button.set_text (PBD::short_version (_route->name(), 5));
1747 set_tooltip (name_button, _route->name());
1749 if (_session->config.get_track_name_number()) {
1750 const int64_t track_number = _route->track_number ();
1751 if (track_number == 0) {
1752 number_label.set_text ("-");
1754 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1757 number_label.set_text ("");
1762 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1764 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1768 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1770 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1774 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1776 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1780 MixerStrip::width_button_pressed (GdkEventButton* ev)
1782 if (ev->button != 1) {
1786 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1789 _mixer.set_strip_width (Narrow, true);
1793 _mixer.set_strip_width (Wide, true);
1799 set_width_enum (Narrow, this);
1802 set_width_enum (Wide, this);
1811 MixerStrip::hide_clicked ()
1813 // LAME fix to reset the button status for when it is redisplayed (part 1)
1814 hide_button.set_sensitive(false);
1817 Hiding(); /* EMIT_SIGNAL */
1819 _mixer.hide_strip (this);
1823 hide_button.set_sensitive(true);
1827 MixerStrip::set_embedded (bool yn)
1833 MixerStrip::map_frozen ()
1835 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1837 boost::shared_ptr<AudioTrack> at = audio_track();
1840 switch (at->freeze_state()) {
1841 case AudioTrack::Frozen:
1842 processor_box.set_sensitive (false);
1843 hide_redirect_editors ();
1846 processor_box.set_sensitive (true);
1847 // XXX need some way, maybe, to retoggle redirect editors
1851 processor_box.set_sensitive (true);
1853 RouteUI::map_frozen ();
1857 MixerStrip::hide_redirect_editors ()
1859 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1863 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1865 boost::shared_ptr<Processor> processor (p.lock ());
1870 Gtk::Window* w = processor_box.get_processor_ui (processor);
1878 MixerStrip::reset_strip_style ()
1880 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1882 gpm.set_fader_name ("SendStripBase");
1886 if (is_midi_track()) {
1887 if (_route->active()) {
1888 set_name ("MidiTrackStripBase");
1890 set_name ("MidiTrackStripBaseInactive");
1892 gpm.set_fader_name ("MidiTrackFader");
1893 } else if (is_audio_track()) {
1894 if (_route->active()) {
1895 set_name ("AudioTrackStripBase");
1897 set_name ("AudioTrackStripBaseInactive");
1899 gpm.set_fader_name ("AudioTrackFader");
1901 if (_route->active()) {
1902 set_name ("AudioBusStripBase");
1904 set_name ("AudioBusStripBaseInactive");
1906 gpm.set_fader_name ("AudioBusFader");
1908 /* (no MIDI busses yet) */
1915 MixerStrip::engine_stopped ()
1920 MixerStrip::engine_running ()
1925 MixerStrip::meter_point_string (MeterPoint mp)
1938 case MeterPostFader:
1955 return S_("Meter|In");
1959 return S_("Meter|Pr");
1962 case MeterPostFader:
1963 return S_("Meter|Po");
1967 return S_("Meter|O");
1972 return S_("Meter|C");
1981 /** Called when the monitor-section state */
1983 MixerStrip::monitor_changed ()
1985 assert (monitor_section_button);
1986 if (_session->monitor_active()) {
1987 monitor_section_button->set_name ("master monitor section button active");
1989 monitor_section_button->set_name ("master monitor section button normal");
1993 /** Called when the metering point has changed */
1995 MixerStrip::meter_changed ()
1997 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1998 gpm.setup_meters ();
1999 // reset peak when meter point changes
2000 gpm.reset_peak_display();
2003 /** The bus that we are displaying sends to has changed, or been turned off.
2004 * @param send_to New bus that we are displaying sends to, or 0.
2007 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2009 RouteUI::bus_send_display_changed (send_to);
2012 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2017 revert_to_default_display ();
2020 revert_to_default_display ();
2025 MixerStrip::drop_send ()
2027 boost::shared_ptr<Send> current_send;
2029 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2030 current_send->set_metering (false);
2033 send_gone_connection.disconnect ();
2034 input_button.set_sensitive (true);
2035 output_button.set_sensitive (true);
2036 group_button.set_sensitive (true);
2037 set_invert_sensitive (true);
2038 meter_point_button.set_sensitive (true);
2039 mute_button->set_sensitive (true);
2040 solo_button->set_sensitive (true);
2041 solo_isolated_led->set_sensitive (true);
2042 solo_safe_led->set_sensitive (true);
2043 monitor_input_button->set_sensitive (true);
2044 monitor_disk_button->set_sensitive (true);
2045 _comment_button.set_sensitive (true);
2046 RouteUI::check_rec_enable_sensitivity ();
2047 set_button_names (); // update solo button visual state
2051 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2053 _current_delivery = d;
2054 DeliveryChanged (_current_delivery);
2058 MixerStrip::show_send (boost::shared_ptr<Send> send)
2064 set_current_delivery (send);
2066 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2067 send->set_metering (true);
2068 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2070 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2071 gain_meter().setup_meters ();
2073 uint32_t const in = _current_delivery->pans_required();
2074 uint32_t const out = _current_delivery->pan_outs();
2076 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2077 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2078 panner_ui().setup_pan ();
2079 panner_ui().set_send_drawing_mode (true);
2080 panner_ui().show_all ();
2082 input_button.set_sensitive (false);
2083 group_button.set_sensitive (false);
2084 set_invert_sensitive (false);
2085 meter_point_button.set_sensitive (false);
2086 mute_button->set_sensitive (false);
2087 solo_button->set_sensitive (false);
2088 rec_enable_button->set_sensitive (false);
2089 solo_isolated_led->set_sensitive (false);
2090 solo_safe_led->set_sensitive (false);
2091 monitor_input_button->set_sensitive (false);
2092 monitor_disk_button->set_sensitive (false);
2093 _comment_button.set_sensitive (false);
2095 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2096 output_button.set_sensitive (false);
2099 reset_strip_style ();
2103 MixerStrip::revert_to_default_display ()
2107 set_current_delivery (_route->main_outs ());
2109 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2110 gain_meter().setup_meters ();
2112 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2113 update_panner_choices();
2114 panner_ui().setup_pan ();
2115 panner_ui().set_send_drawing_mode (false);
2117 if (has_audio_outputs ()) {
2118 panners.show_all ();
2120 panners.hide_all ();
2123 reset_strip_style ();
2127 MixerStrip::set_button_names ()
2131 mute_button->set_text (_("Mute"));
2132 monitor_input_button->set_text (_("In"));
2133 monitor_disk_button->set_text (_("Disk"));
2134 if (monitor_section_button) {
2135 monitor_section_button->set_text (_("Mon"));
2138 if (_route && _route->solo_safe()) {
2139 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2141 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2143 if (!Config->get_solo_control_is_listen_control()) {
2144 solo_button->set_text (_("Solo"));
2146 switch (Config->get_listen_position()) {
2147 case AfterFaderListen:
2148 solo_button->set_text (_("AFL"));
2150 case PreFaderListen:
2151 solo_button->set_text (_("PFL"));
2155 solo_isolated_led->set_text (_("Iso"));
2156 solo_safe_led->set_text (S_("SoloLock|Lock"));
2160 mute_button->set_text (S_("Mute|M"));
2161 monitor_input_button->set_text (S_("MonitorInput|I"));
2162 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2163 if (monitor_section_button) {
2164 monitor_section_button->set_text (S_("Mon|O"));
2167 if (_route && _route->solo_safe()) {
2168 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2170 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2172 if (!Config->get_solo_control_is_listen_control()) {
2173 solo_button->set_text (S_("Solo|S"));
2175 switch (Config->get_listen_position()) {
2176 case AfterFaderListen:
2177 solo_button->set_text (S_("AfterFader|A"));
2179 case PreFaderListen:
2180 solo_button->set_text (S_("Prefader|P"));
2185 solo_isolated_led->set_text (S_("SoloIso|I"));
2186 solo_safe_led->set_text (S_("SoloLock|L"));
2191 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2193 meter_point_button.set_text ("");
2198 MixerStrip::plugin_selector()
2200 return _mixer.plugin_selector();
2204 MixerStrip::hide_things ()
2206 processor_box.hide_things ();
2210 MixerStrip::input_active_button_press (GdkEventButton*)
2212 /* nothing happens on press */
2217 MixerStrip::input_active_button_release (GdkEventButton* ev)
2219 boost::shared_ptr<MidiTrack> mt = midi_track ();
2225 boost::shared_ptr<RouteList> rl (new RouteList);
2227 rl->push_back (route());
2229 _session->set_exclusive_input_active (rl, !mt->input_active(),
2230 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2236 MixerStrip::midi_input_status_changed ()
2238 if (midi_input_enable_button) {
2239 boost::shared_ptr<MidiTrack> mt = midi_track ();
2241 midi_input_enable_button->set_active (mt->input_active ());
2246 MixerStrip::state_id () const
2248 return string_compose ("strip %1", _route->id().to_s());
2252 MixerStrip::parameter_changed (string p)
2254 if (p == _visibility.get_state_name()) {
2255 /* The user has made changes to the mixer strip visibility, so get
2256 our VisibilityGroup to reflect these changes in our widgets.
2258 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2260 else if (p == "track-name-number") {
2263 else if (p == "use-monitor-bus") {
2264 if (monitor_section_button) {
2265 if (mute_button->get_parent()) {
2266 mute_button->get_parent()->remove(*mute_button);
2268 if (monitor_section_button->get_parent()) {
2269 monitor_section_button->get_parent()->remove(*monitor_section_button);
2271 if (Config->get_use_monitor_bus ()) {
2272 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2273 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2274 mute_button->show();
2275 monitor_section_button->show();
2277 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2278 mute_button->show();
2284 /** Called to decide whether the solo isolate / solo lock button visibility should
2285 * be overridden from that configured by the user. We do this for the master bus.
2287 * @return optional value that is present if visibility state should be overridden.
2289 boost::optional<bool>
2290 MixerStrip::override_solo_visibility () const
2292 if (_route && _route->is_master ()) {
2293 return boost::optional<bool> (false);
2296 return boost::optional<bool> ();
2300 MixerStrip::add_input_port (DataType t)
2302 _route->input()->add_port ("", this, t);
2306 MixerStrip::add_output_port (DataType t)
2308 _route->output()->add_port ("", this, t);
2312 MixerStrip::route_active_changed ()
2314 reset_strip_style ();
2318 MixerStrip::copy_processors ()
2320 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2324 MixerStrip::cut_processors ()
2326 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2330 MixerStrip::paste_processors ()
2332 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2336 MixerStrip::select_all_processors ()
2338 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2342 MixerStrip::deselect_all_processors ()
2344 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2348 MixerStrip::delete_processors ()
2350 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2354 MixerStrip::toggle_processors ()
2356 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2360 MixerStrip::ab_plugins ()
2362 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2366 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2368 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2371 if (ev->button == 3) {
2372 popup_level_meter_menu (ev);
2380 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2382 using namespace Gtk::Menu_Helpers;
2384 Gtk::Menu* m = manage (new Menu);
2385 MenuList& items = m->items ();
2387 RadioMenuItem::Group group;
2389 _suspend_menu_callbacks = true;
2390 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2391 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2392 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2393 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2394 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2396 if (gpm.meter_channels().n_audio() == 0) {
2397 m->popup (ev->button, ev->time);
2398 _suspend_menu_callbacks = false;
2402 RadioMenuItem::Group tgroup;
2403 items.push_back (SeparatorElem());
2405 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2406 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2407 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2408 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2409 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2410 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2411 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2412 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2413 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2414 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2415 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2418 if (_route->is_master()) {
2421 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2422 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2423 /* non-master bus */
2426 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2433 MeterType cmt = _route->meter_type();
2434 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2436 items.push_back (SeparatorElem());
2437 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2438 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2439 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2440 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2441 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2442 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2444 m->popup (ev->button, ev->time);
2445 _suspend_menu_callbacks = false;
2449 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2450 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2452 using namespace Menu_Helpers;
2454 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2455 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2456 i->set_active (_route->meter_point() == point);
2460 MixerStrip::set_meter_point (MeterPoint p)
2462 if (_suspend_menu_callbacks) return;
2463 _route->set_meter_point (p);
2467 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2468 RadioMenuItem::Group& group, string const & name, MeterType type)
2470 using namespace Menu_Helpers;
2472 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2473 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2474 i->set_active (_route->meter_type() == type);
2478 MixerStrip::set_meter_type (MeterType t)
2480 if (_suspend_menu_callbacks) return;
2485 MixerStrip::vca_menu_toggle (CheckMenuItem* menuitem, uint32_t n)
2491 boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2497 if (!menuitem->get_active()) {
2498 cerr << "Unassign from " << n << endl;
2499 _mixer.do_vca_unassign (vca);
2501 cerr << "Assign to " << n << endl;
2502 _mixer.do_vca_assign (vca);
2507 MixerStrip::vca_assign (boost::shared_ptr<VCA> vca)
2509 if (!vca || !_route) {
2517 MixerStrip::vca_unassign (boost::shared_ptr<VCA> vca)
2524 /* null VCA means drop all VCA assignments */
2525 _route->gain_control()->clear_masters ();
2527 vca->remove (_route);
2532 MixerStrip::vca_button_release (GdkEventButton* ev)
2534 using namespace Gtk::Menu_Helpers;
2540 /* primary click only */
2542 if (ev->button != 1) {
2547 /* no route - nothing to do */
2551 VCAList vcas (_session->vca_manager().vcas());
2554 /* XXX should probably show a message saying "No VCA masters" */
2558 Menu* menu = new Menu;
2559 MenuList& items = menu->items();
2561 items.push_back (MenuElem (_("Unassign"), sigc::bind (sigc::mem_fun (_mixer, &Mixer_UI::do_vca_unassign), boost::shared_ptr<VCA>())));
2563 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2564 items.push_back (CheckMenuElem ((*v)->name()));
2565 CheckMenuItem* item = dynamic_cast<CheckMenuItem*> (&items.back());
2566 if (_route->slaved_to (*v)) {
2567 cerr << "Yes, slaved to " << (*v)->name() << " aka " << (*v)->number() << endl;
2568 item->set_active (true);
2570 item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), item, (*v)->number()));
2573 menu->popup (1, ev->time);