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 _route->gain_control()->VCAStatusChange.connect (route_connections,
646 boost::bind (&MixerStrip::update_vca_display, this),
649 set_stuff_from_route ();
651 /* now force an update of all the various elements */
653 update_mute_display ();
654 update_solo_display ();
655 update_vca_display ();
658 route_group_changed ();
661 panners.setup_pan ();
663 if (has_audio_outputs ()) {
669 update_diskstream_display ();
670 update_input_display ();
671 update_output_display ();
673 add_events (Gdk::BUTTON_RELEASE_MASK);
675 processor_box.show ();
677 if (!route()->is_master() && !route()->is_monitor()) {
678 /* we don't allow master or control routes to be hidden */
683 gpm.reset_peak_display ();
684 gpm.gain_display.show ();
685 gpm.peak_display.show ();
688 width_hide_box.show();
690 global_vpacker.show();
691 mute_solo_table.show();
692 bottom_button_table.show();
694 meter_point_button.show();
695 input_button_box.show_all();
696 output_button.show();
698 _comment_button.show();
700 gpm.gain_automation_state_button.show();
702 parameter_changed ("mixer-element-visibility");
709 MixerStrip::set_stuff_from_route ()
711 /* if width is not set, it will be set by the MixerUI or editor */
713 string str = gui_property ("strip-width");
715 set_width_enum (Width (string_2_enum (str, _width)), this);
720 MixerStrip::set_width_enum (Width w, void* owner)
722 /* always set the gpm width again, things may be hidden */
725 panners.set_width (w);
727 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
729 _width_owner = owner;
733 if (_width_owner == this) {
734 set_gui_property ("strip-width", enum_2_string (_width));
739 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
744 if (show_sends_button) {
745 show_sends_button->set_text (_("Aux"));
748 gpm.gain_automation_style_button.set_text (
749 gpm.astyle_string(gain_automation->automation_style()));
750 gpm.gain_automation_state_button.set_text (
751 gpm.astate_string(gain_automation->automation_state()));
753 if (_route->panner()) {
754 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
755 panners.astyle_string(_route->panner()->automation_style()));
756 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
757 panners.astate_string(_route->panner()->automation_state()));
761 // panners expect an even number of horiz. pixels
762 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
764 set_size_request (width, -1);
770 if (show_sends_button) {
771 show_sends_button->set_text (_("Snd"));
774 gpm.gain_automation_style_button.set_text (
775 gpm.short_astyle_string(gain_automation->automation_style()));
776 gpm.gain_automation_state_button.set_text (
777 gpm.short_astate_string(gain_automation->automation_state()));
778 gain_meter().setup_meters (); // recalc meter width
780 if (_route->panner()) {
781 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
782 panners.short_astyle_string(_route->panner()->automation_style()));
783 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
784 panners.short_astate_string(_route->panner()->automation_state()));
788 // panners expect an even number of horiz. pixels
789 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
791 set_size_request (width, -1);
796 processor_box.set_width (w);
798 update_input_display ();
799 update_output_display ();
800 setup_comment_button ();
801 route_group_changed ();
807 MixerStrip::set_packed (bool yn)
812 set_gui_property ("visible", true);
814 set_gui_property ("visible", false);
819 struct RouteCompareByName {
820 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
821 return a->name().compare (b->name()) < 0;
826 MixerStrip::output_release (GdkEventButton *ev)
828 switch (ev->button) {
830 edit_output_configuration ();
838 MixerStrip::output_press (GdkEventButton *ev)
840 using namespace Menu_Helpers;
841 if (!_session->engine().connected()) {
842 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
847 MenuList& citems = output_menu.items();
848 switch (ev->button) {
851 return false; //wait for the mouse-up to pop the dialog
855 output_menu.set_name ("ArdourContextMenu");
857 output_menu_bundles.clear ();
859 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
861 citems.push_back (SeparatorElem());
862 uint32_t const n_with_separator = citems.size ();
864 ARDOUR::BundleList current = _route->output()->bundles_connected ();
866 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
868 /* give user bundles first chance at being in the menu */
870 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
871 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
872 maybe_add_bundle_to_output_menu (*i, current);
876 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
877 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
878 maybe_add_bundle_to_output_menu (*i, current);
882 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
883 RouteList copy = *routes;
884 copy.sort (RouteCompareByName ());
885 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
886 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
889 if (citems.size() == n_with_separator) {
890 /* no routes added; remove the separator */
894 if (!ARDOUR::Profile->get_mixbus()) {
895 citems.push_back (SeparatorElem());
897 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
900 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
901 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
907 citems.push_back (SeparatorElem());
908 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
910 output_menu.popup (1, ev->time);
921 MixerStrip::input_release (GdkEventButton *ev)
923 switch (ev->button) {
926 edit_input_configuration ();
938 MixerStrip::input_press (GdkEventButton *ev)
940 using namespace Menu_Helpers;
942 MenuList& citems = input_menu.items();
943 input_menu.set_name ("ArdourContextMenu");
946 if (!_session->engine().connected()) {
947 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
952 if (_session->actively_recording() && _route->record_enabled())
955 switch (ev->button) {
958 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
962 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
964 citems.push_back (SeparatorElem());
965 uint32_t const n_with_separator = citems.size ();
967 input_menu_bundles.clear ();
969 ARDOUR::BundleList current = _route->input()->bundles_connected ();
971 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
973 /* give user bundles first chance at being in the menu */
975 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
976 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
977 maybe_add_bundle_to_input_menu (*i, current);
981 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
982 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
983 maybe_add_bundle_to_input_menu (*i, current);
987 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
988 RouteList copy = *routes;
989 copy.sort (RouteCompareByName ());
990 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
991 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
994 if (citems.size() == n_with_separator) {
995 /* no routes added; remove the separator */
999 citems.push_back (SeparatorElem());
1000 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1003 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1004 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1009 citems.push_back (SeparatorElem());
1010 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1012 input_menu.popup (1, ev->time);
1023 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1025 if (ignore_toggle) {
1029 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1031 if (std::find (current.begin(), current.end(), c) == current.end()) {
1032 _route->input()->connect_ports_to_bundle (c, true, this);
1034 _route->input()->disconnect_ports_from_bundle (c, this);
1039 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1041 if (ignore_toggle) {
1045 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1047 if (std::find (current.begin(), current.end(), c) == current.end()) {
1048 _route->output()->connect_ports_to_bundle (c, true, this);
1050 _route->output()->disconnect_ports_from_bundle (c, this);
1055 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1057 using namespace Menu_Helpers;
1059 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1063 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1064 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1068 if (i != input_menu_bundles.end()) {
1072 input_menu_bundles.push_back (b);
1074 MenuList& citems = input_menu.items();
1076 std::string n = b->name ();
1077 replace_all (n, "_", " ");
1079 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1083 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1085 using namespace Menu_Helpers;
1087 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1091 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1092 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1096 if (i != output_menu_bundles.end()) {
1100 output_menu_bundles.push_back (b);
1102 MenuList& citems = output_menu.items();
1104 std::string n = b->name ();
1105 replace_all (n, "_", " ");
1107 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1111 MixerStrip::update_diskstream_display ()
1113 if (is_track() && input_selector) {
1114 input_selector->hide_all ();
1117 route_color_changed ();
1121 MixerStrip::connect_to_pan ()
1123 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1125 panstate_connection.disconnect ();
1126 panstyle_connection.disconnect ();
1128 if (!_route->panner()) {
1132 boost::shared_ptr<Pannable> p = _route->pannable ();
1134 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1135 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1137 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1138 * However, that only works a panner was previously set.
1140 * PannerUI must remain subscribed to _panshell->Changed() in case
1141 * we switch the panner eg. AUX-Send and back
1142 * _route->panner_shell()->Changed() vs _panshell->Changed
1144 if (panners._panner == 0) {
1145 panners.panshell_changed ();
1147 update_panner_choices();
1151 MixerStrip::update_panner_choices ()
1153 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1154 if (!_route->panner_shell()) { return; }
1156 uint32_t in = _route->output()->n_ports().n_audio();
1158 if (_route->panner()) {
1159 in = _route->panner()->in().n_audio();
1162 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1166 * Output port labelling
1167 * =====================
1169 * Case 1: Each output has one connection, all connections are to system:playback_%i
1170 * out 1 -> system:playback_1
1171 * out 2 -> system:playback_2
1172 * out 3 -> system:playback_3
1175 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1176 * out 1 -> ardour:track_x/in 1
1177 * out 2 -> ardour:track_x/in 2
1178 * Display as: track_x
1180 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1181 * out 1 -> program x:foo
1182 * out 2 -> program x:foo
1183 * Display as: program x
1185 * Case 4: No connections (Disconnected)
1188 * Default case (unusual routing):
1189 * Display as: *number of connections*
1193 * .-----------------------------------------------.
1195 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1196 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1197 * '-----------------------------------------------'
1198 * .-----------------------------------------------.
1201 * '-----------------------------------------------'
1205 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1209 boost::shared_ptr<Port> port;
1210 vector<string> port_connections;
1212 uint32_t total_connection_count = 0;
1213 uint32_t io_connection_count = 0;
1214 uint32_t ardour_connection_count = 0;
1215 uint32_t system_connection_count = 0;
1216 uint32_t other_connection_count = 0;
1217 uint32_t typed_connection_count = 0;
1219 ostringstream label;
1221 bool have_label = false;
1222 bool each_io_has_one_connection = true;
1224 string connection_name;
1225 string ardour_track_name;
1226 string other_connection_type;
1227 string system_ports;
1230 ostringstream tooltip;
1231 char * tooltip_cstr;
1233 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1234 DataType dt = DataType::AUDIO;
1235 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1236 dt = DataType::MIDI;
1237 // avoid further confusion with Midi-tracks that have a synth.
1238 // Audio-ports may be connected, but button says "Disconnected"
1239 tooltip << _("MIDI ");
1243 io_count = route->n_inputs().n_total();
1244 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1246 io_count = route->n_outputs().n_total();
1247 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1251 for (io_index = 0; io_index < io_count; ++io_index) {
1253 port = route->input()->nth (io_index);
1255 port = route->output()->nth (io_index);
1258 port_connections.clear ();
1259 port->get_connections(port_connections);
1261 //ignore any port connections that don't match our DataType
1262 if (port->type() != dt) {
1263 if (!port_connections.empty()) {
1264 ++typed_connection_count;
1269 io_connection_count = 0;
1271 if (!port_connections.empty()) {
1272 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1274 string& connection_name (*i);
1276 if (connection_name.find("system:") == 0) {
1277 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1280 if (io_connection_count == 0) {
1281 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1283 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1286 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1289 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1290 if (ardour_track_name.empty()) {
1291 // "ardour:Master/in 1" -> "ardour:Master/"
1292 string::size_type slash = connection_name.find("/");
1293 if (slash != string::npos) {
1294 ardour_track_name = connection_name.substr(0, slash + 1);
1298 if (connection_name.find(ardour_track_name) == 0) {
1299 ++ardour_connection_count;
1301 } else if (!pn.empty()) {
1302 if (system_ports.empty()) {
1305 system_ports += "/" + pn;
1307 if (connection_name.find("system:") == 0) {
1308 ++system_connection_count;
1310 } else if (connection_name.find("system:midi_") == 0) {
1312 // "system:midi_capture_123" -> "123"
1313 system_port = "M " + connection_name.substr(20);
1315 // "system:midi_playback_123" -> "123"
1316 system_port = "M " + connection_name.substr(21);
1319 if (system_ports.empty()) {
1320 system_ports += system_port;
1322 system_ports += "/" + system_port;
1325 ++system_connection_count;
1327 } else if (connection_name.find("system:") == 0) {
1329 // "system:capture_123" -> "123"
1330 system_port = connection_name.substr(15);
1332 // "system:playback_123" -> "123"
1333 system_port = connection_name.substr(16);
1336 if (system_ports.empty()) {
1337 system_ports += system_port;
1339 system_ports += "/" + system_port;
1342 ++system_connection_count;
1344 if (other_connection_type.empty()) {
1345 // "jamin:in 1" -> "jamin:"
1346 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1349 if (connection_name.find(other_connection_type) == 0) {
1350 ++other_connection_count;
1354 ++total_connection_count;
1355 ++io_connection_count;
1359 if (io_connection_count != 1) {
1360 each_io_has_one_connection = false;
1364 if (total_connection_count == 0) {
1365 tooltip << endl << _("Disconnected");
1368 tooltip_cstr = new char[tooltip.str().size() + 1];
1369 strcpy(tooltip_cstr, tooltip.str().c_str());
1372 set_tooltip (&input_button, tooltip_cstr);
1374 set_tooltip (&output_button, tooltip_cstr);
1377 delete [] tooltip_cstr;
1379 if (each_io_has_one_connection) {
1380 if (total_connection_count == ardour_connection_count) {
1381 // all connections are to the same track in ardour
1382 // "ardour:Master/" -> "Master"
1383 string::size_type slash = ardour_track_name.find("/");
1384 if (slash != string::npos) {
1385 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1386 label << ardour_track_name.substr (ppps, slash - ppps);
1390 else if (total_connection_count == system_connection_count) {
1391 // all connections are to system ports
1392 label << system_ports;
1395 else if (total_connection_count == other_connection_count) {
1396 // all connections are to the same external program eg jamin
1397 // "jamin:" -> "jamin"
1398 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1404 if (total_connection_count == 0) {
1408 // Odd configuration
1409 label << "*" << total_connection_count << "*";
1411 if (typed_connection_count > 0) {
1412 label << "\u2295"; // circled plus
1417 input_button.set_text (label.str());
1419 output_button.set_text (label.str());
1424 MixerStrip::update_input_display ()
1426 update_io_button (_route, _width, true);
1427 panners.setup_pan ();
1429 if (has_audio_outputs ()) {
1430 panners.show_all ();
1432 panners.hide_all ();
1438 MixerStrip::update_output_display ()
1440 update_io_button (_route, _width, false);
1441 gpm.setup_meters ();
1442 panners.setup_pan ();
1444 if (has_audio_outputs ()) {
1445 panners.show_all ();
1447 panners.hide_all ();
1452 MixerStrip::fast_update ()
1454 gpm.update_meters ();
1458 MixerStrip::diskstream_changed ()
1460 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1464 MixerStrip::io_changed_proxy ()
1466 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1470 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1472 boost::shared_ptr<Port> a = wa.lock ();
1473 boost::shared_ptr<Port> b = wb.lock ();
1475 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1476 update_input_display ();
1477 set_width_enum (_width, this);
1480 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1481 update_output_display ();
1482 set_width_enum (_width, this);
1487 MixerStrip::setup_comment_button ()
1492 if (_route->comment().empty ()) {
1493 _comment_button.unset_bg (STATE_NORMAL);
1494 _comment_button.set_text (_("Comments"));
1496 _comment_button.modify_bg (STATE_NORMAL, color ());
1497 _comment_button.set_text (_("*Comments*"));
1502 if (_route->comment().empty ()) {
1503 _comment_button.unset_bg (STATE_NORMAL);
1504 _comment_button.set_text (_("Cmt"));
1506 _comment_button.modify_bg (STATE_NORMAL, color ());
1507 _comment_button.set_text (_("*Cmt*"));
1513 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1519 MixerStrip::select_route_group (GdkEventButton *ev)
1521 using namespace Menu_Helpers;
1523 if (ev->button == 1) {
1525 if (group_menu == 0) {
1527 PropertyList* plist = new PropertyList();
1529 plist->add (Properties::gain, true);
1530 plist->add (Properties::mute, true);
1531 plist->add (Properties::solo, true);
1533 group_menu = new RouteGroupMenu (_session, plist);
1537 r.push_back (route ());
1538 group_menu->build (r);
1539 group_menu->menu()->popup (1, ev->time);
1546 MixerStrip::route_group_changed ()
1548 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1550 RouteGroup *rg = _route->route_group();
1553 group_button.set_text (PBD::short_version (rg->name(), 5));
1557 group_button.set_text (_("Grp"));
1560 group_button.set_text (_("~G"));
1567 MixerStrip::route_color_changed ()
1569 name_button.modify_bg (STATE_NORMAL, color());
1570 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1571 reset_strip_style ();
1575 MixerStrip::show_passthru_color ()
1577 reset_strip_style ();
1581 MixerStrip::build_route_ops_menu ()
1583 using namespace Menu_Helpers;
1584 route_ops_menu = new Menu;
1585 route_ops_menu->set_name ("ArdourContextMenu");
1587 MenuList& items = route_ops_menu->items();
1589 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1591 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1593 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1595 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1597 items.push_back (SeparatorElem());
1599 if (!_route->is_master()) {
1600 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1602 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1603 rename_menu_item = &items.back();
1605 items.push_back (SeparatorElem());
1606 items.push_back (CheckMenuElem (_("Active")));
1607 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1608 i->set_active (_route->active());
1609 i->set_sensitive(! _session->transport_rolling());
1610 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1612 if (!Profile->get_mixbus ()) {
1613 items.push_back (SeparatorElem());
1614 items.push_back (CheckMenuElem (_("Strict I/O")));
1615 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1616 i->set_active (_route->strict_io());
1617 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1620 if (1 /* TODO IFF >= 1 plugin-insert */) {
1621 items.push_back (SeparatorElem());
1622 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1625 items.push_back (SeparatorElem());
1626 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1628 items.push_back (SeparatorElem());
1629 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1630 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1631 denormal_menu_item->set_active (_route->denormal_protection());
1633 items.push_back (SeparatorElem());
1634 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1637 /* note that this relies on selection being shared across editor and
1638 mixer (or global to the backend, in the future), which is the only
1639 sane thing for users anyway.
1642 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1644 Selection& selection (PublicEditor::instance().get_selection());
1645 if (!selection.selected (rtav)) {
1646 selection.set (rtav);
1649 if (!_route->is_master()) {
1650 items.push_back (SeparatorElem());
1651 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1654 items.push_back (SeparatorElem());
1655 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1661 MixerStrip::name_button_button_press (GdkEventButton* ev)
1663 if (ev->button == 3) {
1664 list_route_operations ();
1666 /* do not allow rename if the track is record-enabled */
1667 rename_menu_item->set_sensitive (!_route->record_enabled());
1668 route_ops_menu->popup (1, ev->time);
1677 MixerStrip::name_button_button_release (GdkEventButton* ev)
1679 if (ev->button == 1) {
1680 list_route_operations ();
1682 /* do not allow rename if the track is record-enabled */
1683 rename_menu_item->set_sensitive (!_route->record_enabled());
1684 route_ops_menu->popup (1, ev->time);
1691 MixerStrip::number_button_button_press (GdkEventButton* ev)
1693 if ( ev->button == 3 ) {
1694 list_route_operations ();
1696 /* do not allow rename if the track is record-enabled */
1697 rename_menu_item->set_sensitive (!_route->record_enabled());
1698 route_ops_menu->popup (1, ev->time);
1707 MixerStrip::list_route_operations ()
1709 delete route_ops_menu;
1710 build_route_ops_menu ();
1714 MixerStrip::set_selected (bool yn)
1716 AxisView::set_selected (yn);
1718 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1719 global_frame.set_name ("MixerStripSelectedFrame");
1721 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1722 global_frame.set_name ("MixerStripFrame");
1724 global_frame.queue_draw ();
1727 // processor_box.deselect_all_processors();
1731 MixerStrip::property_changed (const PropertyChange& what_changed)
1733 RouteUI::property_changed (what_changed);
1735 if (what_changed.contains (ARDOUR::Properties::name)) {
1741 MixerStrip::name_changed ()
1745 name_button.set_text (_route->name());
1748 name_button.set_text (PBD::short_version (_route->name(), 5));
1752 set_tooltip (name_button, _route->name());
1754 if (_session->config.get_track_name_number()) {
1755 const int64_t track_number = _route->track_number ();
1756 if (track_number == 0) {
1757 number_label.set_text ("-");
1759 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1762 number_label.set_text ("");
1767 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1769 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1773 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1775 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1779 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1781 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1785 MixerStrip::width_button_pressed (GdkEventButton* ev)
1787 if (ev->button != 1) {
1791 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1794 _mixer.set_strip_width (Narrow, true);
1798 _mixer.set_strip_width (Wide, true);
1804 set_width_enum (Narrow, this);
1807 set_width_enum (Wide, this);
1816 MixerStrip::hide_clicked ()
1818 // LAME fix to reset the button status for when it is redisplayed (part 1)
1819 hide_button.set_sensitive(false);
1822 Hiding(); /* EMIT_SIGNAL */
1824 _mixer.hide_strip (this);
1828 hide_button.set_sensitive(true);
1832 MixerStrip::set_embedded (bool yn)
1838 MixerStrip::map_frozen ()
1840 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1842 boost::shared_ptr<AudioTrack> at = audio_track();
1845 switch (at->freeze_state()) {
1846 case AudioTrack::Frozen:
1847 processor_box.set_sensitive (false);
1848 hide_redirect_editors ();
1851 processor_box.set_sensitive (true);
1852 // XXX need some way, maybe, to retoggle redirect editors
1856 processor_box.set_sensitive (true);
1858 RouteUI::map_frozen ();
1862 MixerStrip::hide_redirect_editors ()
1864 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1868 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1870 boost::shared_ptr<Processor> processor (p.lock ());
1875 Gtk::Window* w = processor_box.get_processor_ui (processor);
1883 MixerStrip::reset_strip_style ()
1885 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1887 gpm.set_fader_name ("SendStripBase");
1891 if (is_midi_track()) {
1892 if (_route->active()) {
1893 set_name ("MidiTrackStripBase");
1895 set_name ("MidiTrackStripBaseInactive");
1897 gpm.set_fader_name ("MidiTrackFader");
1898 } else if (is_audio_track()) {
1899 if (_route->active()) {
1900 set_name ("AudioTrackStripBase");
1902 set_name ("AudioTrackStripBaseInactive");
1904 gpm.set_fader_name ("AudioTrackFader");
1906 if (_route->active()) {
1907 set_name ("AudioBusStripBase");
1909 set_name ("AudioBusStripBaseInactive");
1911 gpm.set_fader_name ("AudioBusFader");
1913 /* (no MIDI busses yet) */
1920 MixerStrip::engine_stopped ()
1925 MixerStrip::engine_running ()
1930 MixerStrip::meter_point_string (MeterPoint mp)
1943 case MeterPostFader:
1960 return S_("Meter|In");
1964 return S_("Meter|Pr");
1967 case MeterPostFader:
1968 return S_("Meter|Po");
1972 return S_("Meter|O");
1977 return S_("Meter|C");
1986 /** Called when the monitor-section state */
1988 MixerStrip::monitor_changed ()
1990 assert (monitor_section_button);
1991 if (_session->monitor_active()) {
1992 monitor_section_button->set_name ("master monitor section button active");
1994 monitor_section_button->set_name ("master monitor section button normal");
1998 /** Called when the metering point has changed */
2000 MixerStrip::meter_changed ()
2002 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2003 gpm.setup_meters ();
2004 // reset peak when meter point changes
2005 gpm.reset_peak_display();
2008 /** The bus that we are displaying sends to has changed, or been turned off.
2009 * @param send_to New bus that we are displaying sends to, or 0.
2012 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2014 RouteUI::bus_send_display_changed (send_to);
2017 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2022 revert_to_default_display ();
2025 revert_to_default_display ();
2030 MixerStrip::drop_send ()
2032 boost::shared_ptr<Send> current_send;
2034 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2035 current_send->set_metering (false);
2038 send_gone_connection.disconnect ();
2039 input_button.set_sensitive (true);
2040 output_button.set_sensitive (true);
2041 group_button.set_sensitive (true);
2042 set_invert_sensitive (true);
2043 meter_point_button.set_sensitive (true);
2044 mute_button->set_sensitive (true);
2045 solo_button->set_sensitive (true);
2046 solo_isolated_led->set_sensitive (true);
2047 solo_safe_led->set_sensitive (true);
2048 monitor_input_button->set_sensitive (true);
2049 monitor_disk_button->set_sensitive (true);
2050 _comment_button.set_sensitive (true);
2051 RouteUI::check_rec_enable_sensitivity ();
2052 set_button_names (); // update solo button visual state
2056 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2058 _current_delivery = d;
2059 DeliveryChanged (_current_delivery);
2063 MixerStrip::show_send (boost::shared_ptr<Send> send)
2069 set_current_delivery (send);
2071 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2072 send->set_metering (true);
2073 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2075 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2076 gain_meter().setup_meters ();
2078 uint32_t const in = _current_delivery->pans_required();
2079 uint32_t const out = _current_delivery->pan_outs();
2081 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2082 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2083 panner_ui().setup_pan ();
2084 panner_ui().set_send_drawing_mode (true);
2085 panner_ui().show_all ();
2087 input_button.set_sensitive (false);
2088 group_button.set_sensitive (false);
2089 set_invert_sensitive (false);
2090 meter_point_button.set_sensitive (false);
2091 mute_button->set_sensitive (false);
2092 solo_button->set_sensitive (false);
2093 rec_enable_button->set_sensitive (false);
2094 solo_isolated_led->set_sensitive (false);
2095 solo_safe_led->set_sensitive (false);
2096 monitor_input_button->set_sensitive (false);
2097 monitor_disk_button->set_sensitive (false);
2098 _comment_button.set_sensitive (false);
2100 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2101 output_button.set_sensitive (false);
2104 reset_strip_style ();
2108 MixerStrip::revert_to_default_display ()
2112 set_current_delivery (_route->main_outs ());
2114 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2115 gain_meter().setup_meters ();
2117 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2118 update_panner_choices();
2119 panner_ui().setup_pan ();
2120 panner_ui().set_send_drawing_mode (false);
2122 if (has_audio_outputs ()) {
2123 panners.show_all ();
2125 panners.hide_all ();
2128 reset_strip_style ();
2132 MixerStrip::set_button_names ()
2136 mute_button->set_text (_("Mute"));
2137 monitor_input_button->set_text (_("In"));
2138 monitor_disk_button->set_text (_("Disk"));
2139 if (monitor_section_button) {
2140 monitor_section_button->set_text (_("Mon"));
2143 if (_route && _route->solo_safe()) {
2144 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2146 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2148 if (!Config->get_solo_control_is_listen_control()) {
2149 solo_button->set_text (_("Solo"));
2151 switch (Config->get_listen_position()) {
2152 case AfterFaderListen:
2153 solo_button->set_text (_("AFL"));
2155 case PreFaderListen:
2156 solo_button->set_text (_("PFL"));
2160 solo_isolated_led->set_text (_("Iso"));
2161 solo_safe_led->set_text (S_("SoloLock|Lock"));
2165 mute_button->set_text (S_("Mute|M"));
2166 monitor_input_button->set_text (S_("MonitorInput|I"));
2167 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2168 if (monitor_section_button) {
2169 monitor_section_button->set_text (S_("Mon|O"));
2172 if (_route && _route->solo_safe()) {
2173 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2175 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2177 if (!Config->get_solo_control_is_listen_control()) {
2178 solo_button->set_text (S_("Solo|S"));
2180 switch (Config->get_listen_position()) {
2181 case AfterFaderListen:
2182 solo_button->set_text (S_("AfterFader|A"));
2184 case PreFaderListen:
2185 solo_button->set_text (S_("Prefader|P"));
2190 solo_isolated_led->set_text (S_("SoloIso|I"));
2191 solo_safe_led->set_text (S_("SoloLock|L"));
2196 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2198 meter_point_button.set_text ("");
2203 MixerStrip::plugin_selector()
2205 return _mixer.plugin_selector();
2209 MixerStrip::hide_things ()
2211 processor_box.hide_things ();
2215 MixerStrip::input_active_button_press (GdkEventButton*)
2217 /* nothing happens on press */
2222 MixerStrip::input_active_button_release (GdkEventButton* ev)
2224 boost::shared_ptr<MidiTrack> mt = midi_track ();
2230 boost::shared_ptr<RouteList> rl (new RouteList);
2232 rl->push_back (route());
2234 _session->set_exclusive_input_active (rl, !mt->input_active(),
2235 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2241 MixerStrip::midi_input_status_changed ()
2243 if (midi_input_enable_button) {
2244 boost::shared_ptr<MidiTrack> mt = midi_track ();
2246 midi_input_enable_button->set_active (mt->input_active ());
2251 MixerStrip::state_id () const
2253 return string_compose ("strip %1", _route->id().to_s());
2257 MixerStrip::parameter_changed (string p)
2259 if (p == _visibility.get_state_name()) {
2260 /* The user has made changes to the mixer strip visibility, so get
2261 our VisibilityGroup to reflect these changes in our widgets.
2263 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2265 else if (p == "track-name-number") {
2268 else if (p == "use-monitor-bus") {
2269 if (monitor_section_button) {
2270 if (mute_button->get_parent()) {
2271 mute_button->get_parent()->remove(*mute_button);
2273 if (monitor_section_button->get_parent()) {
2274 monitor_section_button->get_parent()->remove(*monitor_section_button);
2276 if (Config->get_use_monitor_bus ()) {
2277 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2278 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2279 mute_button->show();
2280 monitor_section_button->show();
2282 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2283 mute_button->show();
2289 /** Called to decide whether the solo isolate / solo lock button visibility should
2290 * be overridden from that configured by the user. We do this for the master bus.
2292 * @return optional value that is present if visibility state should be overridden.
2294 boost::optional<bool>
2295 MixerStrip::override_solo_visibility () const
2297 if (_route && _route->is_master ()) {
2298 return boost::optional<bool> (false);
2301 return boost::optional<bool> ();
2305 MixerStrip::add_input_port (DataType t)
2307 _route->input()->add_port ("", this, t);
2311 MixerStrip::add_output_port (DataType t)
2313 _route->output()->add_port ("", this, t);
2317 MixerStrip::route_active_changed ()
2319 reset_strip_style ();
2323 MixerStrip::copy_processors ()
2325 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2329 MixerStrip::cut_processors ()
2331 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2335 MixerStrip::paste_processors ()
2337 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2341 MixerStrip::select_all_processors ()
2343 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2347 MixerStrip::deselect_all_processors ()
2349 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2353 MixerStrip::delete_processors ()
2355 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2359 MixerStrip::toggle_processors ()
2361 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2365 MixerStrip::ab_plugins ()
2367 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2371 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2373 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2376 if (ev->button == 3) {
2377 popup_level_meter_menu (ev);
2385 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2387 using namespace Gtk::Menu_Helpers;
2389 Gtk::Menu* m = manage (new Menu);
2390 MenuList& items = m->items ();
2392 RadioMenuItem::Group group;
2394 _suspend_menu_callbacks = true;
2395 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2396 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2397 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2398 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2399 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2401 if (gpm.meter_channels().n_audio() == 0) {
2402 m->popup (ev->button, ev->time);
2403 _suspend_menu_callbacks = false;
2407 RadioMenuItem::Group tgroup;
2408 items.push_back (SeparatorElem());
2410 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2411 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2412 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2413 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2414 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2415 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2416 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2417 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2418 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2419 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2420 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2423 if (_route->is_master()) {
2426 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2427 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2428 /* non-master bus */
2431 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2438 MeterType cmt = _route->meter_type();
2439 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2441 items.push_back (SeparatorElem());
2442 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2443 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2444 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2445 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2446 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2447 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2449 m->popup (ev->button, ev->time);
2450 _suspend_menu_callbacks = false;
2454 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2455 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2457 using namespace Menu_Helpers;
2459 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2460 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2461 i->set_active (_route->meter_point() == point);
2465 MixerStrip::set_meter_point (MeterPoint p)
2467 if (_suspend_menu_callbacks) return;
2468 _route->set_meter_point (p);
2472 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2473 RadioMenuItem::Group& group, string const & name, MeterType type)
2475 using namespace Menu_Helpers;
2477 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2478 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2479 i->set_active (_route->meter_type() == type);
2483 MixerStrip::set_meter_type (MeterType t)
2485 if (_suspend_menu_callbacks) return;
2490 MixerStrip::vca_menu_toggle (CheckMenuItem* menuitem, uint32_t n)
2496 boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2503 /* if this strip is not selected, add it before carrying out
2504 changes to assignment. the user probably didn't notice
2505 that they were clicking on an unselected track.
2507 _mixer.select_strip (*this);
2510 if (!menuitem->get_active()) {
2511 _mixer.do_vca_unassign (vca);
2513 _mixer.do_vca_assign (vca);
2518 MixerStrip::vca_assign (boost::shared_ptr<VCA> vca)
2520 if (!vca || !_route) {
2524 _route->vca_assign (vca);
2528 MixerStrip::vca_unassign (boost::shared_ptr<VCA> vca)
2534 _route->vca_unassign (vca);
2538 MixerStrip::vca_button_release (GdkEventButton* ev)
2540 using namespace Gtk::Menu_Helpers;
2546 /* primary click only */
2548 if (ev->button != 1) {
2553 /* no route - nothing to do */
2557 VCAList vcas (_session->vca_manager().vcas());
2560 /* XXX should probably show a message saying "No VCA masters" */
2564 Menu* menu = new Menu;
2565 MenuList& items = menu->items();
2567 items.push_back (MenuElem (_("Unassign"), sigc::bind (sigc::mem_fun (_mixer, &Mixer_UI::do_vca_unassign), boost::shared_ptr<VCA>())));
2569 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2570 items.push_back (CheckMenuElem ((*v)->name()));
2571 CheckMenuItem* item = dynamic_cast<CheckMenuItem*> (&items.back());
2572 if (_route->slaved_to (*v)) {
2573 item->set_active (true);
2575 item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), item, (*v)->number()));
2578 menu->popup (1, ev->time);