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"
56 #include "ardour_window.h"
57 #include "mixer_strip.h"
60 #include "ardour_button.h"
61 #include "public_editor.h"
63 #include "io_selector.h"
65 #include "gui_thread.h"
66 #include "route_group_menu.h"
67 #include "meter_patterns.h"
69 #include "ui_config.h"
73 using namespace ARDOUR;
74 using namespace ARDOUR_UI_UTILS;
77 using namespace Gtkmm2ext;
79 using namespace ArdourMeter;
81 MixerStrip* MixerStrip::_entered_mixer_strip;
83 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
85 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
89 , _mixer_owned (in_mixer)
90 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
93 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
94 , rec_mon_table (2, 2)
95 , solo_iso_table (1, 2)
96 , mute_solo_table (1, 2)
97 , bottom_button_table (1, 3)
98 , meter_point_button (_("pre"))
99 , monitor_section_button (0)
100 , midi_input_enable_button (0)
101 , _comment_button (_("Comments"))
102 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
103 , _visibility (X_("mixer-element-visibility"))
108 /* the editor mixer strip: don't destroy it every time
109 the underlying route goes away.
112 self_destruct = false;
116 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
120 , _mixer_owned (in_mixer)
121 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
124 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
125 , rec_mon_table (2, 2)
126 , solo_iso_table (1, 2)
127 , mute_solo_table (1, 2)
128 , bottom_button_table (1, 3)
129 , meter_point_button (_("pre"))
130 , monitor_section_button (0)
131 , midi_input_enable_button (0)
132 , _comment_button (_("Comments"))
133 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
134 , _visibility (X_("mixer-element-visibility"))
143 _entered_mixer_strip= 0;
146 ignore_comment_edit = false;
147 ignore_toggle = false;
152 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
153 longest_label = "longest label";
155 string t = _("Click to toggle the width of this mixer strip.");
157 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
160 width_button.set_icon (ArdourIcon::StripWidth);
161 set_tooltip (width_button, t);
163 hide_button.set_icon (ArdourIcon::CloseCross);
164 set_tooltip (&hide_button, _("Hide this mixer strip"));
166 input_button_box.set_spacing(2);
168 input_button.set_text (_("Input"));
169 input_button.set_name ("mixer strip button");
170 input_button_box.pack_start (input_button, true, true);
172 output_button.set_text (_("Output"));
173 output_button.set_name ("mixer strip button");
175 set_tooltip (&meter_point_button, _("Click to select metering point"));
176 meter_point_button.set_name ("mixer strip button");
178 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
180 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
181 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
183 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
185 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
186 solo_isolated_led->show ();
187 solo_isolated_led->set_no_show_all (true);
188 solo_isolated_led->set_name (X_("solo isolate"));
189 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
190 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
191 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
193 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
194 solo_safe_led->show ();
195 solo_safe_led->set_no_show_all (true);
196 solo_safe_led->set_name (X_("solo safe"));
197 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
198 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
199 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
201 solo_safe_led->set_text (S_("SoloLock|Lock"));
202 solo_isolated_led->set_text (_("Iso"));
204 solo_iso_table.set_homogeneous (true);
205 solo_iso_table.set_spacings (2);
206 if (!ARDOUR::Profile->get_trx()) {
207 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
208 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
210 solo_iso_table.show ();
212 rec_mon_table.set_homogeneous (true);
213 rec_mon_table.set_row_spacings (2);
214 rec_mon_table.set_col_spacings (2);
215 if (ARDOUR::Profile->get_mixbus()) {
216 rec_mon_table.resize (1, 3);
217 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
218 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
219 } else if (!ARDOUR::Profile->get_trx()) {
220 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
221 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
223 rec_mon_table.show ();
225 if (solo_isolated_led) {
226 button_size_group->add_widget (*solo_isolated_led);
229 button_size_group->add_widget (*solo_safe_led);
232 if (!ARDOUR::Profile->get_mixbus()) {
233 if (rec_enable_button) {
234 button_size_group->add_widget (*rec_enable_button);
236 if (monitor_disk_button) {
237 button_size_group->add_widget (*monitor_disk_button);
239 if (monitor_input_button) {
240 button_size_group->add_widget (*monitor_input_button);
244 mute_solo_table.set_homogeneous (true);
245 mute_solo_table.set_spacings (2);
247 bottom_button_table.set_spacings (2);
248 bottom_button_table.set_homogeneous (true);
249 bottom_button_table.attach (group_button, 1, 2, 0, 1);
250 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
252 name_button.set_name ("mixer strip button");
253 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
254 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
256 set_tooltip (&group_button, _("Mix group"));
257 group_button.set_name ("mixer strip button");
259 _comment_button.set_name (X_("mixer strip button"));
260 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
262 // TODO implement ArdourKnob::on_size_request properly
263 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
264 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
266 trim_control.set_tooltip_prefix (_("Trim: "));
267 trim_control.set_name ("trim knob");
268 trim_control.set_no_show_all (true);
269 input_button_box.pack_start (trim_control, false, false);
271 global_vpacker.set_border_width (1);
272 global_vpacker.set_spacing (0);
274 width_button.set_name ("mixer strip button");
275 hide_button.set_name ("mixer strip button");
277 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
278 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
280 // width_hide_box.set_border_width (1);
281 width_hide_box.set_spacing (2);
282 width_hide_box.pack_start (width_button, false, true);
283 width_hide_box.pack_start (number_label, true, true);
284 width_hide_box.pack_end (hide_button, false, true);
286 number_label.set_text ("-");
287 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
288 number_label.set_no_show_all ();
289 number_label.set_name ("tracknumber label");
290 number_label.set_fixed_colors (0x80808080, 0x80808080);
291 number_label.set_alignment (.5, .5);
292 number_label.set_fallthrough_to_parent (true);
294 global_vpacker.set_spacing (2);
295 if (!ARDOUR::Profile->get_trx()) {
296 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
297 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
298 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
299 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
300 global_vpacker.pack_start (processor_box, true, true);
302 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
303 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
304 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
305 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
306 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
308 if (!ARDOUR::Profile->get_trx()) {
309 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
312 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
315 global_frame.add (global_vpacker);
316 global_frame.set_shadow_type (Gtk::SHADOW_IN);
317 global_frame.set_name ("BaseFrame");
321 /* force setting of visible selected status */
324 set_selected (false);
329 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
330 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
332 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
333 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
334 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
336 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
337 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
339 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
340 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
341 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
343 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
345 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
346 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
348 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
352 /* start off as a passthru strip. we'll correct this, if necessary,
353 in update_diskstream_display().
356 /* start off as a passthru strip. we'll correct this, if necessary,
357 in update_diskstream_display().
360 if (is_midi_track()) {
361 set_name ("MidiTrackStripBase");
363 set_name ("AudioTrackStripBase");
366 add_events (Gdk::BUTTON_RELEASE_MASK|
367 Gdk::ENTER_NOTIFY_MASK|
368 Gdk::LEAVE_NOTIFY_MASK|
370 Gdk::KEY_RELEASE_MASK);
372 set_flags (get_flags() | Gtk::CAN_FOCUS);
374 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
375 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
378 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
379 must be the same as those used in RCOptionEditor so that the configuration changes
380 are recognised when they occur.
382 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
383 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
384 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
385 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
386 _visibility.add (&output_button, X_("Output"), _("Output"), false);
387 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
389 parameter_changed (X_("mixer-element-visibility"));
390 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
391 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
392 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
394 //watch for mouse enter/exit so we can do some stuff
395 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
396 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
398 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
401 MixerStrip::~MixerStrip ()
403 CatchDeletion (this);
405 if (this ==_entered_mixer_strip)
406 _entered_mixer_strip = NULL;
410 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
412 _entered_mixer_strip = this;
414 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
415 //because the mixerstrip control is a parent that encompasses the strip
416 deselect_all_processors();
422 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
424 //if we have moved outside our strip, but not into a child view, then deselect ourselves
425 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
426 _entered_mixer_strip= 0;
428 //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
429 gpm.gain_display.set_sensitive(false);
431 gpm.gain_display.set_sensitive(true);
433 //if we leave this mixer strip we need to clear out any selections
434 //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
441 MixerStrip::set_route (boost::shared_ptr<Route> rt)
443 //the rec/monitor stuff only shows up for tracks.
444 //the show_sends only shows up for buses.
445 //remove them all here, and we may add them back later
446 if (show_sends_button->get_parent()) {
447 rec_mon_table.remove (*show_sends_button);
449 if (rec_enable_button->get_parent()) {
450 rec_mon_table.remove (*rec_enable_button);
452 if (monitor_input_button->get_parent()) {
453 rec_mon_table.remove (*monitor_input_button);
455 if (monitor_disk_button->get_parent()) {
456 rec_mon_table.remove (*monitor_disk_button);
458 if (group_button.get_parent()) {
459 bottom_button_table.remove (group_button);
462 RouteUI::set_route (rt);
464 /* ProcessorBox needs access to _route so that it can read
467 processor_box.set_route (rt);
469 revert_to_default_display ();
471 /* unpack these from the parent and stuff them into our own
475 if (gpm.peak_display.get_parent()) {
476 gpm.peak_display.get_parent()->remove (gpm.peak_display);
478 if (gpm.gain_display.get_parent()) {
479 gpm.gain_display.get_parent()->remove (gpm.gain_display);
482 gpm.set_type (rt->meter_type());
484 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
485 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
487 if (solo_button->get_parent()) {
488 mute_solo_table.remove (*solo_button);
491 if (mute_button->get_parent()) {
492 mute_solo_table.remove (*mute_button);
495 if (route()->is_master()) {
496 solo_button->hide ();
497 mute_button->show ();
498 rec_mon_table.hide ();
499 if (solo_iso_table.get_parent()) {
500 solo_iso_table.get_parent()->remove(solo_iso_table);
502 if (monitor_section_button == 0) {
503 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
504 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
506 monitor_section_button = manage (new ArdourButton);
508 monitor_section_button->set_related_action (act);
509 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
510 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
511 monitor_section_button->show();
512 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
514 parameter_changed ("use-monitor-bus");
516 bottom_button_table.attach (group_button, 1, 2, 0, 1);
517 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
518 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
519 mute_button->show ();
520 solo_button->show ();
521 rec_mon_table.show ();
524 if (_mixer_owned && route()->is_master() ) {
526 HScrollbar scrollbar;
527 Gtk::Requisition requisition(scrollbar.size_request ());
528 int scrollbar_height = requisition.height;
530 spacer = manage (new EventBox);
531 spacer->set_size_request (-1, scrollbar_height+2);
532 global_vpacker.pack_start (*spacer, false, false);
537 monitor_input_button->show ();
538 monitor_disk_button->show ();
540 monitor_input_button->hide();
541 monitor_disk_button->hide ();
544 if (route()->trim() && route()->trim()->active()) {
545 trim_control.show ();
546 trim_control.set_controllable (route()->trim()->gain_control());
548 trim_control.hide ();
549 boost::shared_ptr<Controllable> none;
550 trim_control.set_controllable (none);
553 if (is_midi_track()) {
554 if (midi_input_enable_button == 0) {
555 midi_input_enable_button = manage (new ArdourButton);
556 midi_input_enable_button->set_name ("midi input button");
557 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
558 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
559 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
560 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
561 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
563 input_button_box.remove (*midi_input_enable_button);
565 /* get current state */
566 midi_input_status_changed ();
567 input_button_box.pack_start (*midi_input_enable_button, false, false);
569 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
571 if (midi_input_enable_button) {
572 /* removal from the container will delete it */
573 input_button_box.remove (*midi_input_enable_button);
574 midi_input_enable_button = 0;
578 if (is_audio_track()) {
579 boost::shared_ptr<AudioTrack> at = audio_track();
580 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
585 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
586 rec_enable_button->set_sensitive (_session->writable());
587 rec_enable_button->show();
589 if (ARDOUR::Profile->get_mixbus()) {
590 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
591 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
592 } else if (ARDOUR::Profile->get_trx()) {
593 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
595 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
596 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
603 if (!_route->is_master()) {
604 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
605 show_sends_button->show();
609 meter_point_button.set_text (meter_point_string (_route->meter_point()));
611 delete route_ops_menu;
614 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
615 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
616 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
617 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
619 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
621 if (_route->panner_shell()) {
622 update_panner_choices();
623 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
626 if (is_audio_track()) {
627 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
630 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
631 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
633 set_stuff_from_route ();
635 /* now force an update of all the various elements */
637 update_mute_display ();
638 update_solo_display ();
641 route_group_changed ();
644 panners.setup_pan ();
646 if (has_audio_outputs ()) {
652 update_diskstream_display ();
653 update_input_display ();
654 update_output_display ();
656 add_events (Gdk::BUTTON_RELEASE_MASK);
658 processor_box.show ();
660 if (!route()->is_master() && !route()->is_monitor()) {
661 /* we don't allow master or control routes to be hidden */
666 gpm.reset_peak_display ();
667 gpm.gain_display.show ();
668 gpm.peak_display.show ();
671 width_hide_box.show();
673 global_vpacker.show();
674 mute_solo_table.show();
675 bottom_button_table.show();
677 meter_point_button.show();
678 input_button_box.show_all();
679 output_button.show();
681 _comment_button.show();
683 gpm.gain_automation_state_button.show();
685 parameter_changed ("mixer-element-visibility");
692 MixerStrip::set_stuff_from_route ()
694 /* if width is not set, it will be set by the MixerUI or editor */
696 string str = gui_property ("strip-width");
698 set_width_enum (Width (string_2_enum (str, _width)), this);
703 MixerStrip::set_width_enum (Width w, void* owner)
705 /* always set the gpm width again, things may be hidden */
708 panners.set_width (w);
710 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
712 _width_owner = owner;
716 if (_width_owner == this) {
717 set_gui_property ("strip-width", enum_2_string (_width));
722 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
727 if (show_sends_button) {
728 show_sends_button->set_text (_("Aux"));
731 gpm.gain_automation_style_button.set_text (
732 gpm.astyle_string(gain_automation->automation_style()));
733 gpm.gain_automation_state_button.set_text (
734 gpm.astate_string(gain_automation->automation_state()));
736 if (_route->panner()) {
737 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
738 panners.astyle_string(_route->panner()->automation_style()));
739 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
740 panners.astate_string(_route->panner()->automation_state()));
744 // panners expect an even number of horiz. pixels
745 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
747 set_size_request (width, -1);
753 if (show_sends_button) {
754 show_sends_button->set_text (_("Snd"));
757 gpm.gain_automation_style_button.set_text (
758 gpm.short_astyle_string(gain_automation->automation_style()));
759 gpm.gain_automation_state_button.set_text (
760 gpm.short_astate_string(gain_automation->automation_state()));
761 gain_meter().setup_meters (); // recalc meter width
763 if (_route->panner()) {
764 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
765 panners.short_astyle_string(_route->panner()->automation_style()));
766 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
767 panners.short_astate_string(_route->panner()->automation_state()));
771 // panners expect an even number of horiz. pixels
772 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
774 set_size_request (width, -1);
779 processor_box.set_width (w);
781 update_input_display ();
782 update_output_display ();
783 setup_comment_button ();
784 route_group_changed ();
790 MixerStrip::set_packed (bool yn)
795 set_gui_property ("visible", true);
797 set_gui_property ("visible", false);
802 struct RouteCompareByName {
803 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
804 return a->name().compare (b->name()) < 0;
809 MixerStrip::output_release (GdkEventButton *ev)
811 switch (ev->button) {
813 edit_output_configuration ();
821 MixerStrip::output_press (GdkEventButton *ev)
823 using namespace Menu_Helpers;
824 if (!_session->engine().connected()) {
825 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
830 MenuList& citems = output_menu.items();
831 switch (ev->button) {
834 return false; //wait for the mouse-up to pop the dialog
838 output_menu.set_name ("ArdourContextMenu");
840 output_menu_bundles.clear ();
842 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
844 citems.push_back (SeparatorElem());
845 uint32_t const n_with_separator = citems.size ();
847 ARDOUR::BundleList current = _route->output()->bundles_connected ();
849 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
851 /* give user bundles first chance at being in the menu */
853 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
854 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
855 maybe_add_bundle_to_output_menu (*i, current);
859 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
860 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
861 maybe_add_bundle_to_output_menu (*i, current);
865 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
866 RouteList copy = *routes;
867 copy.sort (RouteCompareByName ());
868 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
869 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
872 if (citems.size() == n_with_separator) {
873 /* no routes added; remove the separator */
877 if (!ARDOUR::Profile->get_mixbus()) {
878 citems.push_back (SeparatorElem());
880 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
883 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
884 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
890 citems.push_back (SeparatorElem());
891 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
893 output_menu.popup (1, ev->time);
904 MixerStrip::input_release (GdkEventButton *ev)
906 switch (ev->button) {
909 edit_input_configuration ();
921 MixerStrip::input_press (GdkEventButton *ev)
923 using namespace Menu_Helpers;
925 MenuList& citems = input_menu.items();
926 input_menu.set_name ("ArdourContextMenu");
929 if (!_session->engine().connected()) {
930 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
935 if (_session->actively_recording() && _route->record_enabled())
938 switch (ev->button) {
941 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
945 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
947 citems.push_back (SeparatorElem());
948 uint32_t const n_with_separator = citems.size ();
950 input_menu_bundles.clear ();
952 ARDOUR::BundleList current = _route->input()->bundles_connected ();
954 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
956 /* give user bundles first chance at being in the menu */
958 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
959 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
960 maybe_add_bundle_to_input_menu (*i, current);
964 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
965 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
966 maybe_add_bundle_to_input_menu (*i, current);
970 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
971 RouteList copy = *routes;
972 copy.sort (RouteCompareByName ());
973 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
974 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
977 if (citems.size() == n_with_separator) {
978 /* no routes added; remove the separator */
982 citems.push_back (SeparatorElem());
983 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
986 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
987 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
992 citems.push_back (SeparatorElem());
993 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
995 input_menu.popup (1, ev->time);
1006 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1008 if (ignore_toggle) {
1012 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1014 if (std::find (current.begin(), current.end(), c) == current.end()) {
1015 _route->input()->connect_ports_to_bundle (c, true, this);
1017 _route->input()->disconnect_ports_from_bundle (c, this);
1022 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1024 if (ignore_toggle) {
1028 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1030 if (std::find (current.begin(), current.end(), c) == current.end()) {
1031 _route->output()->connect_ports_to_bundle (c, true, this);
1033 _route->output()->disconnect_ports_from_bundle (c, this);
1038 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1040 using namespace Menu_Helpers;
1042 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1046 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1047 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1051 if (i != input_menu_bundles.end()) {
1055 input_menu_bundles.push_back (b);
1057 MenuList& citems = input_menu.items();
1059 std::string n = b->name ();
1060 replace_all (n, "_", " ");
1062 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1066 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1068 using namespace Menu_Helpers;
1070 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1074 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1075 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1079 if (i != output_menu_bundles.end()) {
1083 output_menu_bundles.push_back (b);
1085 MenuList& citems = output_menu.items();
1087 std::string n = b->name ();
1088 replace_all (n, "_", " ");
1090 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1094 MixerStrip::update_diskstream_display ()
1096 if (is_track() && input_selector) {
1097 input_selector->hide_all ();
1100 route_color_changed ();
1104 MixerStrip::connect_to_pan ()
1106 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1108 panstate_connection.disconnect ();
1109 panstyle_connection.disconnect ();
1111 if (!_route->panner()) {
1115 boost::shared_ptr<Pannable> p = _route->pannable ();
1117 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1118 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1120 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1121 * However, that only works a panner was previously set.
1123 * PannerUI must remain subscribed to _panshell->Changed() in case
1124 * we switch the panner eg. AUX-Send and back
1125 * _route->panner_shell()->Changed() vs _panshell->Changed
1127 if (panners._panner == 0) {
1128 panners.panshell_changed ();
1130 update_panner_choices();
1134 MixerStrip::update_panner_choices ()
1136 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1137 if (!_route->panner_shell()) { return; }
1139 uint32_t in = _route->output()->n_ports().n_audio();
1141 if (_route->panner()) {
1142 in = _route->panner()->in().n_audio();
1145 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1149 * Output port labelling
1150 * =====================
1152 * Case 1: Each output has one connection, all connections are to system:playback_%i
1153 * out 1 -> system:playback_1
1154 * out 2 -> system:playback_2
1155 * out 3 -> system:playback_3
1158 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1159 * out 1 -> ardour:track_x/in 1
1160 * out 2 -> ardour:track_x/in 2
1161 * Display as: track_x
1163 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1164 * out 1 -> program x:foo
1165 * out 2 -> program x:foo
1166 * Display as: program x
1168 * Case 4: No connections (Disconnected)
1171 * Default case (unusual routing):
1172 * Display as: *number of connections*
1176 * .-----------------------------------------------.
1178 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1179 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1180 * '-----------------------------------------------'
1181 * .-----------------------------------------------.
1184 * '-----------------------------------------------'
1188 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1192 boost::shared_ptr<Port> port;
1193 vector<string> port_connections;
1195 uint32_t total_connection_count = 0;
1196 uint32_t io_connection_count = 0;
1197 uint32_t ardour_connection_count = 0;
1198 uint32_t system_connection_count = 0;
1199 uint32_t other_connection_count = 0;
1200 uint32_t typed_connection_count = 0;
1202 ostringstream label;
1204 bool have_label = false;
1205 bool each_io_has_one_connection = true;
1207 string connection_name;
1208 string ardour_track_name;
1209 string other_connection_type;
1210 string system_ports;
1213 ostringstream tooltip;
1214 char * tooltip_cstr;
1216 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1217 DataType dt = DataType::AUDIO;
1218 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1219 dt = DataType::MIDI;
1220 // avoid further confusion with Midi-tracks that have a synth.
1221 // Audio-ports may be connected, but button says "Disconnected"
1222 tooltip << _("MIDI ");
1226 io_count = route->n_inputs().n_total();
1227 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1229 io_count = route->n_outputs().n_total();
1230 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1234 for (io_index = 0; io_index < io_count; ++io_index) {
1236 port = route->input()->nth (io_index);
1238 port = route->output()->nth (io_index);
1241 port_connections.clear ();
1242 port->get_connections(port_connections);
1244 //ignore any port connections that don't match our DataType
1245 if (port->type() != dt) {
1246 if (!port_connections.empty()) {
1247 ++typed_connection_count;
1252 io_connection_count = 0;
1254 if (!port_connections.empty()) {
1255 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1257 string& connection_name (*i);
1259 if (connection_name.find("system:") == 0) {
1260 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1263 if (io_connection_count == 0) {
1264 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1266 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1269 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1272 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1273 if (ardour_track_name.empty()) {
1274 // "ardour:Master/in 1" -> "ardour:Master/"
1275 string::size_type slash = connection_name.find("/");
1276 if (slash != string::npos) {
1277 ardour_track_name = connection_name.substr(0, slash + 1);
1281 if (connection_name.find(ardour_track_name) == 0) {
1282 ++ardour_connection_count;
1284 } else if (!pn.empty()) {
1285 if (system_ports.empty()) {
1288 system_ports += "/" + pn;
1290 if (connection_name.find("system:") == 0) {
1291 ++system_connection_count;
1293 } else if (connection_name.find("system:midi_") == 0) {
1295 // "system:midi_capture_123" -> "123"
1296 system_port = "M " + connection_name.substr(20);
1298 // "system:midi_playback_123" -> "123"
1299 system_port = "M " + connection_name.substr(21);
1302 if (system_ports.empty()) {
1303 system_ports += system_port;
1305 system_ports += "/" + system_port;
1308 ++system_connection_count;
1310 } else if (connection_name.find("system:") == 0) {
1312 // "system:capture_123" -> "123"
1313 system_port = connection_name.substr(15);
1315 // "system:playback_123" -> "123"
1316 system_port = connection_name.substr(16);
1319 if (system_ports.empty()) {
1320 system_ports += system_port;
1322 system_ports += "/" + system_port;
1325 ++system_connection_count;
1327 if (other_connection_type.empty()) {
1328 // "jamin:in 1" -> "jamin:"
1329 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1332 if (connection_name.find(other_connection_type) == 0) {
1333 ++other_connection_count;
1337 ++total_connection_count;
1338 ++io_connection_count;
1342 if (io_connection_count != 1) {
1343 each_io_has_one_connection = false;
1347 if (total_connection_count == 0) {
1348 tooltip << endl << _("Disconnected");
1351 tooltip_cstr = new char[tooltip.str().size() + 1];
1352 strcpy(tooltip_cstr, tooltip.str().c_str());
1355 set_tooltip (&input_button, tooltip_cstr);
1357 set_tooltip (&output_button, tooltip_cstr);
1360 delete [] tooltip_cstr;
1362 if (each_io_has_one_connection) {
1363 if (total_connection_count == ardour_connection_count) {
1364 // all connections are to the same track in ardour
1365 // "ardour:Master/" -> "Master"
1366 string::size_type slash = ardour_track_name.find("/");
1367 if (slash != string::npos) {
1368 const size_t ppps = RouteUI::program_port_prefix.size () + 1; // "ardour:"
1369 label << ardour_track_name.substr (ppps, slash - ppps);
1373 else if (total_connection_count == system_connection_count) {
1374 // all connections are to system ports
1375 label << system_ports;
1378 else if (total_connection_count == other_connection_count) {
1379 // all connections are to the same external program eg jamin
1380 // "jamin:" -> "jamin"
1381 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1387 if (total_connection_count == 0) {
1391 // Odd configuration
1392 label << "*" << total_connection_count << "*";
1394 if (typed_connection_count > 0) {
1395 label << "\u2295"; // circled plus
1400 input_button.set_text (label.str());
1402 output_button.set_text (label.str());
1407 MixerStrip::update_input_display ()
1409 update_io_button (_route, _width, true);
1410 panners.setup_pan ();
1412 if (has_audio_outputs ()) {
1413 panners.show_all ();
1415 panners.hide_all ();
1421 MixerStrip::update_output_display ()
1423 update_io_button (_route, _width, false);
1424 gpm.setup_meters ();
1425 panners.setup_pan ();
1427 if (has_audio_outputs ()) {
1428 panners.show_all ();
1430 panners.hide_all ();
1435 MixerStrip::fast_update ()
1437 gpm.update_meters ();
1441 MixerStrip::diskstream_changed ()
1443 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1447 MixerStrip::io_changed_proxy ()
1449 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1453 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1455 boost::shared_ptr<Port> a = wa.lock ();
1456 boost::shared_ptr<Port> b = wb.lock ();
1458 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1459 update_input_display ();
1460 set_width_enum (_width, this);
1463 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1464 update_output_display ();
1465 set_width_enum (_width, this);
1470 MixerStrip::setup_comment_button ()
1475 if (_route->comment().empty ()) {
1476 _comment_button.unset_bg (STATE_NORMAL);
1477 _comment_button.set_text (_("Comments"));
1479 _comment_button.modify_bg (STATE_NORMAL, color ());
1480 _comment_button.set_text (_("*Comments*"));
1485 if (_route->comment().empty ()) {
1486 _comment_button.unset_bg (STATE_NORMAL);
1487 _comment_button.set_text (_("Cmt"));
1489 _comment_button.modify_bg (STATE_NORMAL, color ());
1490 _comment_button.set_text (_("*Cmt*"));
1496 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1502 MixerStrip::select_route_group (GdkEventButton *ev)
1504 using namespace Menu_Helpers;
1506 if (ev->button == 1) {
1508 if (group_menu == 0) {
1510 PropertyList* plist = new PropertyList();
1512 plist->add (Properties::gain, true);
1513 plist->add (Properties::mute, true);
1514 plist->add (Properties::solo, true);
1516 group_menu = new RouteGroupMenu (_session, plist);
1520 r.push_back (route ());
1521 group_menu->build (r);
1522 group_menu->menu()->popup (1, ev->time);
1529 MixerStrip::route_group_changed ()
1531 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1533 RouteGroup *rg = _route->route_group();
1536 group_button.set_text (PBD::short_version (rg->name(), 5));
1540 group_button.set_text (_("Grp"));
1543 group_button.set_text (_("~G"));
1550 MixerStrip::route_color_changed ()
1552 name_button.modify_bg (STATE_NORMAL, color());
1553 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1554 reset_strip_style ();
1558 MixerStrip::show_passthru_color ()
1560 reset_strip_style ();
1564 MixerStrip::build_route_ops_menu ()
1566 using namespace Menu_Helpers;
1567 route_ops_menu = new Menu;
1568 route_ops_menu->set_name ("ArdourContextMenu");
1570 MenuList& items = route_ops_menu->items();
1572 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1574 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1576 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1578 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1580 items.push_back (SeparatorElem());
1582 if (!_route->is_master()) {
1583 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1585 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1586 rename_menu_item = &items.back();
1588 items.push_back (SeparatorElem());
1589 items.push_back (CheckMenuElem (_("Active")));
1590 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1591 i->set_active (_route->active());
1592 i->set_sensitive(! _session->transport_rolling());
1593 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1595 if (!Profile->get_mixbus ()) {
1596 items.push_back (SeparatorElem());
1597 items.push_back (CheckMenuElem (_("Strict I/O")));
1598 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1599 i->set_active (_route->strict_io());
1600 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1603 items.push_back (SeparatorElem());
1605 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1607 items.push_back (SeparatorElem());
1608 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1609 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1610 denormal_menu_item->set_active (_route->denormal_protection());
1612 items.push_back (SeparatorElem());
1613 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1616 /* note that this relies on selection being shared across editor and
1617 mixer (or global to the backend, in the future), which is the only
1618 sane thing for users anyway.
1621 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1623 Selection& selection (PublicEditor::instance().get_selection());
1624 if (!selection.selected (rtav)) {
1625 selection.set (rtav);
1628 if (!_route->is_master()) {
1629 items.push_back (SeparatorElem());
1630 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1633 items.push_back (SeparatorElem());
1634 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1640 MixerStrip::name_button_button_press (GdkEventButton* ev)
1642 if (ev->button == 3) {
1643 list_route_operations ();
1645 /* do not allow rename if the track is record-enabled */
1646 rename_menu_item->set_sensitive (!_route->record_enabled());
1647 route_ops_menu->popup (1, ev->time);
1656 MixerStrip::name_button_button_release (GdkEventButton* ev)
1658 if (ev->button == 1) {
1659 list_route_operations ();
1661 /* do not allow rename if the track is record-enabled */
1662 rename_menu_item->set_sensitive (!_route->record_enabled());
1663 route_ops_menu->popup (1, ev->time);
1670 MixerStrip::number_button_button_press (GdkEventButton* ev)
1672 if ( ev->button == 3 ) {
1673 list_route_operations ();
1675 /* do not allow rename if the track is record-enabled */
1676 rename_menu_item->set_sensitive (!_route->record_enabled());
1677 route_ops_menu->popup (1, ev->time);
1686 MixerStrip::list_route_operations ()
1688 delete route_ops_menu;
1689 build_route_ops_menu ();
1693 MixerStrip::set_selected (bool yn)
1695 AxisView::set_selected (yn);
1697 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1698 global_frame.set_name ("MixerStripSelectedFrame");
1700 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1701 global_frame.set_name ("MixerStripFrame");
1703 global_frame.queue_draw ();
1706 // processor_box.deselect_all_processors();
1710 MixerStrip::property_changed (const PropertyChange& what_changed)
1712 RouteUI::property_changed (what_changed);
1714 if (what_changed.contains (ARDOUR::Properties::name)) {
1720 MixerStrip::name_changed ()
1724 name_button.set_text (_route->name());
1727 name_button.set_text (PBD::short_version (_route->name(), 5));
1731 set_tooltip (name_button, _route->name());
1733 if (_session->config.get_track_name_number()) {
1734 const int64_t track_number = _route->track_number ();
1735 if (track_number == 0) {
1736 number_label.set_text ("-");
1738 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1741 number_label.set_text ("");
1746 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1748 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1752 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1754 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1758 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1760 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1764 MixerStrip::width_button_pressed (GdkEventButton* ev)
1766 if (ev->button != 1) {
1770 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1773 _mixer.set_strip_width (Narrow, true);
1777 _mixer.set_strip_width (Wide, true);
1783 set_width_enum (Narrow, this);
1786 set_width_enum (Wide, this);
1795 MixerStrip::hide_clicked ()
1797 // LAME fix to reset the button status for when it is redisplayed (part 1)
1798 hide_button.set_sensitive(false);
1801 Hiding(); /* EMIT_SIGNAL */
1803 _mixer.hide_strip (this);
1807 hide_button.set_sensitive(true);
1811 MixerStrip::set_embedded (bool yn)
1817 MixerStrip::map_frozen ()
1819 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1821 boost::shared_ptr<AudioTrack> at = audio_track();
1824 switch (at->freeze_state()) {
1825 case AudioTrack::Frozen:
1826 processor_box.set_sensitive (false);
1827 hide_redirect_editors ();
1830 processor_box.set_sensitive (true);
1831 // XXX need some way, maybe, to retoggle redirect editors
1835 processor_box.set_sensitive (true);
1840 MixerStrip::hide_redirect_editors ()
1842 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1846 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1848 boost::shared_ptr<Processor> processor (p.lock ());
1853 Gtk::Window* w = processor_box.get_processor_ui (processor);
1861 MixerStrip::reset_strip_style ()
1863 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1865 gpm.set_fader_name ("SendStripBase");
1869 if (is_midi_track()) {
1870 if (_route->active()) {
1871 set_name ("MidiTrackStripBase");
1873 set_name ("MidiTrackStripBaseInactive");
1875 gpm.set_fader_name ("MidiTrackFader");
1876 } else if (is_audio_track()) {
1877 if (_route->active()) {
1878 set_name ("AudioTrackStripBase");
1880 set_name ("AudioTrackStripBaseInactive");
1882 gpm.set_fader_name ("AudioTrackFader");
1884 if (_route->active()) {
1885 set_name ("AudioBusStripBase");
1887 set_name ("AudioBusStripBaseInactive");
1889 gpm.set_fader_name ("AudioBusFader");
1891 /* (no MIDI busses yet) */
1898 MixerStrip::engine_stopped ()
1903 MixerStrip::engine_running ()
1908 MixerStrip::meter_point_string (MeterPoint mp)
1921 case MeterPostFader:
1938 return S_("Meter|In");
1942 return S_("Meter|Pr");
1945 case MeterPostFader:
1946 return S_("Meter|Po");
1950 return S_("Meter|O");
1955 return S_("Meter|C");
1964 /** Called when the monitor-section state */
1966 MixerStrip::monitor_changed ()
1968 assert (monitor_section_button);
1969 if (_session->monitor_active()) {
1970 monitor_section_button->set_name ("master monitor section button active");
1972 monitor_section_button->set_name ("master monitor section button normal");
1976 /** Called when the metering point has changed */
1978 MixerStrip::meter_changed ()
1980 meter_point_button.set_text (meter_point_string (_route->meter_point()));
1981 gpm.setup_meters ();
1982 // reset peak when meter point changes
1983 gpm.reset_peak_display();
1986 /** The bus that we are displaying sends to has changed, or been turned off.
1987 * @param send_to New bus that we are displaying sends to, or 0.
1990 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
1992 RouteUI::bus_send_display_changed (send_to);
1995 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2000 revert_to_default_display ();
2003 revert_to_default_display ();
2008 MixerStrip::drop_send ()
2010 boost::shared_ptr<Send> current_send;
2012 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2013 current_send->set_metering (false);
2016 send_gone_connection.disconnect ();
2017 input_button.set_sensitive (true);
2018 output_button.set_sensitive (true);
2019 group_button.set_sensitive (true);
2020 set_invert_sensitive (true);
2021 meter_point_button.set_sensitive (true);
2022 mute_button->set_sensitive (true);
2023 solo_button->set_sensitive (true);
2024 rec_enable_button->set_sensitive (true);
2025 solo_isolated_led->set_sensitive (true);
2026 solo_safe_led->set_sensitive (true);
2027 monitor_input_button->set_sensitive (true);
2028 monitor_disk_button->set_sensitive (true);
2029 _comment_button.set_sensitive (true);
2033 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2035 _current_delivery = d;
2036 DeliveryChanged (_current_delivery);
2040 MixerStrip::show_send (boost::shared_ptr<Send> send)
2046 set_current_delivery (send);
2048 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2049 send->set_metering (true);
2050 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2052 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2053 gain_meter().setup_meters ();
2055 uint32_t const in = _current_delivery->pans_required();
2056 uint32_t const out = _current_delivery->pan_outs();
2058 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2059 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2060 panner_ui().setup_pan ();
2061 panner_ui().set_send_drawing_mode (true);
2062 panner_ui().show_all ();
2064 input_button.set_sensitive (false);
2065 group_button.set_sensitive (false);
2066 set_invert_sensitive (false);
2067 meter_point_button.set_sensitive (false);
2068 mute_button->set_sensitive (false);
2069 solo_button->set_sensitive (false);
2070 rec_enable_button->set_sensitive (false);
2071 solo_isolated_led->set_sensitive (false);
2072 solo_safe_led->set_sensitive (false);
2073 monitor_input_button->set_sensitive (false);
2074 monitor_disk_button->set_sensitive (false);
2075 _comment_button.set_sensitive (false);
2077 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2078 output_button.set_sensitive (false);
2081 reset_strip_style ();
2085 MixerStrip::revert_to_default_display ()
2089 set_current_delivery (_route->main_outs ());
2091 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2092 gain_meter().setup_meters ();
2094 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2095 update_panner_choices();
2096 panner_ui().setup_pan ();
2097 panner_ui().set_send_drawing_mode (false);
2099 if (has_audio_outputs ()) {
2100 panners.show_all ();
2102 panners.hide_all ();
2105 reset_strip_style ();
2109 MixerStrip::set_button_names ()
2113 mute_button->set_text (_("Mute"));
2114 monitor_input_button->set_text (_("In"));
2115 monitor_disk_button->set_text (_("Disk"));
2116 if (monitor_section_button) {
2117 monitor_section_button->set_text (_("Mon"));
2120 if (_route && _route->solo_safe()) {
2121 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2123 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2125 if (!Config->get_solo_control_is_listen_control()) {
2126 solo_button->set_text (_("Solo"));
2128 switch (Config->get_listen_position()) {
2129 case AfterFaderListen:
2130 solo_button->set_text (_("AFL"));
2132 case PreFaderListen:
2133 solo_button->set_text (_("PFL"));
2137 solo_isolated_led->set_text (_("Iso"));
2138 solo_safe_led->set_text (S_("SoloLock|Lock"));
2142 mute_button->set_text (S_("Mute|M"));
2143 monitor_input_button->set_text (S_("MonitorInput|I"));
2144 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2145 if (monitor_section_button) {
2146 monitor_section_button->set_text (S_("Mon|O"));
2149 if (_route && _route->solo_safe()) {
2150 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2152 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2154 if (!Config->get_solo_control_is_listen_control()) {
2155 solo_button->set_text (S_("Solo|S"));
2157 switch (Config->get_listen_position()) {
2158 case AfterFaderListen:
2159 solo_button->set_text (S_("AfterFader|A"));
2161 case PreFaderListen:
2162 solo_button->set_text (S_("Prefader|P"));
2167 solo_isolated_led->set_text (S_("SoloIso|I"));
2168 solo_safe_led->set_text (S_("SoloLock|L"));
2173 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2175 meter_point_button.set_text ("");
2180 MixerStrip::plugin_selector()
2182 return _mixer.plugin_selector();
2186 MixerStrip::hide_things ()
2188 processor_box.hide_things ();
2192 MixerStrip::input_active_button_press (GdkEventButton*)
2194 /* nothing happens on press */
2199 MixerStrip::input_active_button_release (GdkEventButton* ev)
2201 boost::shared_ptr<MidiTrack> mt = midi_track ();
2207 boost::shared_ptr<RouteList> rl (new RouteList);
2209 rl->push_back (route());
2211 _session->set_exclusive_input_active (rl, !mt->input_active(),
2212 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2218 MixerStrip::midi_input_status_changed ()
2220 if (midi_input_enable_button) {
2221 boost::shared_ptr<MidiTrack> mt = midi_track ();
2223 midi_input_enable_button->set_active (mt->input_active ());
2228 MixerStrip::state_id () const
2230 return string_compose ("strip %1", _route->id().to_s());
2234 MixerStrip::parameter_changed (string p)
2236 if (p == _visibility.get_state_name()) {
2237 /* The user has made changes to the mixer strip visibility, so get
2238 our VisibilityGroup to reflect these changes in our widgets.
2240 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2242 else if (p == "track-name-number") {
2245 else if (p == "use-monitor-bus") {
2246 if (monitor_section_button) {
2247 if (mute_button->get_parent()) {
2248 mute_button->get_parent()->remove(*mute_button);
2250 if (monitor_section_button->get_parent()) {
2251 monitor_section_button->get_parent()->remove(*monitor_section_button);
2253 if (Config->get_use_monitor_bus ()) {
2254 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2255 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2256 mute_button->show();
2257 monitor_section_button->show();
2259 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2260 mute_button->show();
2266 /** Called to decide whether the solo isolate / solo lock button visibility should
2267 * be overridden from that configured by the user. We do this for the master bus.
2269 * @return optional value that is present if visibility state should be overridden.
2271 boost::optional<bool>
2272 MixerStrip::override_solo_visibility () const
2274 if (_route && _route->is_master ()) {
2275 return boost::optional<bool> (false);
2278 return boost::optional<bool> ();
2282 MixerStrip::add_input_port (DataType t)
2284 _route->input()->add_port ("", this, t);
2288 MixerStrip::add_output_port (DataType t)
2290 _route->output()->add_port ("", this, t);
2294 MixerStrip::route_active_changed ()
2296 reset_strip_style ();
2300 MixerStrip::copy_processors ()
2302 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2306 MixerStrip::cut_processors ()
2308 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2312 MixerStrip::paste_processors ()
2314 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2318 MixerStrip::select_all_processors ()
2320 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2324 MixerStrip::deselect_all_processors ()
2326 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2330 MixerStrip::delete_processors ()
2332 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2336 MixerStrip::toggle_processors ()
2338 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2342 MixerStrip::ab_plugins ()
2344 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2348 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2350 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2353 if (ev->button == 3) {
2354 popup_level_meter_menu (ev);
2362 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2364 using namespace Gtk::Menu_Helpers;
2366 Gtk::Menu* m = manage (new Menu);
2367 MenuList& items = m->items ();
2369 RadioMenuItem::Group group;
2371 _suspend_menu_callbacks = true;
2372 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2373 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2374 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2375 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2376 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2378 if (gpm.meter_channels().n_audio() == 0) {
2379 m->popup (ev->button, ev->time);
2380 _suspend_menu_callbacks = false;
2384 RadioMenuItem::Group tgroup;
2385 items.push_back (SeparatorElem());
2387 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2388 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2389 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2390 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2391 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2392 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2393 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2394 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2395 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2396 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2397 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2400 if (_route->is_master()) {
2403 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2404 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2405 /* non-master bus */
2408 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2415 MeterType cmt = _route->meter_type();
2416 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2418 items.push_back (SeparatorElem());
2419 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2420 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2421 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2422 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2423 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2424 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2426 m->popup (ev->button, ev->time);
2427 _suspend_menu_callbacks = false;
2431 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2432 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2434 using namespace Menu_Helpers;
2436 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2437 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2438 i->set_active (_route->meter_point() == point);
2442 MixerStrip::set_meter_point (MeterPoint p)
2444 if (_suspend_menu_callbacks) return;
2445 _route->set_meter_point (p);
2449 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2450 RadioMenuItem::Group& group, string const & name, MeterType type)
2452 using namespace Menu_Helpers;
2454 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2455 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2456 i->set_active (_route->meter_type() == type);
2460 MixerStrip::set_meter_type (MeterType t)
2462 if (_suspend_menu_callbacks) return;