2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <sigc++/bind.h>
25 #include "pbd/convert.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/replace_all.h"
28 #include "pbd/stacktrace.h"
30 #include <gtkmm2ext/gtk_ui.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/choice.h>
33 #include <gtkmm2ext/doi.h>
34 #include <gtkmm2ext/slider_controller.h>
35 #include <gtkmm2ext/bindable_button.h>
37 #include "ardour/amp.h"
38 #include "ardour/audio_track.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/internal_send.h"
41 #include "ardour/meter.h"
42 #include "ardour/midi_track.h"
43 #include "ardour/pannable.h"
44 #include "ardour/panner.h"
45 #include "ardour/panner_shell.h"
46 #include "ardour/panner_manager.h"
47 #include "ardour/port.h"
48 #include "ardour/profile.h"
49 #include "ardour/route.h"
50 #include "ardour/route_group.h"
51 #include "ardour/send.h"
52 #include "ardour/session.h"
53 #include "ardour/types.h"
54 #include "ardour/user_bundle.h"
55 #include "ardour/vca.h"
56 #include "ardour/vca_manager.h"
58 #include "ardour_window.h"
59 #include "mixer_strip.h"
62 #include "ardour_button.h"
63 #include "public_editor.h"
65 #include "io_selector.h"
67 #include "gui_thread.h"
68 #include "route_group_menu.h"
69 #include "meter_patterns.h"
71 #include "ui_config.h"
75 using namespace ARDOUR;
76 using namespace ARDOUR_UI_UTILS;
79 using namespace Gtkmm2ext;
81 using namespace ArdourMeter;
83 MixerStrip* MixerStrip::_entered_mixer_strip;
84 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
86 static const uint32_t n_vca_buttons = 4;
88 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
92 , _mixer_owned (in_mixer)
93 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
96 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
97 , rec_mon_table (2, 2)
98 , solo_iso_table (1, 2)
99 , mute_solo_table (1, 2)
100 , bottom_button_table (1, 3)
102 , meter_point_button (_("pre"))
103 , monitor_section_button (0)
104 , midi_input_enable_button (0)
105 , _comment_button (_("Comments"))
106 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
107 , _visibility (X_("mixer-element-visibility"))
112 /* the editor mixer strip: don't destroy it every time
113 the underlying route goes away.
116 self_destruct = false;
120 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
124 , _mixer_owned (in_mixer)
125 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
128 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
129 , rec_mon_table (2, 2)
130 , solo_iso_table (1, 2)
131 , mute_solo_table (1, 2)
132 , bottom_button_table (1, 3)
134 , meter_point_button (_("pre"))
135 , monitor_section_button (0)
136 , midi_input_enable_button (0)
137 , _comment_button (_("Comments"))
138 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
139 , _visibility (X_("mixer-element-visibility"))
148 _entered_mixer_strip= 0;
151 ignore_comment_edit = false;
152 ignore_toggle = false;
157 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
158 longest_label = "longest label";
160 string t = _("Click to toggle the width of this mixer strip.");
162 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
165 width_button.set_icon (ArdourIcon::StripWidth);
166 set_tooltip (width_button, t);
168 hide_button.set_icon (ArdourIcon::CloseCross);
169 set_tooltip (&hide_button, _("Hide this mixer strip"));
171 input_button_box.set_spacing(2);
173 input_button.set_text (_("Input"));
174 input_button.set_name ("mixer strip button");
175 input_button_box.pack_start (input_button, true, true);
177 output_button.set_text (_("Output"));
178 output_button.set_name ("mixer strip button");
180 set_tooltip (&meter_point_button, _("Click to select metering point"));
181 meter_point_button.set_name ("mixer strip button");
183 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
185 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
186 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
188 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
190 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
191 solo_isolated_led->show ();
192 solo_isolated_led->set_no_show_all (true);
193 solo_isolated_led->set_name (X_("solo isolate"));
194 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
195 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
196 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
198 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
199 solo_safe_led->show ();
200 solo_safe_led->set_no_show_all (true);
201 solo_safe_led->set_name (X_("solo safe"));
202 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
203 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
204 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
206 solo_safe_led->set_text (S_("SoloLock|Lock"));
207 solo_isolated_led->set_text (_("Iso"));
209 solo_iso_table.set_homogeneous (true);
210 solo_iso_table.set_spacings (2);
211 if (!ARDOUR::Profile->get_trx()) {
212 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
213 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
215 solo_iso_table.show ();
217 vca_table.set_homogeneous (true);
218 vca_table.set_spacings (1);
219 for (uint32_t n = 0; n < n_vca_buttons; ++n) {
220 ArdourButton* v = manage (new ArdourButton (ArdourButton::default_elements));
221 vca_buttons.push_back (v); /* no ownership transfer, button is managed by its container */
222 v->set_no_show_all (true);
223 v->set_name (X_("vca assign"));
224 v->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
225 v->signal_button_release_event().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_button_release), n), false);
226 UI::instance()->set_tip (*v, string_compose (_("VCA %1 assign"), n));
227 v->set_text (_("v."));
229 vca_table.attach (*v, n, n+1, 0, 1);
233 rec_mon_table.set_homogeneous (true);
234 rec_mon_table.set_row_spacings (2);
235 rec_mon_table.set_col_spacings (2);
236 if (ARDOUR::Profile->get_mixbus()) {
237 rec_mon_table.resize (1, 3);
238 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
239 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
240 } else if (!ARDOUR::Profile->get_trx()) {
241 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
242 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
244 rec_mon_table.show ();
246 if (solo_isolated_led) {
247 button_size_group->add_widget (*solo_isolated_led);
250 button_size_group->add_widget (*solo_safe_led);
253 if (!ARDOUR::Profile->get_mixbus()) {
254 if (rec_enable_button) {
255 button_size_group->add_widget (*rec_enable_button);
257 if (monitor_disk_button) {
258 button_size_group->add_widget (*monitor_disk_button);
260 if (monitor_input_button) {
261 button_size_group->add_widget (*monitor_input_button);
265 mute_solo_table.set_homogeneous (true);
266 mute_solo_table.set_spacings (2);
268 bottom_button_table.set_spacings (2);
269 bottom_button_table.set_homogeneous (true);
270 bottom_button_table.attach (group_button, 1, 2, 0, 1);
271 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
273 name_button.set_name ("mixer strip button");
274 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
275 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
277 set_tooltip (&group_button, _("Mix group"));
278 group_button.set_name ("mixer strip button");
280 _comment_button.set_name (X_("mixer strip button"));
281 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
283 // TODO implement ArdourKnob::on_size_request properly
284 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
285 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
287 trim_control.set_tooltip_prefix (_("Trim: "));
288 trim_control.set_name ("trim knob");
289 trim_control.set_no_show_all (true);
290 input_button_box.pack_start (trim_control, false, false);
292 global_vpacker.set_border_width (1);
293 global_vpacker.set_spacing (0);
295 width_button.set_name ("mixer strip button");
296 hide_button.set_name ("mixer strip button");
298 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
299 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
301 // width_hide_box.set_border_width (1);
302 width_hide_box.set_spacing (2);
303 width_hide_box.pack_start (width_button, false, true);
304 width_hide_box.pack_start (number_label, true, true);
305 width_hide_box.pack_end (hide_button, false, true);
307 number_label.set_text ("-");
308 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
309 number_label.set_no_show_all ();
310 number_label.set_name ("tracknumber label");
311 number_label.set_fixed_colors (0x80808080, 0x80808080);
312 number_label.set_alignment (.5, .5);
313 number_label.set_fallthrough_to_parent (true);
315 global_vpacker.set_spacing (2);
316 if (!ARDOUR::Profile->get_trx()) {
317 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
318 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
319 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
320 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
321 global_vpacker.pack_start (processor_box, true, true);
323 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
324 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
325 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
326 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
327 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
328 global_vpacker.pack_start (vca_table, Gtk::PACK_SHRINK);
329 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
330 if (!ARDOUR::Profile->get_trx()) {
331 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
332 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
334 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
337 global_frame.add (global_vpacker);
338 global_frame.set_shadow_type (Gtk::SHADOW_IN);
339 global_frame.set_name ("BaseFrame");
343 /* force setting of visible selected status */
346 set_selected (false);
351 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
352 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
354 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
355 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
356 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
358 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
359 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
361 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
362 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
363 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
365 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
367 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
368 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
370 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
374 /* start off as a passthru strip. we'll correct this, if necessary,
375 in update_diskstream_display().
378 /* start off as a passthru strip. we'll correct this, if necessary,
379 in update_diskstream_display().
382 if (is_midi_track()) {
383 set_name ("MidiTrackStripBase");
385 set_name ("AudioTrackStripBase");
388 add_events (Gdk::BUTTON_RELEASE_MASK|
389 Gdk::ENTER_NOTIFY_MASK|
390 Gdk::LEAVE_NOTIFY_MASK|
392 Gdk::KEY_RELEASE_MASK);
394 set_flags (get_flags() | Gtk::CAN_FOCUS);
396 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
397 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
400 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
401 must be the same as those used in RCOptionEditor so that the configuration changes
402 are recognised when they occur.
404 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
405 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
406 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
407 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
408 _visibility.add (&output_button, X_("Output"), _("Output"), false);
409 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
410 _visibility.add (&vca_table, X_("VCA"), _("VCA Assigns"), false);
412 parameter_changed (X_("mixer-element-visibility"));
413 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
414 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
415 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
417 //watch for mouse enter/exit so we can do some stuff
418 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
419 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
421 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
424 MixerStrip::~MixerStrip ()
426 CatchDeletion (this);
428 if (this ==_entered_mixer_strip)
429 _entered_mixer_strip = NULL;
433 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
435 _entered_mixer_strip = this;
437 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
438 //because the mixerstrip control is a parent that encompasses the strip
439 deselect_all_processors();
445 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
447 //if we have moved outside our strip, but not into a child view, then deselect ourselves
448 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
449 _entered_mixer_strip= 0;
451 //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
452 gpm.gain_display.set_sensitive(false);
454 gpm.gain_display.set_sensitive(true);
456 //if we leave this mixer strip we need to clear out any selections
457 //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
464 MixerStrip::set_route (boost::shared_ptr<Route> rt)
466 //the rec/monitor stuff only shows up for tracks.
467 //the show_sends only shows up for buses.
468 //remove them all here, and we may add them back later
469 if (show_sends_button->get_parent()) {
470 rec_mon_table.remove (*show_sends_button);
472 if (rec_enable_button->get_parent()) {
473 rec_mon_table.remove (*rec_enable_button);
475 if (monitor_input_button->get_parent()) {
476 rec_mon_table.remove (*monitor_input_button);
478 if (monitor_disk_button->get_parent()) {
479 rec_mon_table.remove (*monitor_disk_button);
481 if (group_button.get_parent()) {
482 bottom_button_table.remove (group_button);
485 RouteUI::set_route (rt);
487 /* ProcessorBox needs access to _route so that it can read
490 processor_box.set_route (rt);
492 revert_to_default_display ();
494 /* unpack these from the parent and stuff them into our own
498 if (gpm.peak_display.get_parent()) {
499 gpm.peak_display.get_parent()->remove (gpm.peak_display);
501 if (gpm.gain_display.get_parent()) {
502 gpm.gain_display.get_parent()->remove (gpm.gain_display);
505 gpm.set_type (rt->meter_type());
507 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
508 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
510 if (solo_button->get_parent()) {
511 mute_solo_table.remove (*solo_button);
514 if (mute_button->get_parent()) {
515 mute_solo_table.remove (*mute_button);
518 if (route()->is_master()) {
519 solo_button->hide ();
520 mute_button->show ();
521 rec_mon_table.hide ();
522 if (solo_iso_table.get_parent()) {
523 solo_iso_table.get_parent()->remove(solo_iso_table);
525 if (monitor_section_button == 0) {
526 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
527 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
529 monitor_section_button = manage (new ArdourButton);
531 monitor_section_button->set_related_action (act);
532 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
533 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
534 monitor_section_button->show();
535 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
537 parameter_changed ("use-monitor-bus");
539 bottom_button_table.attach (group_button, 1, 2, 0, 1);
540 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
541 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
542 mute_button->show ();
543 solo_button->show ();
544 rec_mon_table.show ();
547 if (_mixer_owned && route()->is_master() ) {
549 HScrollbar scrollbar;
550 Gtk::Requisition requisition(scrollbar.size_request ());
551 int scrollbar_height = requisition.height;
553 spacer = manage (new EventBox);
554 spacer->set_size_request (-1, scrollbar_height+2);
555 global_vpacker.pack_start (*spacer, false, false);
560 monitor_input_button->show ();
561 monitor_disk_button->show ();
563 monitor_input_button->hide();
564 monitor_disk_button->hide ();
567 if (route()->trim() && route()->trim()->active()) {
568 trim_control.show ();
569 trim_control.set_controllable (route()->trim()->gain_control());
571 trim_control.hide ();
572 boost::shared_ptr<Controllable> none;
573 trim_control.set_controllable (none);
576 if (is_midi_track()) {
577 if (midi_input_enable_button == 0) {
578 midi_input_enable_button = manage (new ArdourButton);
579 midi_input_enable_button->set_name ("midi input button");
580 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
581 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
582 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
583 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
584 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
586 input_button_box.remove (*midi_input_enable_button);
588 /* get current state */
589 midi_input_status_changed ();
590 input_button_box.pack_start (*midi_input_enable_button, false, false);
592 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
594 if (midi_input_enable_button) {
595 /* removal from the container will delete it */
596 input_button_box.remove (*midi_input_enable_button);
597 midi_input_enable_button = 0;
601 if (is_audio_track()) {
602 boost::shared_ptr<AudioTrack> at = audio_track();
603 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
608 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
609 rec_enable_button->show();
611 if (ARDOUR::Profile->get_mixbus()) {
612 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
613 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
614 } else if (ARDOUR::Profile->get_trx()) {
615 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
617 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
618 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
625 if (!_route->is_master()) {
626 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
627 show_sends_button->show();
631 meter_point_button.set_text (meter_point_string (_route->meter_point()));
633 delete route_ops_menu;
636 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
637 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
638 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
639 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
641 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
643 if (_route->panner_shell()) {
644 update_panner_choices();
645 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
648 if (is_audio_track()) {
649 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
652 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
653 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
655 set_stuff_from_route ();
657 /* now force an update of all the various elements */
659 update_mute_display ();
660 update_solo_display ();
663 route_group_changed ();
666 panners.setup_pan ();
668 if (has_audio_outputs ()) {
674 update_diskstream_display ();
675 update_input_display ();
676 update_output_display ();
678 add_events (Gdk::BUTTON_RELEASE_MASK);
680 processor_box.show ();
682 if (!route()->is_master() && !route()->is_monitor()) {
683 /* we don't allow master or control routes to be hidden */
688 gpm.reset_peak_display ();
689 gpm.gain_display.show ();
690 gpm.peak_display.show ();
693 width_hide_box.show();
695 global_vpacker.show();
696 mute_solo_table.show();
697 bottom_button_table.show();
699 meter_point_button.show();
700 input_button_box.show_all();
701 output_button.show();
703 _comment_button.show();
705 gpm.gain_automation_state_button.show();
707 parameter_changed ("mixer-element-visibility");
714 MixerStrip::set_stuff_from_route ()
716 /* if width is not set, it will be set by the MixerUI or editor */
718 string str = gui_property ("strip-width");
720 set_width_enum (Width (string_2_enum (str, _width)), this);
725 MixerStrip::set_width_enum (Width w, void* owner)
727 /* always set the gpm width again, things may be hidden */
730 panners.set_width (w);
732 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
734 _width_owner = owner;
738 if (_width_owner == this) {
739 set_gui_property ("strip-width", enum_2_string (_width));
744 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
749 if (show_sends_button) {
750 show_sends_button->set_text (_("Aux"));
753 gpm.gain_automation_style_button.set_text (
754 gpm.astyle_string(gain_automation->automation_style()));
755 gpm.gain_automation_state_button.set_text (
756 gpm.astate_string(gain_automation->automation_state()));
758 if (_route->panner()) {
759 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
760 panners.astyle_string(_route->panner()->automation_style()));
761 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
762 panners.astate_string(_route->panner()->automation_state()));
766 // panners expect an even number of horiz. pixels
767 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
769 set_size_request (width, -1);
775 if (show_sends_button) {
776 show_sends_button->set_text (_("Snd"));
779 gpm.gain_automation_style_button.set_text (
780 gpm.short_astyle_string(gain_automation->automation_style()));
781 gpm.gain_automation_state_button.set_text (
782 gpm.short_astate_string(gain_automation->automation_state()));
783 gain_meter().setup_meters (); // recalc meter width
785 if (_route->panner()) {
786 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
787 panners.short_astyle_string(_route->panner()->automation_style()));
788 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
789 panners.short_astate_string(_route->panner()->automation_state()));
793 // panners expect an even number of horiz. pixels
794 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
796 set_size_request (width, -1);
801 processor_box.set_width (w);
803 update_input_display ();
804 update_output_display ();
805 setup_comment_button ();
806 route_group_changed ();
812 MixerStrip::set_packed (bool yn)
817 set_gui_property ("visible", true);
819 set_gui_property ("visible", false);
824 struct RouteCompareByName {
825 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
826 return a->name().compare (b->name()) < 0;
831 MixerStrip::output_release (GdkEventButton *ev)
833 switch (ev->button) {
835 edit_output_configuration ();
843 MixerStrip::output_press (GdkEventButton *ev)
845 using namespace Menu_Helpers;
846 if (!_session->engine().connected()) {
847 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
852 MenuList& citems = output_menu.items();
853 switch (ev->button) {
856 return false; //wait for the mouse-up to pop the dialog
860 output_menu.set_name ("ArdourContextMenu");
862 output_menu_bundles.clear ();
864 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
866 citems.push_back (SeparatorElem());
867 uint32_t const n_with_separator = citems.size ();
869 ARDOUR::BundleList current = _route->output()->bundles_connected ();
871 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
873 /* give user bundles first chance at being in the menu */
875 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
876 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
877 maybe_add_bundle_to_output_menu (*i, current);
881 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
882 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
883 maybe_add_bundle_to_output_menu (*i, current);
887 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
888 RouteList copy = *routes;
889 copy.sort (RouteCompareByName ());
890 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
891 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
894 if (citems.size() == n_with_separator) {
895 /* no routes added; remove the separator */
899 if (!ARDOUR::Profile->get_mixbus()) {
900 citems.push_back (SeparatorElem());
902 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
905 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
906 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
912 citems.push_back (SeparatorElem());
913 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
915 output_menu.popup (1, ev->time);
926 MixerStrip::input_release (GdkEventButton *ev)
928 switch (ev->button) {
931 edit_input_configuration ();
943 MixerStrip::input_press (GdkEventButton *ev)
945 using namespace Menu_Helpers;
947 MenuList& citems = input_menu.items();
948 input_menu.set_name ("ArdourContextMenu");
951 if (!_session->engine().connected()) {
952 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
957 if (_session->actively_recording() && _route->record_enabled())
960 switch (ev->button) {
963 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
967 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
969 citems.push_back (SeparatorElem());
970 uint32_t const n_with_separator = citems.size ();
972 input_menu_bundles.clear ();
974 ARDOUR::BundleList current = _route->input()->bundles_connected ();
976 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
978 /* give user bundles first chance at being in the menu */
980 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
981 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
982 maybe_add_bundle_to_input_menu (*i, current);
986 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
987 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
988 maybe_add_bundle_to_input_menu (*i, current);
992 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
993 RouteList copy = *routes;
994 copy.sort (RouteCompareByName ());
995 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
996 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
999 if (citems.size() == n_with_separator) {
1000 /* no routes added; remove the separator */
1004 citems.push_back (SeparatorElem());
1005 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1008 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1009 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1014 citems.push_back (SeparatorElem());
1015 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1017 input_menu.popup (1, ev->time);
1028 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1030 if (ignore_toggle) {
1034 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1036 if (std::find (current.begin(), current.end(), c) == current.end()) {
1037 _route->input()->connect_ports_to_bundle (c, true, this);
1039 _route->input()->disconnect_ports_from_bundle (c, this);
1044 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1046 if (ignore_toggle) {
1050 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1052 if (std::find (current.begin(), current.end(), c) == current.end()) {
1053 _route->output()->connect_ports_to_bundle (c, true, this);
1055 _route->output()->disconnect_ports_from_bundle (c, this);
1060 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1062 using namespace Menu_Helpers;
1064 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1068 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1069 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1073 if (i != input_menu_bundles.end()) {
1077 input_menu_bundles.push_back (b);
1079 MenuList& citems = input_menu.items();
1081 std::string n = b->name ();
1082 replace_all (n, "_", " ");
1084 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1088 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1090 using namespace Menu_Helpers;
1092 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1096 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1097 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1101 if (i != output_menu_bundles.end()) {
1105 output_menu_bundles.push_back (b);
1107 MenuList& citems = output_menu.items();
1109 std::string n = b->name ();
1110 replace_all (n, "_", " ");
1112 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1116 MixerStrip::update_diskstream_display ()
1118 if (is_track() && input_selector) {
1119 input_selector->hide_all ();
1122 route_color_changed ();
1126 MixerStrip::connect_to_pan ()
1128 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1130 panstate_connection.disconnect ();
1131 panstyle_connection.disconnect ();
1133 if (!_route->panner()) {
1137 boost::shared_ptr<Pannable> p = _route->pannable ();
1139 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1140 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1142 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1143 * However, that only works a panner was previously set.
1145 * PannerUI must remain subscribed to _panshell->Changed() in case
1146 * we switch the panner eg. AUX-Send and back
1147 * _route->panner_shell()->Changed() vs _panshell->Changed
1149 if (panners._panner == 0) {
1150 panners.panshell_changed ();
1152 update_panner_choices();
1156 MixerStrip::update_panner_choices ()
1158 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1159 if (!_route->panner_shell()) { return; }
1161 uint32_t in = _route->output()->n_ports().n_audio();
1163 if (_route->panner()) {
1164 in = _route->panner()->in().n_audio();
1167 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1171 * Output port labelling
1172 * =====================
1174 * Case 1: Each output has one connection, all connections are to system:playback_%i
1175 * out 1 -> system:playback_1
1176 * out 2 -> system:playback_2
1177 * out 3 -> system:playback_3
1180 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1181 * out 1 -> ardour:track_x/in 1
1182 * out 2 -> ardour:track_x/in 2
1183 * Display as: track_x
1185 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1186 * out 1 -> program x:foo
1187 * out 2 -> program x:foo
1188 * Display as: program x
1190 * Case 4: No connections (Disconnected)
1193 * Default case (unusual routing):
1194 * Display as: *number of connections*
1198 * .-----------------------------------------------.
1200 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1201 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1202 * '-----------------------------------------------'
1203 * .-----------------------------------------------.
1206 * '-----------------------------------------------'
1210 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1214 boost::shared_ptr<Port> port;
1215 vector<string> port_connections;
1217 uint32_t total_connection_count = 0;
1218 uint32_t io_connection_count = 0;
1219 uint32_t ardour_connection_count = 0;
1220 uint32_t system_connection_count = 0;
1221 uint32_t other_connection_count = 0;
1222 uint32_t typed_connection_count = 0;
1224 ostringstream label;
1226 bool have_label = false;
1227 bool each_io_has_one_connection = true;
1229 string connection_name;
1230 string ardour_track_name;
1231 string other_connection_type;
1232 string system_ports;
1235 ostringstream tooltip;
1236 char * tooltip_cstr;
1238 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1239 DataType dt = DataType::AUDIO;
1240 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1241 dt = DataType::MIDI;
1242 // avoid further confusion with Midi-tracks that have a synth.
1243 // Audio-ports may be connected, but button says "Disconnected"
1244 tooltip << _("MIDI ");
1248 io_count = route->n_inputs().n_total();
1249 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1251 io_count = route->n_outputs().n_total();
1252 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1256 for (io_index = 0; io_index < io_count; ++io_index) {
1258 port = route->input()->nth (io_index);
1260 port = route->output()->nth (io_index);
1263 port_connections.clear ();
1264 port->get_connections(port_connections);
1266 //ignore any port connections that don't match our DataType
1267 if (port->type() != dt) {
1268 if (!port_connections.empty()) {
1269 ++typed_connection_count;
1274 io_connection_count = 0;
1276 if (!port_connections.empty()) {
1277 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1279 string& connection_name (*i);
1281 if (connection_name.find("system:") == 0) {
1282 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1285 if (io_connection_count == 0) {
1286 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1288 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1291 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1294 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1295 if (ardour_track_name.empty()) {
1296 // "ardour:Master/in 1" -> "ardour:Master/"
1297 string::size_type slash = connection_name.find("/");
1298 if (slash != string::npos) {
1299 ardour_track_name = connection_name.substr(0, slash + 1);
1303 if (connection_name.find(ardour_track_name) == 0) {
1304 ++ardour_connection_count;
1306 } else if (!pn.empty()) {
1307 if (system_ports.empty()) {
1310 system_ports += "/" + pn;
1312 if (connection_name.find("system:") == 0) {
1313 ++system_connection_count;
1315 } else if (connection_name.find("system:midi_") == 0) {
1317 // "system:midi_capture_123" -> "123"
1318 system_port = "M " + connection_name.substr(20);
1320 // "system:midi_playback_123" -> "123"
1321 system_port = "M " + connection_name.substr(21);
1324 if (system_ports.empty()) {
1325 system_ports += system_port;
1327 system_ports += "/" + system_port;
1330 ++system_connection_count;
1332 } else if (connection_name.find("system:") == 0) {
1334 // "system:capture_123" -> "123"
1335 system_port = connection_name.substr(15);
1337 // "system:playback_123" -> "123"
1338 system_port = connection_name.substr(16);
1341 if (system_ports.empty()) {
1342 system_ports += system_port;
1344 system_ports += "/" + system_port;
1347 ++system_connection_count;
1349 if (other_connection_type.empty()) {
1350 // "jamin:in 1" -> "jamin:"
1351 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1354 if (connection_name.find(other_connection_type) == 0) {
1355 ++other_connection_count;
1359 ++total_connection_count;
1360 ++io_connection_count;
1364 if (io_connection_count != 1) {
1365 each_io_has_one_connection = false;
1369 if (total_connection_count == 0) {
1370 tooltip << endl << _("Disconnected");
1373 tooltip_cstr = new char[tooltip.str().size() + 1];
1374 strcpy(tooltip_cstr, tooltip.str().c_str());
1377 set_tooltip (&input_button, tooltip_cstr);
1379 set_tooltip (&output_button, tooltip_cstr);
1382 delete [] tooltip_cstr;
1384 if (each_io_has_one_connection) {
1385 if (total_connection_count == ardour_connection_count) {
1386 // all connections are to the same track in ardour
1387 // "ardour:Master/" -> "Master"
1388 string::size_type slash = ardour_track_name.find("/");
1389 if (slash != string::npos) {
1390 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1391 label << ardour_track_name.substr (ppps, slash - ppps);
1395 else if (total_connection_count == system_connection_count) {
1396 // all connections are to system ports
1397 label << system_ports;
1400 else if (total_connection_count == other_connection_count) {
1401 // all connections are to the same external program eg jamin
1402 // "jamin:" -> "jamin"
1403 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1409 if (total_connection_count == 0) {
1413 // Odd configuration
1414 label << "*" << total_connection_count << "*";
1416 if (typed_connection_count > 0) {
1417 label << "\u2295"; // circled plus
1422 input_button.set_text (label.str());
1424 output_button.set_text (label.str());
1429 MixerStrip::update_input_display ()
1431 update_io_button (_route, _width, true);
1432 panners.setup_pan ();
1434 if (has_audio_outputs ()) {
1435 panners.show_all ();
1437 panners.hide_all ();
1443 MixerStrip::update_output_display ()
1445 update_io_button (_route, _width, false);
1446 gpm.setup_meters ();
1447 panners.setup_pan ();
1449 if (has_audio_outputs ()) {
1450 panners.show_all ();
1452 panners.hide_all ();
1457 MixerStrip::fast_update ()
1459 gpm.update_meters ();
1463 MixerStrip::diskstream_changed ()
1465 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1469 MixerStrip::io_changed_proxy ()
1471 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1475 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1477 boost::shared_ptr<Port> a = wa.lock ();
1478 boost::shared_ptr<Port> b = wb.lock ();
1480 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1481 update_input_display ();
1482 set_width_enum (_width, this);
1485 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1486 update_output_display ();
1487 set_width_enum (_width, this);
1492 MixerStrip::setup_comment_button ()
1497 if (_route->comment().empty ()) {
1498 _comment_button.unset_bg (STATE_NORMAL);
1499 _comment_button.set_text (_("Comments"));
1501 _comment_button.modify_bg (STATE_NORMAL, color ());
1502 _comment_button.set_text (_("*Comments*"));
1507 if (_route->comment().empty ()) {
1508 _comment_button.unset_bg (STATE_NORMAL);
1509 _comment_button.set_text (_("Cmt"));
1511 _comment_button.modify_bg (STATE_NORMAL, color ());
1512 _comment_button.set_text (_("*Cmt*"));
1518 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1524 MixerStrip::select_route_group (GdkEventButton *ev)
1526 using namespace Menu_Helpers;
1528 if (ev->button == 1) {
1530 if (group_menu == 0) {
1532 PropertyList* plist = new PropertyList();
1534 plist->add (Properties::gain, true);
1535 plist->add (Properties::mute, true);
1536 plist->add (Properties::solo, true);
1538 group_menu = new RouteGroupMenu (_session, plist);
1542 r.push_back (route ());
1543 group_menu->build (r);
1544 group_menu->menu()->popup (1, ev->time);
1551 MixerStrip::route_group_changed ()
1553 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1555 RouteGroup *rg = _route->route_group();
1558 group_button.set_text (PBD::short_version (rg->name(), 5));
1562 group_button.set_text (_("Grp"));
1565 group_button.set_text (_("~G"));
1572 MixerStrip::route_color_changed ()
1574 name_button.modify_bg (STATE_NORMAL, color());
1575 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1576 reset_strip_style ();
1580 MixerStrip::show_passthru_color ()
1582 reset_strip_style ();
1586 MixerStrip::build_route_ops_menu ()
1588 using namespace Menu_Helpers;
1589 route_ops_menu = new Menu;
1590 route_ops_menu->set_name ("ArdourContextMenu");
1592 MenuList& items = route_ops_menu->items();
1594 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1596 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1598 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1600 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1602 items.push_back (SeparatorElem());
1604 if (!_route->is_master()) {
1605 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1607 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1608 rename_menu_item = &items.back();
1610 items.push_back (SeparatorElem());
1611 items.push_back (CheckMenuElem (_("Active")));
1612 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1613 i->set_active (_route->active());
1614 i->set_sensitive(! _session->transport_rolling());
1615 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1617 if (!Profile->get_mixbus ()) {
1618 items.push_back (SeparatorElem());
1619 items.push_back (CheckMenuElem (_("Strict I/O")));
1620 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1621 i->set_active (_route->strict_io());
1622 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1625 if (1 /* TODO IFF >= 1 plugin-insert */) {
1626 items.push_back (SeparatorElem());
1627 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1630 items.push_back (SeparatorElem());
1631 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1633 items.push_back (SeparatorElem());
1634 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1635 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1636 denormal_menu_item->set_active (_route->denormal_protection());
1638 items.push_back (SeparatorElem());
1639 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1642 /* note that this relies on selection being shared across editor and
1643 mixer (or global to the backend, in the future), which is the only
1644 sane thing for users anyway.
1647 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1649 Selection& selection (PublicEditor::instance().get_selection());
1650 if (!selection.selected (rtav)) {
1651 selection.set (rtav);
1654 if (!_route->is_master()) {
1655 items.push_back (SeparatorElem());
1656 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1659 items.push_back (SeparatorElem());
1660 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1666 MixerStrip::name_button_button_press (GdkEventButton* ev)
1668 if (ev->button == 3) {
1669 list_route_operations ();
1671 /* do not allow rename if the track is record-enabled */
1672 rename_menu_item->set_sensitive (!_route->record_enabled());
1673 route_ops_menu->popup (1, ev->time);
1682 MixerStrip::name_button_button_release (GdkEventButton* ev)
1684 if (ev->button == 1) {
1685 list_route_operations ();
1687 /* do not allow rename if the track is record-enabled */
1688 rename_menu_item->set_sensitive (!_route->record_enabled());
1689 route_ops_menu->popup (1, ev->time);
1696 MixerStrip::number_button_button_press (GdkEventButton* ev)
1698 if ( ev->button == 3 ) {
1699 list_route_operations ();
1701 /* do not allow rename if the track is record-enabled */
1702 rename_menu_item->set_sensitive (!_route->record_enabled());
1703 route_ops_menu->popup (1, ev->time);
1712 MixerStrip::list_route_operations ()
1714 delete route_ops_menu;
1715 build_route_ops_menu ();
1719 MixerStrip::set_selected (bool yn)
1721 AxisView::set_selected (yn);
1723 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1724 global_frame.set_name ("MixerStripSelectedFrame");
1726 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1727 global_frame.set_name ("MixerStripFrame");
1729 global_frame.queue_draw ();
1732 // processor_box.deselect_all_processors();
1736 MixerStrip::property_changed (const PropertyChange& what_changed)
1738 RouteUI::property_changed (what_changed);
1740 if (what_changed.contains (ARDOUR::Properties::name)) {
1746 MixerStrip::name_changed ()
1750 name_button.set_text (_route->name());
1753 name_button.set_text (PBD::short_version (_route->name(), 5));
1757 set_tooltip (name_button, _route->name());
1759 if (_session->config.get_track_name_number()) {
1760 const int64_t track_number = _route->track_number ();
1761 if (track_number == 0) {
1762 number_label.set_text ("-");
1764 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1767 number_label.set_text ("");
1772 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1774 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1778 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1780 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1784 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1786 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1790 MixerStrip::width_button_pressed (GdkEventButton* ev)
1792 if (ev->button != 1) {
1796 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1799 _mixer.set_strip_width (Narrow, true);
1803 _mixer.set_strip_width (Wide, true);
1809 set_width_enum (Narrow, this);
1812 set_width_enum (Wide, this);
1821 MixerStrip::hide_clicked ()
1823 // LAME fix to reset the button status for when it is redisplayed (part 1)
1824 hide_button.set_sensitive(false);
1827 Hiding(); /* EMIT_SIGNAL */
1829 _mixer.hide_strip (this);
1833 hide_button.set_sensitive(true);
1837 MixerStrip::set_embedded (bool yn)
1843 MixerStrip::map_frozen ()
1845 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1847 boost::shared_ptr<AudioTrack> at = audio_track();
1850 switch (at->freeze_state()) {
1851 case AudioTrack::Frozen:
1852 processor_box.set_sensitive (false);
1853 hide_redirect_editors ();
1856 processor_box.set_sensitive (true);
1857 // XXX need some way, maybe, to retoggle redirect editors
1861 processor_box.set_sensitive (true);
1863 RouteUI::map_frozen ();
1867 MixerStrip::hide_redirect_editors ()
1869 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1873 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1875 boost::shared_ptr<Processor> processor (p.lock ());
1880 Gtk::Window* w = processor_box.get_processor_ui (processor);
1888 MixerStrip::reset_strip_style ()
1890 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1892 gpm.set_fader_name ("SendStripBase");
1896 if (is_midi_track()) {
1897 if (_route->active()) {
1898 set_name ("MidiTrackStripBase");
1900 set_name ("MidiTrackStripBaseInactive");
1902 gpm.set_fader_name ("MidiTrackFader");
1903 } else if (is_audio_track()) {
1904 if (_route->active()) {
1905 set_name ("AudioTrackStripBase");
1907 set_name ("AudioTrackStripBaseInactive");
1909 gpm.set_fader_name ("AudioTrackFader");
1911 if (_route->active()) {
1912 set_name ("AudioBusStripBase");
1914 set_name ("AudioBusStripBaseInactive");
1916 gpm.set_fader_name ("AudioBusFader");
1918 /* (no MIDI busses yet) */
1925 MixerStrip::engine_stopped ()
1930 MixerStrip::engine_running ()
1935 MixerStrip::meter_point_string (MeterPoint mp)
1948 case MeterPostFader:
1965 return S_("Meter|In");
1969 return S_("Meter|Pr");
1972 case MeterPostFader:
1973 return S_("Meter|Po");
1977 return S_("Meter|O");
1982 return S_("Meter|C");
1991 /** Called when the monitor-section state */
1993 MixerStrip::monitor_changed ()
1995 assert (monitor_section_button);
1996 if (_session->monitor_active()) {
1997 monitor_section_button->set_name ("master monitor section button active");
1999 monitor_section_button->set_name ("master monitor section button normal");
2003 /** Called when the metering point has changed */
2005 MixerStrip::meter_changed ()
2007 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2008 gpm.setup_meters ();
2009 // reset peak when meter point changes
2010 gpm.reset_peak_display();
2013 /** The bus that we are displaying sends to has changed, or been turned off.
2014 * @param send_to New bus that we are displaying sends to, or 0.
2017 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2019 RouteUI::bus_send_display_changed (send_to);
2022 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2027 revert_to_default_display ();
2030 revert_to_default_display ();
2035 MixerStrip::drop_send ()
2037 boost::shared_ptr<Send> current_send;
2039 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2040 current_send->set_metering (false);
2043 send_gone_connection.disconnect ();
2044 input_button.set_sensitive (true);
2045 output_button.set_sensitive (true);
2046 group_button.set_sensitive (true);
2047 set_invert_sensitive (true);
2048 meter_point_button.set_sensitive (true);
2049 mute_button->set_sensitive (true);
2050 solo_button->set_sensitive (true);
2051 solo_isolated_led->set_sensitive (true);
2052 solo_safe_led->set_sensitive (true);
2053 monitor_input_button->set_sensitive (true);
2054 monitor_disk_button->set_sensitive (true);
2055 _comment_button.set_sensitive (true);
2056 RouteUI::check_rec_enable_sensitivity ();
2057 set_button_names (); // update solo button visual state
2061 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2063 _current_delivery = d;
2064 DeliveryChanged (_current_delivery);
2068 MixerStrip::show_send (boost::shared_ptr<Send> send)
2074 set_current_delivery (send);
2076 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2077 send->set_metering (true);
2078 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2080 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2081 gain_meter().setup_meters ();
2083 uint32_t const in = _current_delivery->pans_required();
2084 uint32_t const out = _current_delivery->pan_outs();
2086 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2087 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2088 panner_ui().setup_pan ();
2089 panner_ui().set_send_drawing_mode (true);
2090 panner_ui().show_all ();
2092 input_button.set_sensitive (false);
2093 group_button.set_sensitive (false);
2094 set_invert_sensitive (false);
2095 meter_point_button.set_sensitive (false);
2096 mute_button->set_sensitive (false);
2097 solo_button->set_sensitive (false);
2098 rec_enable_button->set_sensitive (false);
2099 solo_isolated_led->set_sensitive (false);
2100 solo_safe_led->set_sensitive (false);
2101 monitor_input_button->set_sensitive (false);
2102 monitor_disk_button->set_sensitive (false);
2103 _comment_button.set_sensitive (false);
2105 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2106 output_button.set_sensitive (false);
2109 reset_strip_style ();
2113 MixerStrip::revert_to_default_display ()
2117 set_current_delivery (_route->main_outs ());
2119 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2120 gain_meter().setup_meters ();
2122 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2123 update_panner_choices();
2124 panner_ui().setup_pan ();
2125 panner_ui().set_send_drawing_mode (false);
2127 if (has_audio_outputs ()) {
2128 panners.show_all ();
2130 panners.hide_all ();
2133 reset_strip_style ();
2137 MixerStrip::set_button_names ()
2141 mute_button->set_text (_("Mute"));
2142 monitor_input_button->set_text (_("In"));
2143 monitor_disk_button->set_text (_("Disk"));
2144 if (monitor_section_button) {
2145 monitor_section_button->set_text (_("Mon"));
2148 if (_route && _route->solo_safe()) {
2149 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2151 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2153 if (!Config->get_solo_control_is_listen_control()) {
2154 solo_button->set_text (_("Solo"));
2156 switch (Config->get_listen_position()) {
2157 case AfterFaderListen:
2158 solo_button->set_text (_("AFL"));
2160 case PreFaderListen:
2161 solo_button->set_text (_("PFL"));
2165 solo_isolated_led->set_text (_("Iso"));
2166 solo_safe_led->set_text (S_("SoloLock|Lock"));
2170 mute_button->set_text (S_("Mute|M"));
2171 monitor_input_button->set_text (S_("MonitorInput|I"));
2172 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2173 if (monitor_section_button) {
2174 monitor_section_button->set_text (S_("Mon|O"));
2177 if (_route && _route->solo_safe()) {
2178 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2180 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2182 if (!Config->get_solo_control_is_listen_control()) {
2183 solo_button->set_text (S_("Solo|S"));
2185 switch (Config->get_listen_position()) {
2186 case AfterFaderListen:
2187 solo_button->set_text (S_("AfterFader|A"));
2189 case PreFaderListen:
2190 solo_button->set_text (S_("Prefader|P"));
2195 solo_isolated_led->set_text (S_("SoloIso|I"));
2196 solo_safe_led->set_text (S_("SoloLock|L"));
2201 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2203 meter_point_button.set_text ("");
2208 MixerStrip::plugin_selector()
2210 return _mixer.plugin_selector();
2214 MixerStrip::hide_things ()
2216 processor_box.hide_things ();
2220 MixerStrip::input_active_button_press (GdkEventButton*)
2222 /* nothing happens on press */
2227 MixerStrip::input_active_button_release (GdkEventButton* ev)
2229 boost::shared_ptr<MidiTrack> mt = midi_track ();
2235 boost::shared_ptr<RouteList> rl (new RouteList);
2237 rl->push_back (route());
2239 _session->set_exclusive_input_active (rl, !mt->input_active(),
2240 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2246 MixerStrip::midi_input_status_changed ()
2248 if (midi_input_enable_button) {
2249 boost::shared_ptr<MidiTrack> mt = midi_track ();
2251 midi_input_enable_button->set_active (mt->input_active ());
2256 MixerStrip::state_id () const
2258 return string_compose ("strip %1", _route->id().to_s());
2262 MixerStrip::parameter_changed (string p)
2264 if (p == _visibility.get_state_name()) {
2265 /* The user has made changes to the mixer strip visibility, so get
2266 our VisibilityGroup to reflect these changes in our widgets.
2268 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2270 else if (p == "track-name-number") {
2273 else if (p == "use-monitor-bus") {
2274 if (monitor_section_button) {
2275 if (mute_button->get_parent()) {
2276 mute_button->get_parent()->remove(*mute_button);
2278 if (monitor_section_button->get_parent()) {
2279 monitor_section_button->get_parent()->remove(*monitor_section_button);
2281 if (Config->get_use_monitor_bus ()) {
2282 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2283 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2284 mute_button->show();
2285 monitor_section_button->show();
2287 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2288 mute_button->show();
2294 /** Called to decide whether the solo isolate / solo lock button visibility should
2295 * be overridden from that configured by the user. We do this for the master bus.
2297 * @return optional value that is present if visibility state should be overridden.
2299 boost::optional<bool>
2300 MixerStrip::override_solo_visibility () const
2302 if (_route && _route->is_master ()) {
2303 return boost::optional<bool> (false);
2306 return boost::optional<bool> ();
2310 MixerStrip::add_input_port (DataType t)
2312 _route->input()->add_port ("", this, t);
2316 MixerStrip::add_output_port (DataType t)
2318 _route->output()->add_port ("", this, t);
2322 MixerStrip::route_active_changed ()
2324 reset_strip_style ();
2328 MixerStrip::copy_processors ()
2330 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2334 MixerStrip::cut_processors ()
2336 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2340 MixerStrip::paste_processors ()
2342 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2346 MixerStrip::select_all_processors ()
2348 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2352 MixerStrip::deselect_all_processors ()
2354 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2358 MixerStrip::delete_processors ()
2360 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2364 MixerStrip::toggle_processors ()
2366 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2370 MixerStrip::ab_plugins ()
2372 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2376 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2378 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2381 if (ev->button == 3) {
2382 popup_level_meter_menu (ev);
2390 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2392 using namespace Gtk::Menu_Helpers;
2394 Gtk::Menu* m = manage (new Menu);
2395 MenuList& items = m->items ();
2397 RadioMenuItem::Group group;
2399 _suspend_menu_callbacks = true;
2400 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2401 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2402 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2403 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2404 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2406 if (gpm.meter_channels().n_audio() == 0) {
2407 m->popup (ev->button, ev->time);
2408 _suspend_menu_callbacks = false;
2412 RadioMenuItem::Group tgroup;
2413 items.push_back (SeparatorElem());
2415 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2416 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2417 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2418 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2419 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2420 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2421 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2422 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2423 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2424 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2425 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2428 if (_route->is_master()) {
2431 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2432 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2433 /* non-master bus */
2436 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2443 MeterType cmt = _route->meter_type();
2444 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2446 items.push_back (SeparatorElem());
2447 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2448 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2449 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2450 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2451 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2452 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2454 m->popup (ev->button, ev->time);
2455 _suspend_menu_callbacks = false;
2459 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2460 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2462 using namespace Menu_Helpers;
2464 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2465 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2466 i->set_active (_route->meter_point() == point);
2470 MixerStrip::set_meter_point (MeterPoint p)
2472 if (_suspend_menu_callbacks) return;
2473 _route->set_meter_point (p);
2477 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2478 RadioMenuItem::Group& group, string const & name, MeterType type)
2480 using namespace Menu_Helpers;
2482 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2483 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2484 i->set_active (_route->meter_type() == type);
2488 MixerStrip::set_meter_type (MeterType t)
2490 if (_suspend_menu_callbacks) return;
2495 MixerStrip::vca_menu_toggle (uint32_t n)
2501 boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2511 MixerStrip::vca_button_release (GdkEventButton* ev, uint32_t which)
2513 using namespace Gtk::Menu_Helpers;
2515 if (!_session || !Keyboard::is_context_menu_event (ev)) {
2519 VCAList vcas (_session->vca_manager().vcas());
2522 /* XXX should probably show a message saying "No VCA masters" */
2526 Menu* menu = new Menu;
2527 MenuList& items = menu->items();
2528 RadioMenuItem::Group group;
2530 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2531 items.push_back (RadioMenuElem (group, (*v)->name(), sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), (*v)->number())));
2534 menu->popup (1, ev->time);