2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include <sigc++/bind.h>
25 #include "pbd/convert.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/replace_all.h"
28 #include "pbd/stacktrace.h"
30 #include <gtkmm2ext/gtk_ui.h>
31 #include <gtkmm2ext/utils.h>
32 #include <gtkmm2ext/choice.h>
33 #include <gtkmm2ext/doi.h>
34 #include <gtkmm2ext/slider_controller.h>
35 #include <gtkmm2ext/bindable_button.h>
37 #include "ardour/amp.h"
38 #include "ardour/audio_track.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/internal_send.h"
41 #include "ardour/meter.h"
42 #include "ardour/midi_track.h"
43 #include "ardour/pannable.h"
44 #include "ardour/panner.h"
45 #include "ardour/panner_shell.h"
46 #include "ardour/panner_manager.h"
47 #include "ardour/port.h"
48 #include "ardour/profile.h"
49 #include "ardour/route.h"
50 #include "ardour/route_group.h"
51 #include "ardour/send.h"
52 #include "ardour/session.h"
53 #include "ardour/types.h"
54 #include "ardour/user_bundle.h"
55 #include "ardour/vca.h"
56 #include "ardour/vca_manager.h"
58 #include "ardour_window.h"
59 #include "mixer_strip.h"
62 #include "ardour_button.h"
63 #include "public_editor.h"
65 #include "io_selector.h"
67 #include "gui_thread.h"
68 #include "route_group_menu.h"
69 #include "meter_patterns.h"
71 #include "ui_config.h"
75 using namespace ARDOUR;
76 using namespace ARDOUR_UI_UTILS;
79 using namespace Gtkmm2ext;
81 using namespace ArdourMeter;
83 MixerStrip* MixerStrip::_entered_mixer_strip;
84 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
86 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
90 , _mixer_owned (in_mixer)
91 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
94 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
95 , rec_mon_table (2, 2)
96 , solo_iso_table (1, 2)
97 , mute_solo_table (1, 2)
98 , bottom_button_table (1, 3)
99 , meter_point_button (_("pre"))
100 , monitor_section_button (0)
101 , midi_input_enable_button (0)
102 , _comment_button (_("Comments"))
103 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
104 , _visibility (X_("mixer-element-visibility"))
109 /* the editor mixer strip: don't destroy it every time
110 the underlying route goes away.
113 self_destruct = false;
117 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
121 , _mixer_owned (in_mixer)
122 , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
125 , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
126 , rec_mon_table (2, 2)
127 , solo_iso_table (1, 2)
128 , mute_solo_table (1, 2)
129 , bottom_button_table (1, 3)
130 , meter_point_button (_("pre"))
131 , monitor_section_button (0)
132 , midi_input_enable_button (0)
133 , _comment_button (_("Comments"))
134 , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
135 , _visibility (X_("mixer-element-visibility"))
144 _entered_mixer_strip= 0;
147 ignore_comment_edit = false;
148 ignore_toggle = false;
153 /* the length of this string determines the width of the mixer strip when it is set to `wide' */
154 longest_label = "longest label";
156 string t = _("Click to toggle the width of this mixer strip.");
158 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
161 width_button.set_icon (ArdourIcon::StripWidth);
162 set_tooltip (width_button, t);
164 hide_button.set_icon (ArdourIcon::CloseCross);
165 set_tooltip (&hide_button, _("Hide this mixer strip"));
167 input_button_box.set_spacing(2);
169 input_button.set_text (_("Input"));
170 input_button.set_name ("mixer strip button");
171 input_button_box.pack_start (input_button, true, true);
173 output_button.set_text (_("Output"));
174 output_button.set_name ("mixer strip button");
176 set_tooltip (&meter_point_button, _("Click to select metering point"));
177 meter_point_button.set_name ("mixer strip button");
179 bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
181 meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
182 meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
184 hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
186 solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
187 solo_isolated_led->show ();
188 solo_isolated_led->set_no_show_all (true);
189 solo_isolated_led->set_name (X_("solo isolate"));
190 solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
191 solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
192 UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
194 solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
195 solo_safe_led->show ();
196 solo_safe_led->set_no_show_all (true);
197 solo_safe_led->set_name (X_("solo safe"));
198 solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
199 solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
200 UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
202 solo_safe_led->set_text (S_("SoloLock|Lock"));
203 solo_isolated_led->set_text (_("Iso"));
205 solo_iso_table.set_homogeneous (true);
206 solo_iso_table.set_spacings (2);
207 if (!ARDOUR::Profile->get_trx()) {
208 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
209 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
211 solo_iso_table.show ();
213 vca_button = manage (new ArdourButton (ArdourButton::default_elements));
214 vca_button->set_no_show_all (true);
215 vca_button->set_name (X_("vca assign"));
216 vca_button->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
217 vca_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::vca_button_release), false);
218 UI::instance()->set_tip (*vca_button, _("VCA assignments"));
219 vca_button->set_text (_("-vca-"));
222 rec_mon_table.set_homogeneous (true);
223 rec_mon_table.set_row_spacings (2);
224 rec_mon_table.set_col_spacings (2);
225 if (ARDOUR::Profile->get_mixbus()) {
226 rec_mon_table.resize (1, 3);
227 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
228 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
229 } else if (!ARDOUR::Profile->get_trx()) {
230 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
231 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
233 rec_mon_table.show ();
235 if (solo_isolated_led) {
236 button_size_group->add_widget (*solo_isolated_led);
239 button_size_group->add_widget (*solo_safe_led);
242 if (!ARDOUR::Profile->get_mixbus()) {
243 if (rec_enable_button) {
244 button_size_group->add_widget (*rec_enable_button);
246 if (monitor_disk_button) {
247 button_size_group->add_widget (*monitor_disk_button);
249 if (monitor_input_button) {
250 button_size_group->add_widget (*monitor_input_button);
254 mute_solo_table.set_homogeneous (true);
255 mute_solo_table.set_spacings (2);
257 bottom_button_table.set_spacings (2);
258 bottom_button_table.set_homogeneous (true);
259 bottom_button_table.attach (group_button, 1, 2, 0, 1);
260 bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
262 name_button.set_name ("mixer strip button");
263 name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
264 name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
266 set_tooltip (&group_button, _("Mix group"));
267 group_button.set_name ("mixer strip button");
269 _comment_button.set_name (X_("mixer strip button"));
270 _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
272 // TODO implement ArdourKnob::on_size_request properly
273 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
274 trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
276 trim_control.set_tooltip_prefix (_("Trim: "));
277 trim_control.set_name ("trim knob");
278 trim_control.set_no_show_all (true);
279 input_button_box.pack_start (trim_control, false, false);
281 global_vpacker.set_border_width (1);
282 global_vpacker.set_spacing (0);
284 width_button.set_name ("mixer strip button");
285 hide_button.set_name ("mixer strip button");
287 width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
288 hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
290 // width_hide_box.set_border_width (1);
291 width_hide_box.set_spacing (2);
292 width_hide_box.pack_start (width_button, false, true);
293 width_hide_box.pack_start (number_label, true, true);
294 width_hide_box.pack_end (hide_button, false, true);
296 number_label.set_text ("-");
297 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
298 number_label.set_no_show_all ();
299 number_label.set_name ("tracknumber label");
300 number_label.set_fixed_colors (0x80808080, 0x80808080);
301 number_label.set_alignment (.5, .5);
302 number_label.set_fallthrough_to_parent (true);
304 global_vpacker.set_spacing (2);
305 if (!ARDOUR::Profile->get_trx()) {
306 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
307 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
308 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
309 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
310 global_vpacker.pack_start (processor_box, true, true);
312 global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
313 global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
314 global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
315 global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
316 global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
317 global_vpacker.pack_start (*vca_button, Gtk::PACK_SHRINK);
318 global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
319 if (!ARDOUR::Profile->get_trx()) {
320 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
321 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
323 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
326 global_frame.add (global_vpacker);
327 global_frame.set_shadow_type (Gtk::SHADOW_IN);
328 global_frame.set_name ("BaseFrame");
332 /* force setting of visible selected status */
335 set_selected (false);
340 _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
341 _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
343 input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
344 input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
345 input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
347 input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
348 output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
350 output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
351 output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
352 output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
354 number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
356 name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
357 name_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_release), false);
359 group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
363 /* start off as a passthru strip. we'll correct this, if necessary,
364 in update_diskstream_display().
367 /* start off as a passthru strip. we'll correct this, if necessary,
368 in update_diskstream_display().
371 if (is_midi_track()) {
372 set_name ("MidiTrackStripBase");
374 set_name ("AudioTrackStripBase");
377 add_events (Gdk::BUTTON_RELEASE_MASK|
378 Gdk::ENTER_NOTIFY_MASK|
379 Gdk::LEAVE_NOTIFY_MASK|
381 Gdk::KEY_RELEASE_MASK);
383 set_flags (get_flags() | Gtk::CAN_FOCUS);
385 AudioEngine::instance()->PortConnectedOrDisconnected.connect (
386 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
389 /* Add the widgets under visibility control to the VisibilityGroup; the names used here
390 must be the same as those used in RCOptionEditor so that the configuration changes
391 are recognised when they occur.
393 _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
394 _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
395 _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
396 _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
397 _visibility.add (&output_button, X_("Output"), _("Output"), false);
398 _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
399 _visibility.add (vca_button, X_("VCA"), _("VCA Assigns"), false);
401 parameter_changed (X_("mixer-element-visibility"));
402 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
403 Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
404 _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
406 //watch for mouse enter/exit so we can do some stuff
407 signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
408 signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
410 gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
413 MixerStrip::~MixerStrip ()
415 CatchDeletion (this);
417 if (this ==_entered_mixer_strip)
418 _entered_mixer_strip = NULL;
422 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
424 _entered_mixer_strip = this;
426 //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
427 //because the mixerstrip control is a parent that encompasses the strip
428 deselect_all_processors();
434 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
436 //if we have moved outside our strip, but not into a child view, then deselect ourselves
437 if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
438 _entered_mixer_strip= 0;
440 //clear keyboard focus in the gain display. this is cheesy but fixes a longstanding "bug" where the user starts typing in the gain entry, and leaves it active, thereby prohibiting other keybindings from working
441 gpm.gain_display.set_sensitive(false);
443 gpm.gain_display.set_sensitive(true);
445 //if we leave this mixer strip we need to clear out any selections
446 //processor_box.processor_display.select_none(); //but this doesn't work, because it gets triggered when (for example) you open the menu or start a drag
453 MixerStrip::set_route (boost::shared_ptr<Route> rt)
455 //the rec/monitor stuff only shows up for tracks.
456 //the show_sends only shows up for buses.
457 //remove them all here, and we may add them back later
458 if (show_sends_button->get_parent()) {
459 rec_mon_table.remove (*show_sends_button);
461 if (rec_enable_button->get_parent()) {
462 rec_mon_table.remove (*rec_enable_button);
464 if (monitor_input_button->get_parent()) {
465 rec_mon_table.remove (*monitor_input_button);
467 if (monitor_disk_button->get_parent()) {
468 rec_mon_table.remove (*monitor_disk_button);
470 if (group_button.get_parent()) {
471 bottom_button_table.remove (group_button);
474 RouteUI::set_route (rt);
476 /* ProcessorBox needs access to _route so that it can read
479 processor_box.set_route (rt);
481 revert_to_default_display ();
483 /* unpack these from the parent and stuff them into our own
487 if (gpm.peak_display.get_parent()) {
488 gpm.peak_display.get_parent()->remove (gpm.peak_display);
490 if (gpm.gain_display.get_parent()) {
491 gpm.gain_display.get_parent()->remove (gpm.gain_display);
494 gpm.set_type (rt->meter_type());
496 mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
497 mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
499 if (solo_button->get_parent()) {
500 mute_solo_table.remove (*solo_button);
503 if (mute_button->get_parent()) {
504 mute_solo_table.remove (*mute_button);
507 if (route()->is_master()) {
508 solo_button->hide ();
509 mute_button->show ();
510 rec_mon_table.hide ();
511 if (solo_iso_table.get_parent()) {
512 solo_iso_table.get_parent()->remove(solo_iso_table);
514 if (monitor_section_button == 0) {
515 Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
516 _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
518 monitor_section_button = manage (new ArdourButton);
520 monitor_section_button->set_related_action (act);
521 set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
522 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
523 monitor_section_button->show();
524 monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
526 parameter_changed ("use-monitor-bus");
528 bottom_button_table.attach (group_button, 1, 2, 0, 1);
529 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
530 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
531 mute_button->show ();
532 solo_button->show ();
533 rec_mon_table.show ();
536 if (_mixer_owned && route()->is_master() ) {
538 HScrollbar scrollbar;
539 Gtk::Requisition requisition(scrollbar.size_request ());
540 int scrollbar_height = requisition.height;
542 spacer = manage (new EventBox);
543 spacer->set_size_request (-1, scrollbar_height+2);
544 global_vpacker.pack_start (*spacer, false, false);
549 monitor_input_button->show ();
550 monitor_disk_button->show ();
552 monitor_input_button->hide();
553 monitor_disk_button->hide ();
556 if (route()->trim() && route()->trim()->active()) {
557 trim_control.show ();
558 trim_control.set_controllable (route()->trim()->gain_control());
560 trim_control.hide ();
561 boost::shared_ptr<Controllable> none;
562 trim_control.set_controllable (none);
565 if (is_midi_track()) {
566 if (midi_input_enable_button == 0) {
567 midi_input_enable_button = manage (new ArdourButton);
568 midi_input_enable_button->set_name ("midi input button");
569 midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
570 midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
571 midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
572 midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
573 set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
575 input_button_box.remove (*midi_input_enable_button);
577 /* get current state */
578 midi_input_status_changed ();
579 input_button_box.pack_start (*midi_input_enable_button, false, false);
581 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
583 if (midi_input_enable_button) {
584 /* removal from the container will delete it */
585 input_button_box.remove (*midi_input_enable_button);
586 midi_input_enable_button = 0;
590 if (is_audio_track()) {
591 boost::shared_ptr<AudioTrack> at = audio_track();
592 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
597 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
598 rec_enable_button->show();
600 if (ARDOUR::Profile->get_mixbus()) {
601 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
602 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
603 } else if (ARDOUR::Profile->get_trx()) {
604 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
606 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
607 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
614 if (!_route->is_master()) {
615 rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
616 show_sends_button->show();
620 meter_point_button.set_text (meter_point_string (_route->meter_point()));
622 delete route_ops_menu;
625 _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
626 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
627 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
628 _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
630 _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
632 if (_route->panner_shell()) {
633 update_panner_choices();
634 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
637 if (is_audio_track()) {
638 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
641 _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
642 _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::property_changed, this, _1), gui_context());
644 _route->gain_control()->MasterStatusChange.connect (route_connections,
646 boost::bind (&MixerStrip::update_vca_display, this),
649 set_stuff_from_route ();
651 /* now force an update of all the various elements */
653 update_mute_display ();
654 update_solo_display ();
655 update_vca_display ();
658 route_group_changed ();
659 update_track_number_visibility ();
662 panners.setup_pan ();
664 if (has_audio_outputs ()) {
670 update_diskstream_display ();
671 update_input_display ();
672 update_output_display ();
674 add_events (Gdk::BUTTON_RELEASE_MASK);
676 processor_box.show ();
678 if (!route()->is_master() && !route()->is_monitor()) {
679 /* we don't allow master or control routes to be hidden */
684 gpm.reset_peak_display ();
685 gpm.gain_display.show ();
686 gpm.peak_display.show ();
689 width_hide_box.show();
691 global_vpacker.show();
692 mute_solo_table.show();
693 bottom_button_table.show();
695 meter_point_button.show();
696 input_button_box.show_all();
697 output_button.show();
699 _comment_button.show();
701 gpm.gain_automation_state_button.show();
703 parameter_changed ("mixer-element-visibility");
710 MixerStrip::set_stuff_from_route ()
712 /* if width is not set, it will be set by the MixerUI or editor */
714 string str = gui_property ("strip-width");
716 set_width_enum (Width (string_2_enum (str, _width)), this);
721 MixerStrip::set_width_enum (Width w, void* owner)
723 /* always set the gpm width again, things may be hidden */
726 panners.set_width (w);
728 boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
730 _width_owner = owner;
734 if (_width_owner == this) {
735 set_gui_property ("strip-width", enum_2_string (_width));
740 const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
745 if (show_sends_button) {
746 show_sends_button->set_text (_("Aux"));
749 gpm.gain_automation_style_button.set_text (
750 gpm.astyle_string(gain_automation->automation_style()));
751 gpm.gain_automation_state_button.set_text (
752 gpm.astate_string(gain_automation->automation_state()));
754 if (_route->panner()) {
755 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
756 panners.astyle_string(_route->panner()->automation_style()));
757 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
758 panners.astate_string(_route->panner()->automation_state()));
762 // panners expect an even number of horiz. pixels
763 int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
765 set_size_request (width, -1);
771 if (show_sends_button) {
772 show_sends_button->set_text (_("Snd"));
775 gpm.gain_automation_style_button.set_text (
776 gpm.short_astyle_string(gain_automation->automation_style()));
777 gpm.gain_automation_state_button.set_text (
778 gpm.short_astate_string(gain_automation->automation_state()));
779 gain_meter().setup_meters (); // recalc meter width
781 if (_route->panner()) {
782 ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
783 panners.short_astyle_string(_route->panner()->automation_style()));
784 ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
785 panners.short_astate_string(_route->panner()->automation_state()));
789 // panners expect an even number of horiz. pixels
790 int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
792 set_size_request (width, -1);
797 processor_box.set_width (w);
799 update_input_display ();
800 update_output_display ();
801 setup_comment_button ();
802 route_group_changed ();
808 MixerStrip::set_packed (bool yn)
813 set_gui_property ("visible", true);
815 set_gui_property ("visible", false);
820 struct RouteCompareByName {
821 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
822 return a->name().compare (b->name()) < 0;
827 MixerStrip::output_release (GdkEventButton *ev)
829 switch (ev->button) {
831 edit_output_configuration ();
839 MixerStrip::output_press (GdkEventButton *ev)
841 using namespace Menu_Helpers;
842 if (!_session->engine().connected()) {
843 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
848 MenuList& citems = output_menu.items();
849 switch (ev->button) {
852 return false; //wait for the mouse-up to pop the dialog
856 output_menu.set_name ("ArdourContextMenu");
858 output_menu_bundles.clear ();
860 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
862 citems.push_back (SeparatorElem());
863 uint32_t const n_with_separator = citems.size ();
865 ARDOUR::BundleList current = _route->output()->bundles_connected ();
867 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
869 /* give user bundles first chance at being in the menu */
871 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
872 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
873 maybe_add_bundle_to_output_menu (*i, current);
877 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
878 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
879 maybe_add_bundle_to_output_menu (*i, current);
883 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
884 RouteList copy = *routes;
885 copy.sort (RouteCompareByName ());
886 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
887 maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
890 if (citems.size() == n_with_separator) {
891 /* no routes added; remove the separator */
895 if (!ARDOUR::Profile->get_mixbus()) {
896 citems.push_back (SeparatorElem());
898 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
901 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
902 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
908 citems.push_back (SeparatorElem());
909 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
911 output_menu.popup (1, ev->time);
922 MixerStrip::input_release (GdkEventButton *ev)
924 switch (ev->button) {
927 edit_input_configuration ();
939 MixerStrip::input_press (GdkEventButton *ev)
941 using namespace Menu_Helpers;
943 MenuList& citems = input_menu.items();
944 input_menu.set_name ("ArdourContextMenu");
947 if (!_session->engine().connected()) {
948 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
953 if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
956 switch (ev->button) {
959 return false; //don't handle the mouse-down here. wait for mouse-up to pop the menu
963 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
965 citems.push_back (SeparatorElem());
966 uint32_t const n_with_separator = citems.size ();
968 input_menu_bundles.clear ();
970 ARDOUR::BundleList current = _route->input()->bundles_connected ();
972 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
974 /* give user bundles first chance at being in the menu */
976 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
977 if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
978 maybe_add_bundle_to_input_menu (*i, current);
982 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
983 if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
984 maybe_add_bundle_to_input_menu (*i, current);
988 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
989 RouteList copy = *routes;
990 copy.sort (RouteCompareByName ());
991 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
992 maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
995 if (citems.size() == n_with_separator) {
996 /* no routes added; remove the separator */
1000 citems.push_back (SeparatorElem());
1001 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1004 string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1005 sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1010 citems.push_back (SeparatorElem());
1011 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1013 input_menu.popup (1, ev->time);
1024 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1026 if (ignore_toggle) {
1030 ARDOUR::BundleList current = _route->input()->bundles_connected ();
1032 if (std::find (current.begin(), current.end(), c) == current.end()) {
1033 _route->input()->connect_ports_to_bundle (c, true, this);
1035 _route->input()->disconnect_ports_from_bundle (c, this);
1040 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1042 if (ignore_toggle) {
1046 ARDOUR::BundleList current = _route->output()->bundles_connected ();
1048 if (std::find (current.begin(), current.end(), c) == current.end()) {
1049 _route->output()->connect_ports_to_bundle (c, true, this);
1051 _route->output()->disconnect_ports_from_bundle (c, this);
1056 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1058 using namespace Menu_Helpers;
1060 if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1064 list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1065 while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1069 if (i != input_menu_bundles.end()) {
1073 input_menu_bundles.push_back (b);
1075 MenuList& citems = input_menu.items();
1077 std::string n = b->name ();
1078 replace_all (n, "_", " ");
1080 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1084 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1086 using namespace Menu_Helpers;
1088 if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1092 list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1093 while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1097 if (i != output_menu_bundles.end()) {
1101 output_menu_bundles.push_back (b);
1103 MenuList& citems = output_menu.items();
1105 std::string n = b->name ();
1106 replace_all (n, "_", " ");
1108 citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1112 MixerStrip::update_diskstream_display ()
1114 if (is_track() && input_selector) {
1115 input_selector->hide_all ();
1118 route_color_changed ();
1122 MixerStrip::connect_to_pan ()
1124 ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1126 panstate_connection.disconnect ();
1127 panstyle_connection.disconnect ();
1129 if (!_route->panner()) {
1133 boost::shared_ptr<Pannable> p = _route->pannable ();
1135 p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1136 p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1138 /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1139 * However, that only works a panner was previously set.
1141 * PannerUI must remain subscribed to _panshell->Changed() in case
1142 * we switch the panner eg. AUX-Send and back
1143 * _route->panner_shell()->Changed() vs _panshell->Changed
1145 if (panners._panner == 0) {
1146 panners.panshell_changed ();
1148 update_panner_choices();
1152 MixerStrip::update_panner_choices ()
1154 ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1155 if (!_route->panner_shell()) { return; }
1157 uint32_t in = _route->output()->n_ports().n_audio();
1159 if (_route->panner()) {
1160 in = _route->panner()->in().n_audio();
1163 panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1167 * Output port labelling
1168 * =====================
1170 * Case 1: Each output has one connection, all connections are to system:playback_%i
1171 * out 1 -> system:playback_1
1172 * out 2 -> system:playback_2
1173 * out 3 -> system:playback_3
1176 * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1177 * out 1 -> ardour:track_x/in 1
1178 * out 2 -> ardour:track_x/in 2
1179 * Display as: track_x
1181 * Case 3: Each output has one connection, all connections are to Jack client "program x"
1182 * out 1 -> program x:foo
1183 * out 2 -> program x:foo
1184 * Display as: program x
1186 * Case 4: No connections (Disconnected)
1189 * Default case (unusual routing):
1190 * Display as: *number of connections*
1194 * .-----------------------------------------------.
1196 * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1197 * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1198 * '-----------------------------------------------'
1199 * .-----------------------------------------------.
1202 * '-----------------------------------------------'
1206 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1210 boost::shared_ptr<Port> port;
1211 vector<string> port_connections;
1213 uint32_t total_connection_count = 0;
1214 uint32_t io_connection_count = 0;
1215 uint32_t ardour_connection_count = 0;
1216 uint32_t system_connection_count = 0;
1217 uint32_t other_connection_count = 0;
1218 uint32_t typed_connection_count = 0;
1220 ostringstream label;
1222 bool have_label = false;
1223 bool each_io_has_one_connection = true;
1225 string connection_name;
1226 string ardour_track_name;
1227 string other_connection_type;
1228 string system_ports;
1231 ostringstream tooltip;
1232 char * tooltip_cstr;
1234 //to avoid confusion, the button caption should only show connections that match the datatype of the track
1235 DataType dt = DataType::AUDIO;
1236 if ( boost::dynamic_pointer_cast<MidiTrack>(route) != 0 ) {
1237 dt = DataType::MIDI;
1238 // avoid further confusion with Midi-tracks that have a synth.
1239 // Audio-ports may be connected, but button says "Disconnected"
1240 tooltip << _("MIDI ");
1244 io_count = route->n_inputs().n_total();
1245 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1247 io_count = route->n_outputs().n_total();
1248 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1252 for (io_index = 0; io_index < io_count; ++io_index) {
1254 port = route->input()->nth (io_index);
1256 port = route->output()->nth (io_index);
1259 port_connections.clear ();
1260 port->get_connections(port_connections);
1262 //ignore any port connections that don't match our DataType
1263 if (port->type() != dt) {
1264 if (!port_connections.empty()) {
1265 ++typed_connection_count;
1270 io_connection_count = 0;
1272 if (!port_connections.empty()) {
1273 for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1275 string& connection_name (*i);
1277 if (connection_name.find("system:") == 0) {
1278 pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1281 if (io_connection_count == 0) {
1282 tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1284 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1287 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1290 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1291 if (ardour_track_name.empty()) {
1292 // "ardour:Master/in 1" -> "ardour:Master/"
1293 string::size_type slash = connection_name.find("/");
1294 if (slash != string::npos) {
1295 ardour_track_name = connection_name.substr(0, slash + 1);
1299 if (connection_name.find(ardour_track_name) == 0) {
1300 ++ardour_connection_count;
1302 } else if (!pn.empty()) {
1303 if (system_ports.empty()) {
1306 system_ports += "/" + pn;
1308 if (connection_name.find("system:") == 0) {
1309 ++system_connection_count;
1311 } else if (connection_name.find("system:midi_") == 0) {
1313 // "system:midi_capture_123" -> "123"
1314 system_port = "M " + connection_name.substr(20);
1316 // "system:midi_playback_123" -> "123"
1317 system_port = "M " + connection_name.substr(21);
1320 if (system_ports.empty()) {
1321 system_ports += system_port;
1323 system_ports += "/" + system_port;
1326 ++system_connection_count;
1328 } else if (connection_name.find("system:") == 0) {
1330 // "system:capture_123" -> "123"
1331 system_port = connection_name.substr(15);
1333 // "system:playback_123" -> "123"
1334 system_port = connection_name.substr(16);
1337 if (system_ports.empty()) {
1338 system_ports += system_port;
1340 system_ports += "/" + system_port;
1343 ++system_connection_count;
1345 if (other_connection_type.empty()) {
1346 // "jamin:in 1" -> "jamin:"
1347 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1350 if (connection_name.find(other_connection_type) == 0) {
1351 ++other_connection_count;
1355 ++total_connection_count;
1356 ++io_connection_count;
1360 if (io_connection_count != 1) {
1361 each_io_has_one_connection = false;
1365 if (total_connection_count == 0) {
1366 tooltip << endl << _("Disconnected");
1369 tooltip_cstr = new char[tooltip.str().size() + 1];
1370 strcpy(tooltip_cstr, tooltip.str().c_str());
1373 set_tooltip (&input_button, tooltip_cstr);
1375 set_tooltip (&output_button, tooltip_cstr);
1378 delete [] tooltip_cstr;
1380 if (each_io_has_one_connection) {
1381 if (total_connection_count == ardour_connection_count) {
1382 // all connections are to the same track in ardour
1383 // "ardour:Master/" -> "Master"
1384 string::size_type slash = ardour_track_name.find("/");
1385 if (slash != string::npos) {
1386 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1387 label << ardour_track_name.substr (ppps, slash - ppps);
1391 else if (total_connection_count == system_connection_count) {
1392 // all connections are to system ports
1393 label << system_ports;
1396 else if (total_connection_count == other_connection_count) {
1397 // all connections are to the same external program eg jamin
1398 // "jamin:" -> "jamin"
1399 label << other_connection_type.substr(0, other_connection_type.size() - 1);
1405 if (total_connection_count == 0) {
1409 // Odd configuration
1410 label << "*" << total_connection_count << "*";
1412 if (typed_connection_count > 0) {
1413 label << "\u2295"; // circled plus
1418 input_button.set_text (label.str());
1420 output_button.set_text (label.str());
1425 MixerStrip::update_input_display ()
1427 update_io_button (_route, _width, true);
1428 panners.setup_pan ();
1430 if (has_audio_outputs ()) {
1431 panners.show_all ();
1433 panners.hide_all ();
1439 MixerStrip::update_output_display ()
1441 update_io_button (_route, _width, false);
1442 gpm.setup_meters ();
1443 panners.setup_pan ();
1445 if (has_audio_outputs ()) {
1446 panners.show_all ();
1448 panners.hide_all ();
1453 MixerStrip::fast_update ()
1455 gpm.update_meters ();
1459 MixerStrip::diskstream_changed ()
1461 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1465 MixerStrip::io_changed_proxy ()
1467 Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1471 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1473 boost::shared_ptr<Port> a = wa.lock ();
1474 boost::shared_ptr<Port> b = wb.lock ();
1476 if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1477 update_input_display ();
1478 set_width_enum (_width, this);
1481 if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1482 update_output_display ();
1483 set_width_enum (_width, this);
1488 MixerStrip::setup_comment_button ()
1493 if (_route->comment().empty ()) {
1494 _comment_button.unset_bg (STATE_NORMAL);
1495 _comment_button.set_text (_("Comments"));
1497 _comment_button.modify_bg (STATE_NORMAL, color ());
1498 _comment_button.set_text (_("*Comments*"));
1503 if (_route->comment().empty ()) {
1504 _comment_button.unset_bg (STATE_NORMAL);
1505 _comment_button.set_text (_("Cmt"));
1507 _comment_button.modify_bg (STATE_NORMAL, color ());
1508 _comment_button.set_text (_("*Cmt*"));
1514 _comment_button, _route->comment().empty() ? _("Click to add/edit comments") : _route->comment()
1520 MixerStrip::select_route_group (GdkEventButton *ev)
1522 using namespace Menu_Helpers;
1524 if (ev->button == 1) {
1526 if (group_menu == 0) {
1528 PropertyList* plist = new PropertyList();
1530 plist->add (Properties::gain, true);
1531 plist->add (Properties::mute, true);
1532 plist->add (Properties::solo, true);
1534 group_menu = new RouteGroupMenu (_session, plist);
1538 r.push_back (route ());
1539 group_menu->build (r);
1540 group_menu->menu()->popup (1, ev->time);
1547 MixerStrip::route_group_changed ()
1549 ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1551 RouteGroup *rg = _route->route_group();
1554 group_button.set_text (PBD::short_version (rg->name(), 5));
1558 group_button.set_text (_("Grp"));
1561 group_button.set_text (_("~G"));
1568 MixerStrip::route_color_changed ()
1570 name_button.modify_bg (STATE_NORMAL, color());
1571 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1572 reset_strip_style ();
1576 MixerStrip::show_passthru_color ()
1578 reset_strip_style ();
1582 MixerStrip::build_route_ops_menu ()
1584 using namespace Menu_Helpers;
1585 route_ops_menu = new Menu;
1586 route_ops_menu->set_name ("ArdourContextMenu");
1588 MenuList& items = route_ops_menu->items();
1590 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1592 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1594 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1596 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1598 items.push_back (SeparatorElem());
1600 if (!_route->is_master()) {
1601 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1603 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1604 rename_menu_item = &items.back();
1606 items.push_back (SeparatorElem());
1607 items.push_back (CheckMenuElem (_("Active")));
1608 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1609 i->set_active (_route->active());
1610 i->set_sensitive(! _session->transport_rolling());
1611 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1613 if (!Profile->get_mixbus ()) {
1614 items.push_back (SeparatorElem());
1615 items.push_back (CheckMenuElem (_("Strict I/O")));
1616 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1617 i->set_active (_route->strict_io());
1618 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1621 if (1 /* TODO IFF >= 1 plugin-insert */) {
1622 items.push_back (SeparatorElem());
1623 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1626 items.push_back (SeparatorElem());
1627 items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1629 items.push_back (SeparatorElem());
1630 items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1631 denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1632 denormal_menu_item->set_active (_route->denormal_protection());
1634 items.push_back (SeparatorElem());
1635 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
1638 /* note that this relies on selection being shared across editor and
1639 mixer (or global to the backend, in the future), which is the only
1640 sane thing for users anyway.
1643 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1645 Selection& selection (PublicEditor::instance().get_selection());
1646 if (!selection.selected (rtav)) {
1647 selection.set (rtav);
1650 if (!_route->is_master()) {
1651 items.push_back (SeparatorElem());
1652 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1655 items.push_back (SeparatorElem());
1656 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1662 MixerStrip::name_button_button_press (GdkEventButton* ev)
1664 if (ev->button == 3) {
1665 list_route_operations ();
1667 /* do not allow rename if the track is record-enabled */
1668 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1669 route_ops_menu->popup (1, ev->time);
1678 MixerStrip::name_button_button_release (GdkEventButton* ev)
1680 if (ev->button == 1) {
1681 list_route_operations ();
1683 /* do not allow rename if the track is record-enabled */
1684 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1685 route_ops_menu->popup (1, ev->time);
1692 MixerStrip::number_button_button_press (GdkEventButton* ev)
1694 if ( ev->button == 3 ) {
1695 list_route_operations ();
1697 /* do not allow rename if the track is record-enabled */
1698 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1699 route_ops_menu->popup (1, ev->time);
1708 MixerStrip::list_route_operations ()
1710 delete route_ops_menu;
1711 build_route_ops_menu ();
1715 MixerStrip::set_selected (bool yn)
1717 AxisView::set_selected (yn);
1719 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1720 global_frame.set_name ("MixerStripSelectedFrame");
1722 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1723 global_frame.set_name ("MixerStripFrame");
1725 global_frame.queue_draw ();
1728 // processor_box.deselect_all_processors();
1732 MixerStrip::property_changed (const PropertyChange& what_changed)
1734 RouteUI::property_changed (what_changed);
1736 if (what_changed.contains (ARDOUR::Properties::name)) {
1742 MixerStrip::name_changed ()
1746 name_button.set_text (_route->name());
1749 name_button.set_text (PBD::short_version (_route->name(), 5));
1753 set_tooltip (name_button, _route->name());
1755 if (_session->config.get_track_name_number()) {
1756 const int64_t track_number = _route->track_number ();
1757 if (track_number == 0) {
1758 number_label.set_text ("-");
1760 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1763 number_label.set_text ("");
1768 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1770 input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1774 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1776 output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1780 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1782 name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1786 MixerStrip::width_button_pressed (GdkEventButton* ev)
1788 if (ev->button != 1) {
1792 if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1795 _mixer.set_strip_width (Narrow, true);
1799 _mixer.set_strip_width (Wide, true);
1805 set_width_enum (Narrow, this);
1808 set_width_enum (Wide, this);
1817 MixerStrip::hide_clicked ()
1819 // LAME fix to reset the button status for when it is redisplayed (part 1)
1820 hide_button.set_sensitive(false);
1823 Hiding(); /* EMIT_SIGNAL */
1825 _mixer.hide_strip (this);
1829 hide_button.set_sensitive(true);
1833 MixerStrip::set_embedded (bool yn)
1839 MixerStrip::map_frozen ()
1841 ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1843 boost::shared_ptr<AudioTrack> at = audio_track();
1846 switch (at->freeze_state()) {
1847 case AudioTrack::Frozen:
1848 processor_box.set_sensitive (false);
1849 hide_redirect_editors ();
1852 processor_box.set_sensitive (true);
1853 // XXX need some way, maybe, to retoggle redirect editors
1857 processor_box.set_sensitive (true);
1859 RouteUI::map_frozen ();
1863 MixerStrip::hide_redirect_editors ()
1865 _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1869 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1871 boost::shared_ptr<Processor> processor (p.lock ());
1876 Gtk::Window* w = processor_box.get_processor_ui (processor);
1884 MixerStrip::reset_strip_style ()
1886 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1888 gpm.set_fader_name ("SendStripBase");
1892 if (is_midi_track()) {
1893 if (_route->active()) {
1894 set_name ("MidiTrackStripBase");
1896 set_name ("MidiTrackStripBaseInactive");
1898 gpm.set_fader_name ("MidiTrackFader");
1899 } else if (is_audio_track()) {
1900 if (_route->active()) {
1901 set_name ("AudioTrackStripBase");
1903 set_name ("AudioTrackStripBaseInactive");
1905 gpm.set_fader_name ("AudioTrackFader");
1907 if (_route->active()) {
1908 set_name ("AudioBusStripBase");
1910 set_name ("AudioBusStripBaseInactive");
1912 gpm.set_fader_name ("AudioBusFader");
1914 /* (no MIDI busses yet) */
1921 MixerStrip::engine_stopped ()
1926 MixerStrip::engine_running ()
1931 MixerStrip::meter_point_string (MeterPoint mp)
1944 case MeterPostFader:
1961 return S_("Meter|In");
1965 return S_("Meter|Pr");
1968 case MeterPostFader:
1969 return S_("Meter|Po");
1973 return S_("Meter|O");
1978 return S_("Meter|C");
1987 /** Called when the monitor-section state */
1989 MixerStrip::monitor_changed ()
1991 assert (monitor_section_button);
1992 if (_session->monitor_active()) {
1993 monitor_section_button->set_name ("master monitor section button active");
1995 monitor_section_button->set_name ("master monitor section button normal");
1999 /** Called when the metering point has changed */
2001 MixerStrip::meter_changed ()
2003 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2004 gpm.setup_meters ();
2005 // reset peak when meter point changes
2006 gpm.reset_peak_display();
2009 /** The bus that we are displaying sends to has changed, or been turned off.
2010 * @param send_to New bus that we are displaying sends to, or 0.
2013 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2015 RouteUI::bus_send_display_changed (send_to);
2018 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2023 revert_to_default_display ();
2026 revert_to_default_display ();
2031 MixerStrip::drop_send ()
2033 boost::shared_ptr<Send> current_send;
2035 if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2036 current_send->set_metering (false);
2039 send_gone_connection.disconnect ();
2040 input_button.set_sensitive (true);
2041 output_button.set_sensitive (true);
2042 group_button.set_sensitive (true);
2043 set_invert_sensitive (true);
2044 meter_point_button.set_sensitive (true);
2045 mute_button->set_sensitive (true);
2046 solo_button->set_sensitive (true);
2047 solo_isolated_led->set_sensitive (true);
2048 solo_safe_led->set_sensitive (true);
2049 monitor_input_button->set_sensitive (true);
2050 monitor_disk_button->set_sensitive (true);
2051 _comment_button.set_sensitive (true);
2052 RouteUI::check_rec_enable_sensitivity ();
2053 set_button_names (); // update solo button visual state
2057 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2059 _current_delivery = d;
2060 DeliveryChanged (_current_delivery);
2064 MixerStrip::show_send (boost::shared_ptr<Send> send)
2070 set_current_delivery (send);
2072 send->meter()->set_type(_route->shared_peak_meter()->get_type());
2073 send->set_metering (true);
2074 _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2076 gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2077 gain_meter().setup_meters ();
2079 uint32_t const in = _current_delivery->pans_required();
2080 uint32_t const out = _current_delivery->pan_outs();
2082 panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2083 panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2084 panner_ui().setup_pan ();
2085 panner_ui().set_send_drawing_mode (true);
2086 panner_ui().show_all ();
2088 input_button.set_sensitive (false);
2089 group_button.set_sensitive (false);
2090 set_invert_sensitive (false);
2091 meter_point_button.set_sensitive (false);
2092 mute_button->set_sensitive (false);
2093 solo_button->set_sensitive (false);
2094 rec_enable_button->set_sensitive (false);
2095 solo_isolated_led->set_sensitive (false);
2096 solo_safe_led->set_sensitive (false);
2097 monitor_input_button->set_sensitive (false);
2098 monitor_disk_button->set_sensitive (false);
2099 _comment_button.set_sensitive (false);
2101 if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2102 output_button.set_sensitive (false);
2105 reset_strip_style ();
2109 MixerStrip::revert_to_default_display ()
2113 set_current_delivery (_route->main_outs ());
2115 gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2116 gain_meter().setup_meters ();
2118 panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2119 update_panner_choices();
2120 panner_ui().setup_pan ();
2121 panner_ui().set_send_drawing_mode (false);
2123 if (has_audio_outputs ()) {
2124 panners.show_all ();
2126 panners.hide_all ();
2129 reset_strip_style ();
2133 MixerStrip::set_button_names ()
2137 mute_button->set_text (_("Mute"));
2138 monitor_input_button->set_text (_("In"));
2139 monitor_disk_button->set_text (_("Disk"));
2140 if (monitor_section_button) {
2141 monitor_section_button->set_text (_("Mon"));
2144 if (_route && _route->solo_safe_control()->solo_safe()) {
2145 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2147 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2149 if (!Config->get_solo_control_is_listen_control()) {
2150 solo_button->set_text (_("Solo"));
2152 switch (Config->get_listen_position()) {
2153 case AfterFaderListen:
2154 solo_button->set_text (_("AFL"));
2156 case PreFaderListen:
2157 solo_button->set_text (_("PFL"));
2161 solo_isolated_led->set_text (_("Iso"));
2162 solo_safe_led->set_text (S_("SoloLock|Lock"));
2166 mute_button->set_text (S_("Mute|M"));
2167 monitor_input_button->set_text (S_("MonitorInput|I"));
2168 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2169 if (monitor_section_button) {
2170 monitor_section_button->set_text (S_("Mon|O"));
2173 if (_route && _route->solo_safe_control()->solo_safe()) {
2174 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2176 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2178 if (!Config->get_solo_control_is_listen_control()) {
2179 solo_button->set_text (S_("Solo|S"));
2181 switch (Config->get_listen_position()) {
2182 case AfterFaderListen:
2183 solo_button->set_text (S_("AfterFader|A"));
2185 case PreFaderListen:
2186 solo_button->set_text (S_("Prefader|P"));
2191 solo_isolated_led->set_text (S_("SoloIso|I"));
2192 solo_safe_led->set_text (S_("SoloLock|L"));
2197 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2199 meter_point_button.set_text ("");
2204 MixerStrip::plugin_selector()
2206 return _mixer.plugin_selector();
2210 MixerStrip::hide_things ()
2212 processor_box.hide_things ();
2216 MixerStrip::input_active_button_press (GdkEventButton*)
2218 /* nothing happens on press */
2223 MixerStrip::input_active_button_release (GdkEventButton* ev)
2225 boost::shared_ptr<MidiTrack> mt = midi_track ();
2231 boost::shared_ptr<RouteList> rl (new RouteList);
2233 rl->push_back (route());
2235 _session->set_exclusive_input_active (rl, !mt->input_active(),
2236 Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2242 MixerStrip::midi_input_status_changed ()
2244 if (midi_input_enable_button) {
2245 boost::shared_ptr<MidiTrack> mt = midi_track ();
2247 midi_input_enable_button->set_active (mt->input_active ());
2252 MixerStrip::state_id () const
2254 return string_compose ("strip %1", _route->id().to_s());
2258 MixerStrip::parameter_changed (string p)
2260 if (p == _visibility.get_state_name()) {
2261 /* The user has made changes to the mixer strip visibility, so get
2262 our VisibilityGroup to reflect these changes in our widgets.
2264 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2265 } else if (p == "track-name-number") {
2267 } else if (p == "use-monitor-bus") {
2268 if (monitor_section_button) {
2269 if (mute_button->get_parent()) {
2270 mute_button->get_parent()->remove(*mute_button);
2272 if (monitor_section_button->get_parent()) {
2273 monitor_section_button->get_parent()->remove(*monitor_section_button);
2275 if (Config->get_use_monitor_bus ()) {
2276 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2277 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2278 mute_button->show();
2279 monitor_section_button->show();
2281 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2282 mute_button->show();
2285 } else if (p == "track-name-number") {
2286 update_track_number_visibility();
2290 /** Called to decide whether the solo isolate / solo lock button visibility should
2291 * be overridden from that configured by the user. We do this for the master bus.
2293 * @return optional value that is present if visibility state should be overridden.
2295 boost::optional<bool>
2296 MixerStrip::override_solo_visibility () const
2298 if (_route && _route->is_master ()) {
2299 return boost::optional<bool> (false);
2302 return boost::optional<bool> ();
2306 MixerStrip::add_input_port (DataType t)
2308 _route->input()->add_port ("", this, t);
2312 MixerStrip::add_output_port (DataType t)
2314 _route->output()->add_port ("", this, t);
2318 MixerStrip::route_active_changed ()
2320 reset_strip_style ();
2324 MixerStrip::copy_processors ()
2326 processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2330 MixerStrip::cut_processors ()
2332 processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2336 MixerStrip::paste_processors ()
2338 processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2342 MixerStrip::select_all_processors ()
2344 processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2348 MixerStrip::deselect_all_processors ()
2350 processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2354 MixerStrip::delete_processors ()
2356 return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2360 MixerStrip::toggle_processors ()
2362 processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2366 MixerStrip::ab_plugins ()
2368 processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2372 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2374 if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2377 if (ev->button == 3) {
2378 popup_level_meter_menu (ev);
2386 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2388 using namespace Gtk::Menu_Helpers;
2390 Gtk::Menu* m = manage (new Menu);
2391 MenuList& items = m->items ();
2393 RadioMenuItem::Group group;
2395 _suspend_menu_callbacks = true;
2396 add_level_meter_item_point (items, group, _("Input"), MeterInput);
2397 add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2398 add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2399 add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2400 add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2402 if (gpm.meter_channels().n_audio() == 0) {
2403 m->popup (ev->button, ev->time);
2404 _suspend_menu_callbacks = false;
2408 RadioMenuItem::Group tgroup;
2409 items.push_back (SeparatorElem());
2411 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2412 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2413 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms), MeterKrms);
2414 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2415 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2416 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2417 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2418 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2419 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2420 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2421 add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU), MeterVU);
2424 if (_route->is_master()) {
2427 else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2428 && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2429 /* non-master bus */
2432 else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2439 MeterType cmt = _route->meter_type();
2440 const std::string cmn = ArdourMeter::meter_type_string(cmt);
2442 items.push_back (SeparatorElem());
2443 items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2444 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2445 items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2446 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2447 items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2448 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2450 m->popup (ev->button, ev->time);
2451 _suspend_menu_callbacks = false;
2455 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2456 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2458 using namespace Menu_Helpers;
2460 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2461 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2462 i->set_active (_route->meter_point() == point);
2466 MixerStrip::set_meter_point (MeterPoint p)
2468 if (_suspend_menu_callbacks) return;
2469 _route->set_meter_point (p);
2473 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2474 RadioMenuItem::Group& group, string const & name, MeterType type)
2476 using namespace Menu_Helpers;
2478 items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2479 RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2480 i->set_active (_route->meter_type() == type);
2484 MixerStrip::set_meter_type (MeterType t)
2486 if (_suspend_menu_callbacks) return;
2491 MixerStrip::vca_menu_toggle (CheckMenuItem* menuitem, uint32_t n)
2497 boost::shared_ptr<VCA> vca = _session->vca_manager().vca_by_number (n);
2504 /* if this strip is not selected, add it before carrying out
2505 changes to assignment. the user probably didn't notice
2506 that they were clicking on an unselected track.
2508 _mixer.select_strip (*this);
2511 if (!menuitem->get_active()) {
2512 _mixer.do_vca_unassign (vca);
2514 _mixer.do_vca_assign (vca);
2519 MixerStrip::vca_assign (boost::shared_ptr<VCA> vca)
2521 if (!vca || !_route) {
2525 _route->assign (vca);
2529 MixerStrip::vca_unassign (boost::shared_ptr<VCA> vca)
2535 _route->unassign (vca);
2539 MixerStrip::vca_button_release (GdkEventButton* ev)
2541 using namespace Gtk::Menu_Helpers;
2547 /* primary click only */
2549 if (ev->button != 1) {
2554 /* no route - nothing to do */
2558 VCAList vcas (_session->vca_manager().vcas());
2561 /* XXX should probably show a message saying "No VCA masters" */
2565 Menu* menu = new Menu;
2566 MenuList& items = menu->items();
2568 items.push_back (MenuElem (_("Unassign"), sigc::bind (sigc::mem_fun (_mixer, &Mixer_UI::do_vca_unassign), boost::shared_ptr<VCA>())));
2570 for (VCAList::iterator v = vcas.begin(); v != vcas.end(); ++v) {
2571 items.push_back (CheckMenuElem ((*v)->name()));
2572 CheckMenuItem* item = dynamic_cast<CheckMenuItem*> (&items.back());
2573 if (_route->slaved_to (*v)) {
2574 item->set_active (true);
2576 item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &MixerStrip::vca_menu_toggle), item, (*v)->number()));
2579 menu->popup (1, ev->time);
2585 MixerStrip::update_track_number_visibility ()
2587 DisplaySuspender ds;
2588 bool show_label = _session->config.get_track_name_number();
2590 if (_route && _route->is_master()) {
2595 number_label.show ();
2596 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2597 // except the width of the number label is subtracted from the name-hbox, so we
2598 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2599 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2601 number_label.set_size_request(tnw, -1);
2602 number_label.show ();
2603 //name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
2605 number_label.hide ();