9adb670924ebb75d52195c752b1d4dc0be996ab7
[ardour.git] / gtk2_ardour / mixer_strip.cc
1 /*
2     Copyright (C) 2000-2006 Paul Davis
3
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.
8
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.
13
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.
17 */
18
19 #include <cmath>
20 #include <list>
21 #include <algorithm>
22
23 #include <sigc++/bind.h>
24
25 #include "pbd/convert.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/replace_all.h"
28 #include "pbd/stacktrace.h"
29
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>
36
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/io.h"
42 #include "ardour/meter.h"
43 #include "ardour/midi_track.h"
44 #include "ardour/pannable.h"
45 #include "ardour/panner.h"
46 #include "ardour/panner_shell.h"
47 #include "ardour/panner_manager.h"
48 #include "ardour/port.h"
49 #include "ardour/profile.h"
50 #include "ardour/route.h"
51 #include "ardour/route_group.h"
52 #include "ardour/send.h"
53 #include "ardour/session.h"
54 #include "ardour/types.h"
55 #include "ardour/user_bundle.h"
56 #include "ardour/vca.h"
57 #include "ardour/vca_manager.h"
58
59 #include "ardour_window.h"
60 #include "mixer_strip.h"
61 #include "mixer_ui.h"
62 #include "keyboard.h"
63 #include "ardour_button.h"
64 #include "public_editor.h"
65 #include "send_ui.h"
66 #include "io_selector.h"
67 #include "utils.h"
68 #include "gui_thread.h"
69 #include "route_group_menu.h"
70 #include "meter_patterns.h"
71 #include "tooltips.h"
72 #include "ui_config.h"
73
74 #include "pbd/i18n.h"
75
76 using namespace ARDOUR;
77 using namespace ARDOUR_UI_UTILS;
78 using namespace PBD;
79 using namespace Gtk;
80 using namespace Gtkmm2ext;
81 using namespace std;
82 using namespace ArdourMeter;
83
84 MixerStrip* MixerStrip::_entered_mixer_strip;
85 PBD::Signal1<void,MixerStrip*> MixerStrip::CatchDeletion;
86
87 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, bool in_mixer)
88         : SessionHandlePtr (sess)
89         , RouteUI (sess)
90         , _mixer(mx)
91         , _mixer_owned (in_mixer)
92         , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
93         , gpm (sess, 250)
94         , panners (sess)
95         , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
96         , rec_mon_table (2, 2)
97         , solo_iso_table (1, 2)
98         , mute_solo_table (1, 2)
99         , bottom_button_table (1, 3)
100         , meter_point_button (_("pre"))
101         , monitor_section_button (0)
102         , midi_input_enable_button (0)
103         , _plugin_insert_cnt (0)
104         , _comment_button (_("Comments"))
105         , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
106         , _visibility (X_("mixer-element-visibility"))
107         , control_slave_ui (sess)
108 {
109         init ();
110
111         if (!_mixer_owned) {
112                 /* the editor mixer strip: don't destroy it every time
113                    the underlying route goes away.
114                 */
115
116                 self_destruct = false;
117         }
118 }
119
120 MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, boost::shared_ptr<Route> rt, bool in_mixer)
121         : SessionHandlePtr (sess)
122         , RouteUI (sess)
123         , _mixer(mx)
124         , _mixer_owned (in_mixer)
125         , processor_box (sess, boost::bind (&MixerStrip::plugin_selector, this), mx.selection(), this, in_mixer)
126         , gpm (sess, 250)
127         , panners (sess)
128         , button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
129         , rec_mon_table (2, 2)
130         , solo_iso_table (1, 2)
131         , mute_solo_table (1, 2)
132         , bottom_button_table (1, 3)
133         , meter_point_button (_("pre"))
134         , monitor_section_button (0)
135         , midi_input_enable_button (0)
136         , _comment_button (_("Comments"))
137         , trim_control (ArdourKnob::default_elements, ArdourKnob::Flags (ArdourKnob::Detent | ArdourKnob::ArcToZero))
138         , _visibility (X_("mixer-element-visibility"))
139         , control_slave_ui (sess)
140 {
141         init ();
142         set_route (rt);
143 }
144
145 void
146 MixerStrip::init ()
147 {
148         _entered_mixer_strip= 0;
149         group_menu = 0;
150         route_ops_menu = 0;
151         ignore_comment_edit = false;
152         ignore_toggle = false;
153         comment_area = 0;
154         _width_owner = 0;
155
156         /* the length of this string determines the width of the mixer strip when it is set to `wide' */
157         longest_label = "longest label";
158
159         string t = _("Click to toggle the width of this mixer strip.");
160         if (_mixer_owned) {
161                 t += string_compose (_("\n%1-%2-click to toggle the width of all strips."), Keyboard::primary_modifier_name(), Keyboard::tertiary_modifier_name ());
162         }
163
164         width_button.set_icon (ArdourIcon::StripWidth);
165         hide_button.set_tweaks (ArdourButton::Square);
166         set_tooltip (width_button, t);
167
168         hide_button.set_icon (ArdourIcon::CloseCross);
169         hide_button.set_tweaks (ArdourButton::Square);
170         set_tooltip (&hide_button, _("Hide this mixer strip"));
171
172         input_button_box.set_spacing(2);
173
174         input_button.set_text (_("Input"));
175         input_button.set_name ("mixer strip button");
176         input_button_box.pack_start (input_button, true, true);
177
178         output_button.set_text (_("Output"));
179         output_button.set_name ("mixer strip button");
180
181         set_tooltip (&meter_point_button, _("Click to select metering point"));
182         meter_point_button.set_name ("mixer strip button");
183
184         bottom_button_table.attach (meter_point_button, 2, 3, 0, 1);
185
186         meter_point_button.signal_button_press_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_press), false);
187         meter_point_button.signal_button_release_event().connect (sigc::mem_fun (gpm, &GainMeter::meter_release), false);
188
189         hide_button.set_events (hide_button.get_events() & ~(Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK));
190
191         solo_isolated_led = manage (new ArdourButton (ArdourButton::led_default_elements));
192         solo_isolated_led->show ();
193         solo_isolated_led->set_no_show_all (true);
194         solo_isolated_led->set_name (X_("solo isolate"));
195         solo_isolated_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
196         solo_isolated_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_isolate_button_release), false);
197         UI::instance()->set_tip (solo_isolated_led, _("Isolate Solo"), "");
198
199         solo_safe_led = manage (new ArdourButton (ArdourButton::led_default_elements));
200         solo_safe_led->show ();
201         solo_safe_led->set_no_show_all (true);
202         solo_safe_led->set_name (X_("solo safe"));
203         solo_safe_led->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
204         solo_safe_led->signal_button_release_event().connect (sigc::mem_fun (*this, &RouteUI::solo_safe_button_release), false);
205         UI::instance()->set_tip (solo_safe_led, _("Lock Solo Status"), "");
206
207         solo_safe_led->set_text (S_("SoloLock|Lock"));
208         solo_isolated_led->set_text (_("Iso"));
209
210         solo_iso_table.set_homogeneous (true);
211         solo_iso_table.set_spacings (2);
212         if (!ARDOUR::Profile->get_trx()) {
213                 solo_iso_table.attach (*solo_isolated_led, 0, 1, 0, 1);
214                 solo_iso_table.attach (*solo_safe_led, 1, 2, 0, 1);
215         }
216         solo_iso_table.show ();
217
218         rec_mon_table.set_homogeneous (true);
219         rec_mon_table.set_row_spacings (2);
220         rec_mon_table.set_col_spacings (2);
221         if (ARDOUR::Profile->get_mixbus()) {
222                 rec_mon_table.resize (1, 3);
223                 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
224                 rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
225         } else if (!ARDOUR::Profile->get_trx()) {
226                 rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
227                 rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
228         }
229         rec_mon_table.show ();
230
231         if (solo_isolated_led) {
232                 button_size_group->add_widget (*solo_isolated_led);
233         }
234         if (solo_safe_led) {
235                 button_size_group->add_widget (*solo_safe_led);
236         }
237
238         if (!ARDOUR::Profile->get_mixbus()) {
239                 if (rec_enable_button) {
240                         button_size_group->add_widget (*rec_enable_button);
241                 }
242                 if (monitor_disk_button) {
243                         button_size_group->add_widget (*monitor_disk_button);
244                 }
245                 if (monitor_input_button) {
246                         button_size_group->add_widget (*monitor_input_button);
247                 }
248         }
249
250         mute_solo_table.set_homogeneous (true);
251         mute_solo_table.set_spacings (2);
252
253         bottom_button_table.set_spacings (2);
254         bottom_button_table.set_homogeneous (true);
255         bottom_button_table.attach (group_button, 1, 2, 0, 1);
256         bottom_button_table.attach (gpm.gain_automation_state_button, 0, 1, 0, 1);
257
258         name_button.set_name ("mixer strip button");
259         name_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
260         name_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::name_button_resized));
261
262         set_tooltip (&group_button, _("Mix group"));
263         group_button.set_name ("mixer strip button");
264
265         _comment_button.set_name (X_("mixer strip button"));
266         _comment_button.set_text_ellipsize (Pango::ELLIPSIZE_END);
267         _comment_button.signal_clicked.connect (sigc::mem_fun (*this, &RouteUI::toggle_comment_editor));
268         _comment_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::comment_button_resized));
269
270         // TODO implement ArdourKnob::on_size_request properly
271 #define PX_SCALE(px) std::max((float)px, rintf((float)px * UIConfiguration::instance().get_ui_scale()))
272         trim_control.set_size_request (PX_SCALE(19), PX_SCALE(19));
273 #undef PX_SCALE
274         trim_control.set_tooltip_prefix (_("Trim: "));
275         trim_control.set_name ("trim knob");
276         trim_control.set_no_show_all (true);
277         input_button_box.pack_start (trim_control, false, false);
278
279         global_vpacker.set_border_width (1);
280         global_vpacker.set_spacing (0);
281
282         width_button.set_name ("mixer strip button");
283         hide_button.set_name ("mixer strip button");
284
285         width_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::width_button_pressed), false);
286         hide_button.signal_clicked.connect (sigc::mem_fun(*this, &MixerStrip::hide_clicked));
287
288         width_hide_box.set_spacing (2);
289         width_hide_box.pack_start (width_button, false, true);
290         width_hide_box.pack_start (number_label, true, true);
291         width_hide_box.pack_end (hide_button, false, true);
292
293         number_label.set_text ("-");
294         number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
295         number_label.set_no_show_all ();
296         number_label.set_name ("tracknumber label");
297         number_label.set_fixed_colors (0x80808080, 0x80808080);
298         number_label.set_alignment (.5, .5);
299         number_label.set_fallthrough_to_parent (true);
300         number_label.set_tweaks (ArdourButton::OccasionalText);
301
302         global_vpacker.set_spacing (2);
303         if (!ARDOUR::Profile->get_trx()) {
304                 global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
305                 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
306                 global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
307                 global_vpacker.pack_start (_invert_button_box, Gtk::PACK_SHRINK);
308                 global_vpacker.pack_start (processor_box, true, true);
309         }
310         global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
311         global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
312         global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
313         global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
314         global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
315         global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
316         global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
317         if (!ARDOUR::Profile->get_trx()) {
318                 global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
319                 global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
320         } else {
321                 global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
322         }
323
324         //add a spacer underneath the master bus;
325         //this fills the area that is taken up by the scrollbar on the tracks;
326         //and therefore keeps the faders "even" across the bottom
327         HScrollbar scrollbar;
328         Gtk::Requisition requisition(scrollbar.size_request ());
329         int scrollbar_height = requisition.height;
330         spacer.set_size_request (-1, scrollbar_height+2);  //+2 is a fudge factor to accomodate extra padding in mixer strip
331         global_vpacker.pack_end (spacer, false, false);
332
333         global_frame.add (global_vpacker);
334         global_frame.set_shadow_type (Gtk::SHADOW_IN);
335         global_frame.set_name ("BaseFrame");
336
337         add (global_frame);
338
339         /* force setting of visible selected status */
340
341         _selected = true;
342         set_selected (false);
343
344         _packed = false;
345         _embedded = false;
346
347         _session->engine().Stopped.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_stopped, this), gui_context());
348         _session->engine().Running.connect (*this, invalidator (*this), boost::bind (&MixerStrip::engine_running, this), gui_context());
349
350         input_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::input_press), false);
351         input_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::input_release), false);
352         input_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::input_button_resized));
353
354         input_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
355         output_button.set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
356
357         output_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::output_press), false);
358         output_button.signal_button_release_event().connect (sigc::mem_fun(*this, &MixerStrip::output_release), false);
359         output_button.signal_size_allocate().connect (sigc::mem_fun (*this, &MixerStrip::output_button_resized));
360
361         number_label.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::number_button_button_press), false);
362
363         name_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::name_button_button_press), false);
364
365         group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &MixerStrip::select_route_group), false);
366
367         _width = (Width) -1;
368
369         /* start off as a passthru strip. we'll correct this, if necessary,
370            in update_diskstream_display().
371         */
372
373         /* start off as a passthru strip. we'll correct this, if necessary,
374            in update_diskstream_display().
375         */
376
377         if (is_midi_track()) {
378                 set_name ("MidiTrackStripBase");
379         } else {
380                 set_name ("AudioTrackStripBase");
381         }
382
383         add_events (Gdk::BUTTON_RELEASE_MASK|
384                     Gdk::ENTER_NOTIFY_MASK|
385                     Gdk::LEAVE_NOTIFY_MASK|
386                     Gdk::KEY_PRESS_MASK|
387                     Gdk::KEY_RELEASE_MASK);
388
389         set_flags (get_flags() | Gtk::CAN_FOCUS);
390
391         AudioEngine::instance()->PortConnectedOrDisconnected.connect (
392                 *this, invalidator (*this), boost::bind (&MixerStrip::port_connected_or_disconnected, this, _1, _3), gui_context ()
393                 );
394
395         /* Add the widgets under visibility control to the VisibilityGroup; the names used here
396            must be the same as those used in RCOptionEditor so that the configuration changes
397            are recognised when they occur.
398         */
399         _visibility.add (&input_button_box, X_("Input"), _("Input"), false);
400         _visibility.add (&_invert_button_box, X_("PhaseInvert"), _("Phase Invert"), false);
401         _visibility.add (&rec_mon_table, X_("RecMon"), _("Record & Monitor"), false);
402         _visibility.add (&solo_iso_table, X_("SoloIsoLock"), _("Solo Iso / Lock"), false);
403         _visibility.add (&output_button, X_("Output"), _("Output"), false);
404         _visibility.add (&_comment_button, X_("Comments"), _("Comments"), false);
405         _visibility.add (&control_slave_ui, X_("VCA"), _("VCA Assigns"), false);
406
407         parameter_changed (X_("mixer-element-visibility"));
408         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MixerStrip::parameter_changed));
409         Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
410         _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
411
412         //watch for mouse enter/exit so we can do some stuff
413         signal_enter_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_enter_event ));
414         signal_leave_notify_event().connect (sigc::mem_fun(*this, &MixerStrip::mixer_strip_leave_event ));
415
416         gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
417 }
418
419 MixerStrip::~MixerStrip ()
420 {
421         CatchDeletion (this);
422
423         if (this ==_entered_mixer_strip)
424                 _entered_mixer_strip = NULL;
425 }
426
427 bool
428 MixerStrip::mixer_strip_enter_event (GdkEventCrossing* /*ev*/)
429 {
430         _entered_mixer_strip = this;
431
432         //although we are triggering on the "enter", to the user it will appear that it is happenin on the "leave"
433         //because the mixerstrip control is a parent that encompasses the strip
434         deselect_all_processors();
435
436         return false;
437 }
438
439 bool
440 MixerStrip::mixer_strip_leave_event (GdkEventCrossing *ev)
441 {
442         //if we have moved outside our strip, but not into a child view, then deselect ourselves
443         if ( !(ev->detail == GDK_NOTIFY_INFERIOR) ) {
444                 _entered_mixer_strip= 0;
445
446                 //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
447                 gpm.gain_display.set_sensitive(false);
448                 gpm.show_gain();
449                 gpm.gain_display.set_sensitive(true);
450
451                 //if we leave this mixer strip we need to clear out any selections
452                 //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         }
454
455         return false;
456 }
457
458 string
459 MixerStrip::name() const
460 {
461         if (_route) {
462                 return _route->name();
463         }
464         return string();
465 }
466
467 void
468 MixerStrip::update_trim_control ()
469 {
470         if (route()->trim() && route()->trim()->active() &&
471             route()->n_inputs().n_audio() > 0) {
472                 trim_control.show ();
473                 trim_control.set_controllable (route()->trim()->gain_control());
474         } else {
475                 trim_control.hide ();
476                 boost::shared_ptr<Controllable> none;
477                 trim_control.set_controllable (none);
478         }
479 }
480
481 void
482 MixerStrip::set_route (boost::shared_ptr<Route> rt)
483 {
484         //the rec/monitor stuff only shows up for tracks.
485         //the show_sends only shows up for buses.
486         //remove them all here, and we may add them back later
487         if (show_sends_button->get_parent()) {
488                 rec_mon_table.remove (*show_sends_button);
489         }
490         if (rec_enable_button->get_parent()) {
491                 rec_mon_table.remove (*rec_enable_button);
492         }
493         if (monitor_input_button->get_parent()) {
494                 rec_mon_table.remove (*monitor_input_button);
495         }
496         if (monitor_disk_button->get_parent()) {
497                 rec_mon_table.remove (*monitor_disk_button);
498         }
499         if (group_button.get_parent()) {
500                 bottom_button_table.remove (group_button);
501         }
502
503         RouteUI::set_route (rt);
504
505         control_slave_ui.set_stripable (boost::dynamic_pointer_cast<Stripable> (rt));
506
507         /* ProcessorBox needs access to _route so that it can read
508            GUI object state.
509         */
510         processor_box.set_route (rt);
511
512         revert_to_default_display ();
513
514         /* unpack these from the parent and stuff them into our own
515            table
516         */
517
518         if (gpm.peak_display.get_parent()) {
519                 gpm.peak_display.get_parent()->remove (gpm.peak_display);
520         }
521         if (gpm.gain_display.get_parent()) {
522                 gpm.gain_display.get_parent()->remove (gpm.gain_display);
523         }
524
525         gpm.set_type (rt->meter_type());
526
527         mute_solo_table.attach (gpm.gain_display,0,1,1,2, EXPAND|FILL, EXPAND);
528         mute_solo_table.attach (gpm.peak_display,1,2,1,2, EXPAND|FILL, EXPAND);
529
530         if (solo_button->get_parent()) {
531                 mute_solo_table.remove (*solo_button);
532         }
533
534         if (mute_button->get_parent()) {
535                 mute_solo_table.remove (*mute_button);
536         }
537
538         if (route()->is_master()) {
539                 solo_button->hide ();
540                 mute_button->show ();
541                 rec_mon_table.hide ();
542                 if (solo_iso_table.get_parent()) {
543                         solo_iso_table.get_parent()->remove(solo_iso_table);
544                 }
545                 if (monitor_section_button == 0) {
546                         Glib::RefPtr<Action> act = ActionManager::get_action ("Common", "ToggleMonitorSection");
547                         _session->MonitorChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::monitor_changed, this), gui_context());
548
549                         monitor_section_button = manage (new ArdourButton);
550                         monitor_changed ();
551                         monitor_section_button->set_related_action (act);
552                         set_tooltip (monitor_section_button, _("Show/Hide Monitoring Section"));
553                         mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
554                         monitor_section_button->show();
555                         monitor_section_button->unset_flags (Gtk::CAN_FOCUS);
556                 }
557                 parameter_changed ("use-monitor-bus");
558         } else {
559                 bottom_button_table.attach (group_button, 1, 2, 0, 1);
560                 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
561                 mute_solo_table.attach (*solo_button, 1, 2, 0, 1);
562                 mute_button->show ();
563                 solo_button->show ();
564                 rec_mon_table.show ();
565         }
566
567         if (_mixer_owned && route()->is_master() ) {
568                 spacer.show();
569         } else {
570                 spacer.hide();
571         }
572
573         if (is_track()) {
574                 monitor_input_button->show ();
575                 monitor_disk_button->show ();
576         } else {
577                 monitor_input_button->hide();
578                 monitor_disk_button->hide ();
579         }
580
581         update_trim_control();
582
583         if (is_midi_track()) {
584                 if (midi_input_enable_button == 0) {
585                         midi_input_enable_button = manage (new ArdourButton);
586                         midi_input_enable_button->set_name ("midi input button");
587                         midi_input_enable_button->set_elements ((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::VectorIcon));
588                         midi_input_enable_button->set_icon (ArdourIcon::DinMidi);
589                         midi_input_enable_button->signal_button_press_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_press), false);
590                         midi_input_enable_button->signal_button_release_event().connect (sigc::mem_fun (*this, &MixerStrip::input_active_button_release), false);
591                         set_tooltip (midi_input_enable_button, _("Enable/Disable MIDI input"));
592                 } else {
593                         input_button_box.remove (*midi_input_enable_button);
594                 }
595                 /* get current state */
596                 midi_input_status_changed ();
597                 input_button_box.pack_start (*midi_input_enable_button, false, false);
598                 /* follow changes */
599                 midi_track()->InputActiveChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::midi_input_status_changed, this), gui_context());
600         } else {
601                 if (midi_input_enable_button) {
602                         /* removal from the container will delete it */
603                         input_button_box.remove (*midi_input_enable_button);
604                         midi_input_enable_button = 0;
605                 }
606         }
607
608         if (is_audio_track()) {
609                 boost::shared_ptr<AudioTrack> at = audio_track();
610                 at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::map_frozen, this), gui_context());
611         }
612
613         if (is_track ()) {
614
615                 rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
616                 rec_enable_button->show();
617
618                 if (ARDOUR::Profile->get_mixbus()) {
619                         rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
620                         rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
621                 } else if (ARDOUR::Profile->get_trx()) {
622                         rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 2);
623                 } else {
624                         rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
625                         rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
626                 }
627
628         } else {
629
630                 /* non-master bus */
631
632                 if (!_route->is_master()) {
633                         rec_mon_table.attach (*show_sends_button, 0, 1, 0, 2);
634                         show_sends_button->show();
635                 }
636         }
637
638         meter_point_button.set_text (meter_point_string (_route->meter_point()));
639
640         delete route_ops_menu;
641         route_ops_menu = 0;
642
643         _route->meter_change.connect (route_connections, invalidator (*this), bind (&MixerStrip::meter_changed, this), gui_context());
644         _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_input_display, this), gui_context());
645         _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&MixerStrip::update_output_display, this), gui_context());
646         _route->route_group_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::route_group_changed, this), gui_context());
647
648         _route->io_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::io_changed_proxy, this), gui_context ());
649
650         if (_route->panner_shell()) {
651                 update_panner_choices();
652                 _route->panner_shell()->Changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::connect_to_pan, this), gui_context());
653         }
654
655         if (is_audio_track()) {
656                 audio_track()->DiskstreamChanged.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::diskstream_changed, this), gui_context());
657         }
658
659         _route->comment_changed.connect (route_connections, invalidator (*this), boost::bind (&MixerStrip::setup_comment_button, this), gui_context());
660
661         set_stuff_from_route ();
662
663         /* now force an update of all the various elements */
664
665         update_mute_display ();
666         update_solo_display ();
667         name_changed ();
668         comment_changed ();
669         route_group_changed ();
670         update_track_number_visibility ();
671
672         connect_to_pan ();
673         panners.setup_pan ();
674
675         if (has_audio_outputs ()) {
676                 panners.show_all ();
677         } else {
678                 panners.hide_all ();
679         }
680
681         update_diskstream_display ();
682         update_input_display ();
683         update_output_display ();
684
685         add_events (Gdk::BUTTON_RELEASE_MASK);
686
687         processor_box.show ();
688
689         if (!route()->is_master() && !route()->is_monitor()) {
690                 /* we don't allow master or control routes to be hidden */
691                 hide_button.show();
692                 number_label.show();
693         }
694
695         gpm.reset_peak_display ();
696         gpm.gain_display.show ();
697         gpm.peak_display.show ();
698
699         width_button.show();
700         width_hide_box.show();
701         global_frame.show();
702         global_vpacker.show();
703         mute_solo_table.show();
704         bottom_button_table.show();
705         gpm.show_all ();
706         meter_point_button.show();
707         input_button_box.show_all();
708         output_button.show();
709         name_button.show();
710         _comment_button.show();
711         group_button.show();
712         gpm.gain_automation_state_button.show();
713
714         parameter_changed ("mixer-element-visibility");
715         map_frozen();
716
717         show ();
718 }
719
720 void
721 MixerStrip::set_stuff_from_route ()
722 {
723         /* if width is not set, it will be set by the MixerUI or editor */
724
725         string str = gui_property ("strip-width");
726         if (!str.empty()) {
727                 set_width_enum (Width (string_2_enum (str, _width)), this);
728         }
729 }
730
731 void
732 MixerStrip::set_width_enum (Width w, void* owner)
733 {
734         /* always set the gpm width again, things may be hidden */
735
736         gpm.set_width (w);
737         panners.set_width (w);
738
739         boost::shared_ptr<AutomationList> gain_automation = _route->gain_control()->alist();
740
741         _width_owner = owner;
742
743         _width = w;
744
745         if (_width_owner == this) {
746                 set_gui_property ("strip-width", enum_2_string (_width));
747         }
748
749         set_button_names ();
750
751         const float scale = std::max(1.f, UIConfiguration::instance().get_ui_scale());
752
753         switch (w) {
754         case Wide:
755
756                 if (show_sends_button)  {
757                         show_sends_button->set_text (_("Aux"));
758                 }
759
760                 gpm.gain_automation_style_button.set_text (
761                                 gpm.astyle_string(gain_automation->automation_style()));
762                 gpm.gain_automation_state_button.set_text (
763                                 gpm.astate_string(gain_automation->automation_state()));
764
765                 if (_route->panner()) {
766                         ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
767                                         panners.astyle_string(_route->panner()->automation_style()));
768                         ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
769                                         panners.astate_string(_route->panner()->automation_state()));
770                 }
771
772                 {
773                         // panners expect an even number of horiz. pixels
774                         int width = rintf (max (110.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
775                         width &= ~1;
776                         set_size_request (width, -1);
777                 }
778                 break;
779
780         case Narrow:
781
782                 if (show_sends_button) {
783                         show_sends_button->set_text (_("Snd"));
784                 }
785
786                 gpm.gain_automation_style_button.set_text (
787                                 gpm.short_astyle_string(gain_automation->automation_style()));
788                 gpm.gain_automation_state_button.set_text (
789                                 gpm.short_astate_string(gain_automation->automation_state()));
790                 gain_meter().setup_meters (); // recalc meter width
791
792                 if (_route->panner()) {
793                         ((Gtk::Label*)panners.pan_automation_style_button.get_child())->set_text (
794                         panners.short_astyle_string(_route->panner()->automation_style()));
795                         ((Gtk::Label*)panners.pan_automation_state_button.get_child())->set_text (
796                         panners.short_astate_string(_route->panner()->automation_state()));
797                 }
798
799                 {
800                         // panners expect an even number of horiz. pixels
801                         int width = rintf (max (60.f * scale, gpm.get_gm_width() + 10.f * scale)) + 1;
802                         width &= ~1;
803                         set_size_request (width, -1);
804                 }
805                 break;
806         }
807
808         processor_box.set_width (w);
809
810         update_input_display ();
811         update_output_display ();
812         setup_comment_button ();
813         route_group_changed ();
814         name_changed ();
815         WidthChanged ();
816 }
817
818 void
819 MixerStrip::set_packed (bool yn)
820 {
821         _packed = yn;
822
823         if (_packed) {
824                 set_gui_property ("visible", true);
825         } else {
826                 set_gui_property ("visible", false);
827         }
828 }
829
830
831 struct RouteCompareByName {
832         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
833                 return a->name().compare (b->name()) < 0;
834         }
835 };
836
837 gint
838 MixerStrip::output_release (GdkEventButton *ev)
839 {
840         switch (ev->button) {
841         case 3:
842                 edit_output_configuration ();
843                 break;
844         }
845
846         return false;
847 }
848
849 gint
850 MixerStrip::output_press (GdkEventButton *ev)
851 {
852         using namespace Menu_Helpers;
853         if (!_session->engine().connected()) {
854                 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
855                 msg.run ();
856                 return true;
857         }
858
859         MenuList& citems = output_menu.items();
860         switch (ev->button) {
861
862         case 3:
863                 return false;  //wait for the mouse-up to pop the dialog
864
865         case 1:
866         {
867                 output_menu.set_name ("ArdourContextMenu");
868                 citems.clear ();
869                 output_menu_bundles.clear ();
870
871                 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_output)));
872
873                 citems.push_back (SeparatorElem());
874                 uint32_t const n_with_separator = citems.size ();
875
876                 ARDOUR::BundleList current = _route->output()->bundles_connected ();
877
878                 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
879
880                 /* give user bundles first chance at being in the menu */
881
882                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
883                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
884                                 maybe_add_bundle_to_output_menu (*i, current);
885                         }
886                 }
887
888                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
889                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
890                                 maybe_add_bundle_to_output_menu (*i, current);
891                         }
892                 }
893
894                 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
895                 RouteList copy = *routes;
896                 copy.sort (RouteCompareByName ());
897                 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
898                         maybe_add_bundle_to_output_menu ((*i)->input()->bundle(), current);
899                 }
900
901                 if (citems.size() == n_with_separator) {
902                         /* no routes added; remove the separator */
903                         citems.pop_back ();
904                 }
905
906                 if (!ARDOUR::Profile->get_mixbus()) {
907                         citems.push_back (SeparatorElem());
908
909                         for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
910                                 citems.push_back (
911                                                 MenuElem (
912                                                         string_compose (_("Add %1 port"), (*i).to_i18n_string()),
913                                                         sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_output_port), *i)
914                                                         )
915                                                 );
916                         }
917                 }
918
919                 citems.push_back (SeparatorElem());
920                 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_output_configuration)));
921
922                 Gtkmm2ext::anchored_menu_popup(&output_menu, &output_button, "",
923                                                1, ev->time);
924
925                 break;
926         }
927
928         default:
929                 break;
930         }
931         return TRUE;
932 }
933
934 gint
935 MixerStrip::input_release (GdkEventButton *ev)
936 {
937         switch (ev->button) {
938
939         case 3:
940                 edit_input_configuration ();
941                 break;
942         default:
943                 break;
944
945         }
946
947         return false;
948 }
949
950
951 gint
952 MixerStrip::input_press (GdkEventButton *ev)
953 {
954         using namespace Menu_Helpers;
955
956         MenuList& citems = input_menu.items();
957         input_menu.set_name ("ArdourContextMenu");
958         citems.clear();
959
960         if (!_session->engine().connected()) {
961                 MessageDialog msg (_("Not connected to audio engine - no I/O changes are possible"));
962                 msg.run ();
963                 return true;
964         }
965
966         if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value())
967                 return true;
968
969         switch (ev->button) {
970
971         case 3:
972                 return false;  //don't handle the mouse-down here.  wait for mouse-up to pop the menu
973
974         case 1:
975         {
976                 citems.push_back (MenuElem (_("Disconnect"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::disconnect_input)));
977
978                 citems.push_back (SeparatorElem());
979                 uint32_t const n_with_separator = citems.size ();
980
981                 input_menu_bundles.clear ();
982
983                 ARDOUR::BundleList current = _route->input()->bundles_connected ();
984
985                 boost::shared_ptr<ARDOUR::BundleList> b = _session->bundles ();
986
987                 /* give user bundles first chance at being in the menu */
988
989                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
990                         if (boost::dynamic_pointer_cast<UserBundle> (*i)) {
991                                 maybe_add_bundle_to_input_menu (*i, current);
992                         }
993                 }
994
995                 for (ARDOUR::BundleList::iterator i = b->begin(); i != b->end(); ++i) {
996                         if (boost::dynamic_pointer_cast<UserBundle> (*i) == 0) {
997                                 maybe_add_bundle_to_input_menu (*i, current);
998                         }
999                 }
1000
1001                 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1002                 RouteList copy = *routes;
1003                 copy.sort (RouteCompareByName ());
1004                 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1005                         maybe_add_bundle_to_input_menu ((*i)->output()->bundle(), current);
1006                 }
1007
1008                 if (citems.size() == n_with_separator) {
1009                         /* no routes added; remove the separator */
1010                         citems.pop_back ();
1011                 }
1012
1013                 citems.push_back (SeparatorElem());
1014                 for (DataType::iterator i = DataType::begin(); i != DataType::end(); ++i) {
1015                         citems.push_back (
1016                                 MenuElem (
1017                                         string_compose (_("Add %1 port"), (*i).to_i18n_string()),
1018                                         sigc::bind (sigc::mem_fun (*this, &MixerStrip::add_input_port), *i)
1019                                         )
1020                                 );
1021                 }
1022
1023                 citems.push_back (SeparatorElem());
1024                 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*(static_cast<RouteUI*>(this)), &RouteUI::edit_input_configuration)));
1025
1026                 Gtkmm2ext::anchored_menu_popup(&input_menu, &input_button, "",
1027                                                1, ev->time);
1028
1029                 break;
1030         }
1031         default:
1032                 break;
1033         }
1034         return TRUE;
1035 }
1036
1037 void
1038 MixerStrip::bundle_input_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1039 {
1040         if (ignore_toggle) {
1041                 return;
1042         }
1043
1044         ARDOUR::BundleList current = _route->input()->bundles_connected ();
1045
1046         if (std::find (current.begin(), current.end(), c) == current.end()) {
1047                 _route->input()->connect_ports_to_bundle (c, true, this);
1048         } else {
1049                 _route->input()->disconnect_ports_from_bundle (c, this);
1050         }
1051 }
1052
1053 void
1054 MixerStrip::bundle_output_chosen (boost::shared_ptr<ARDOUR::Bundle> c)
1055 {
1056         if (ignore_toggle) {
1057                 return;
1058         }
1059
1060         ARDOUR::BundleList current = _route->output()->bundles_connected ();
1061
1062         if (std::find (current.begin(), current.end(), c) == current.end()) {
1063                 _route->output()->connect_ports_to_bundle (c, true, this);
1064         } else {
1065                 _route->output()->disconnect_ports_from_bundle (c, this);
1066         }
1067 }
1068
1069 void
1070 MixerStrip::maybe_add_bundle_to_input_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1071 {
1072         using namespace Menu_Helpers;
1073
1074         if (b->ports_are_outputs() == false || b->nchannels() != _route->n_inputs() || *b == *_route->output()->bundle()) {
1075                 return;
1076         }
1077
1078         list<boost::shared_ptr<Bundle> >::iterator i = input_menu_bundles.begin ();
1079         while (i != input_menu_bundles.end() && b->has_same_ports (*i) == false) {
1080                 ++i;
1081         }
1082
1083         if (i != input_menu_bundles.end()) {
1084                 return;
1085         }
1086
1087         input_menu_bundles.push_back (b);
1088
1089         MenuList& citems = input_menu.items();
1090
1091         std::string n = b->name ();
1092         replace_all (n, "_", " ");
1093
1094         citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_input_chosen), b)));
1095 }
1096
1097 void
1098 MixerStrip::maybe_add_bundle_to_output_menu (boost::shared_ptr<Bundle> b, ARDOUR::BundleList const& /*current*/)
1099 {
1100         using namespace Menu_Helpers;
1101
1102         if (b->ports_are_inputs() == false || b->nchannels() != _route->n_outputs() || *b == *_route->input()->bundle()) {
1103                 return;
1104         }
1105
1106         list<boost::shared_ptr<Bundle> >::iterator i = output_menu_bundles.begin ();
1107         while (i != output_menu_bundles.end() && b->has_same_ports (*i) == false) {
1108                 ++i;
1109         }
1110
1111         if (i != output_menu_bundles.end()) {
1112                 return;
1113         }
1114
1115         output_menu_bundles.push_back (b);
1116
1117         MenuList& citems = output_menu.items();
1118
1119         std::string n = b->name ();
1120         replace_all (n, "_", " ");
1121
1122         citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &MixerStrip::bundle_output_chosen), b)));
1123 }
1124
1125 void
1126 MixerStrip::update_diskstream_display ()
1127 {
1128         if (is_track() && input_selector) {
1129                         input_selector->hide_all ();
1130         }
1131
1132         route_color_changed ();
1133 }
1134
1135 void
1136 MixerStrip::connect_to_pan ()
1137 {
1138         ENSURE_GUI_THREAD (*this, &MixerStrip::connect_to_pan)
1139
1140         panstate_connection.disconnect ();
1141         panstyle_connection.disconnect ();
1142
1143         if (!_route->panner()) {
1144                 return;
1145         }
1146
1147         boost::shared_ptr<Pannable> p = _route->pannable ();
1148
1149         p->automation_state_changed.connect (panstate_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_state_changed, &panners), gui_context());
1150         p->automation_style_changed.connect (panstyle_connection, invalidator (*this), boost::bind (&PannerUI::pan_automation_style_changed, &panners), gui_context());
1151
1152         /* This call reduncant, PannerUI::set_panner() connects to _panshell->Changed itself
1153          * However, that only works a panner was previously set.
1154          *
1155          * PannerUI must remain subscribed to _panshell->Changed() in case
1156          * we switch the panner eg. AUX-Send and back
1157          * _route->panner_shell()->Changed() vs _panshell->Changed
1158          */
1159         if (panners._panner == 0) {
1160                 panners.panshell_changed ();
1161         }
1162         update_panner_choices();
1163 }
1164
1165 void
1166 MixerStrip::update_panner_choices ()
1167 {
1168         ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices)
1169         if (!_route->panner_shell()) { return; }
1170
1171         uint32_t in = _route->output()->n_ports().n_audio();
1172         uint32_t out = in;
1173         if (_route->panner()) {
1174                 in = _route->panner()->in().n_audio();
1175         }
1176
1177         panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
1178 }
1179
1180 /*
1181  * Output port labelling
1182  * =====================
1183  *
1184  * Case 1: Each output has one connection, all connections are to system:playback_%i
1185  *   out 1 -> system:playback_1
1186  *   out 2 -> system:playback_2
1187  *   out 3 -> system:playback_3
1188  *   Display as: 1/2/3
1189  *
1190  * Case 2: Each output has one connection, all connections are to ardour:track_x/in 1
1191  *   out 1 -> ardour:track_x/in 1
1192  *   out 2 -> ardour:track_x/in 2
1193  *   Display as: track_x
1194  *
1195  * Case 3: Each output has one connection, all connections are to Jack client "program x"
1196  *   out 1 -> program x:foo
1197  *   out 2 -> program x:foo
1198  *   Display as: program x
1199  *
1200  * Case 4: No connections (Disconnected)
1201  *   Display as: -
1202  *
1203  * Default case (unusual routing):
1204  *   Display as: *number of connections*
1205  *
1206  * Tooltips
1207  * ========
1208  * .-----------------------------------------------.
1209  * | Mixdown                                       |
1210  * | out 1 -> ardour:master/in 1, jamin:input/in 1 |
1211  * | out 2 -> ardour:master/in 2, jamin:input/in 2 |
1212  * '-----------------------------------------------'
1213  * .-----------------------------------------------.
1214  * | Guitar SM58                                   |
1215  * | Disconnected                                  |
1216  * '-----------------------------------------------'
1217  */
1218
1219 void
1220 MixerStrip::update_io_button (boost::shared_ptr<ARDOUR::Route> route, Width width, bool for_input)
1221 {
1222         uint32_t io_count;
1223         uint32_t io_index;
1224         boost::shared_ptr<IO> io;
1225         boost::shared_ptr<Port> port;
1226         vector<string> port_connections;
1227
1228         uint32_t total_connection_count = 0;
1229         uint32_t io_connection_count = 0;
1230         uint32_t ardour_connection_count = 0;
1231         uint32_t system_connection_count = 0;
1232         uint32_t other_connection_count = 0;
1233         uint32_t typed_connection_count = 0;
1234
1235         ostringstream label;
1236
1237         bool have_label = false;
1238         bool each_io_has_one_connection = true;
1239
1240         string connection_name;
1241         string ardour_track_name;
1242         string other_connection_type;
1243         string system_ports;
1244         string system_port;
1245
1246         ostringstream tooltip;
1247         char * tooltip_cstr;
1248
1249         /* To avoid confusion, the button caption only shows connections that match the expected datatype
1250          *
1251          * First of all, if the user made only connections to a given type, we should use that one since
1252          * it is very probably what the user expects. If there are several connections types, then show
1253          * audio ones as primary, which matches expectations for both audio tracks with midi control and
1254          * synthesisers. This first heuristic can be expressed with these two rules:
1255          * A) If there are connected audio ports, consider audio as primary type.
1256          * B) Else, if there are connected midi ports, consider midi as primary type.
1257          *
1258          * If there are no connected ports, then we choose the primary type based on the type of existing
1259          * but unconnected ports. Again:
1260          * C) If there are audio ports, consider audio as primary type.
1261          * D) Else, if there are midi ports, consider midi as primary type. */
1262
1263         DataType dt = DataType::AUDIO;
1264         bool match = false;
1265
1266         if (for_input) {
1267                 io = route->input();
1268         } else {
1269                 io = route->output();
1270         }
1271
1272         io_count = io->n_ports().n_total();
1273         for (io_index = 0; io_index < io_count; ++io_index) {
1274                 port = io->nth (io_index);
1275                 if (port->connected()) {
1276                         match = true;
1277                         if (port->type() == DataType::AUDIO) {
1278                                 /* Rule A) applies no matter the remaining ports */
1279                                 dt = DataType::AUDIO;
1280                                 break;
1281                         }
1282                         if (port->type() == DataType::MIDI) {
1283                                 /* Rule B) is a good candidate... */
1284                                 dt = DataType::MIDI;
1285                                 /* ...but continue the loop to check remaining ports for rule A) */
1286                         }
1287                 }
1288         }
1289
1290         if (!match) {
1291                 /* Neither rule A) nor rule B) matched */
1292                 if ( io->n_ports().n_audio() > 0 ) {
1293                         /* Rule C */
1294                         dt = DataType::AUDIO;
1295                 } else if ( io->n_ports().n_midi() > 0 ) {
1296                         /* Rule D */
1297                         dt = DataType::MIDI;
1298                 }
1299         }
1300
1301         if ( dt == DataType::MIDI ) {
1302                 tooltip << _("MIDI ");
1303         }
1304
1305         if (for_input) {
1306                 tooltip << string_compose (_("<b>INPUT</b> to %1"), Gtkmm2ext::markup_escape_text (route->name()));
1307         } else {
1308                 tooltip << string_compose (_("<b>OUTPUT</b> from %1"), Gtkmm2ext::markup_escape_text (route->name()));
1309         }
1310
1311         for (io_index = 0; io_index < io_count; ++io_index) {
1312                 port = io->nth (io_index);
1313
1314                 port_connections.clear ();
1315                 port->get_connections(port_connections);
1316
1317                 //ignore any port connections that don't match our DataType
1318                 if (port->type() != dt) {
1319                         if (!port_connections.empty()) {
1320                                 ++typed_connection_count;
1321                         }
1322                         continue;
1323                 }
1324
1325                 io_connection_count = 0;
1326
1327                 if (!port_connections.empty()) {
1328                         for (vector<string>::iterator i = port_connections.begin(); i != port_connections.end(); ++i) {
1329                                 string pn = "";
1330                                 string& connection_name (*i);
1331
1332                                 if (connection_name.find("system:") == 0) {
1333                                         pn = AudioEngine::instance()->get_pretty_name_by_name (connection_name);
1334                                 }
1335
1336                                 if (io_connection_count == 0) {
1337                                         tooltip << endl << Gtkmm2ext::markup_escape_text (port->name().substr(port->name().find("/") + 1))
1338                                                 << " -> "
1339                                                 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1340                                 } else {
1341                                         tooltip << ", "
1342                                                 << Gtkmm2ext::markup_escape_text ( pn.empty() ? connection_name : pn );
1343                                 }
1344
1345                                 if (connection_name.find(RouteUI::program_port_prefix) == 0) {
1346                                         if (ardour_track_name.empty()) {
1347                                                 // "ardour:Master/in 1" -> "ardour:Master/"
1348                                                 string::size_type slash = connection_name.find("/");
1349                                                 if (slash != string::npos) {
1350                                                         ardour_track_name = connection_name.substr(0, slash + 1);
1351                                                 }
1352                                         }
1353
1354                                         if (connection_name.find(ardour_track_name) == 0) {
1355                                                 ++ardour_connection_count;
1356                                         }
1357                                 } else if (!pn.empty()) {
1358                                         if (system_ports.empty()) {
1359                                                 system_ports += pn;
1360                                         } else {
1361                                                 system_ports += "/" + pn;
1362                                         }
1363                                         if (connection_name.find("system:") == 0) {
1364                                                 ++system_connection_count;
1365                                         }
1366                                 } else if (connection_name.find("system:midi_") == 0) {
1367                                         if (for_input) {
1368                                                 // "system:midi_capture_123" -> "123"
1369                                                 system_port = "M " + connection_name.substr(20);
1370                                         } else {
1371                                                 // "system:midi_playback_123" -> "123"
1372                                                 system_port = "M " + connection_name.substr(21);
1373                                         }
1374
1375                                         if (system_ports.empty()) {
1376                                                 system_ports += system_port;
1377                                         } else {
1378                                                 system_ports += "/" + system_port;
1379                                         }
1380
1381                                         ++system_connection_count;
1382
1383                                 } else if (connection_name.find("system:") == 0) {
1384                                         if (for_input) {
1385                                                 // "system:capture_123" -> "123"
1386                                                 system_port = connection_name.substr(15);
1387                                         } else {
1388                                                 // "system:playback_123" -> "123"
1389                                                 system_port = connection_name.substr(16);
1390                                         }
1391
1392                                         if (system_ports.empty()) {
1393                                                 system_ports += system_port;
1394                                         } else {
1395                                                 system_ports += "/" + system_port;
1396                                         }
1397
1398                                         ++system_connection_count;
1399                                 } else {
1400                                         if (other_connection_type.empty()) {
1401                                                 // "jamin:in 1" -> "jamin:"
1402                                                 other_connection_type = connection_name.substr(0, connection_name.find(":") + 1);
1403                                         }
1404
1405                                         if (connection_name.find(other_connection_type) == 0) {
1406                                                 ++other_connection_count;
1407                                         }
1408                                 }
1409
1410                                 ++total_connection_count;
1411                                 ++io_connection_count;
1412                         }
1413                 }
1414
1415                 if (io_connection_count != 1) {
1416                         each_io_has_one_connection = false;
1417                 }
1418         }
1419
1420         if (total_connection_count == 0) {
1421                 tooltip << endl << _("Disconnected");
1422         }
1423
1424         tooltip_cstr = new char[tooltip.str().size() + 1];
1425         strcpy(tooltip_cstr, tooltip.str().c_str());
1426
1427         if (for_input) {
1428                 set_tooltip (&input_button, tooltip_cstr);
1429         } else {
1430                 set_tooltip (&output_button, tooltip_cstr);
1431         }
1432
1433         delete [] tooltip_cstr;
1434
1435         if (each_io_has_one_connection) {
1436                 if (total_connection_count == ardour_connection_count) {
1437                         // all connections are to the same track in ardour
1438                         // "ardour:Master/" -> "Master"
1439                         string::size_type slash = ardour_track_name.find("/");
1440                         if (slash != string::npos) {
1441                                 const size_t ppps = RouteUI::program_port_prefix.size (); // "ardour:"
1442                                 label << ardour_track_name.substr (ppps, slash - ppps);
1443                                 have_label = true;
1444                         }
1445                 }
1446                 else if (total_connection_count == system_connection_count) {
1447                         // all connections are to system ports
1448                         label << system_ports;
1449                         have_label = true;
1450                 }
1451                 else if (total_connection_count == other_connection_count) {
1452                         // all connections are to the same external program eg jamin
1453                         // "jamin:" -> "jamin"
1454                         label << other_connection_type.substr(0, other_connection_type.size() - 1);
1455                         have_label = true;
1456                 }
1457         }
1458
1459         if (!have_label) {
1460                 if (total_connection_count == 0) {
1461                         // Disconnected
1462                         label << "-";
1463                 } else {
1464                         // Odd configuration
1465                         label << "*" << total_connection_count << "*";
1466                 }
1467                 if (typed_connection_count > 0) {
1468                         label << "\u2295"; // circled plus
1469                 }
1470         }
1471
1472         if (for_input) {
1473                 input_button.set_text (label.str());
1474         } else {
1475                 output_button.set_text (label.str());
1476         }
1477 }
1478
1479 void
1480 MixerStrip::update_input_display ()
1481 {
1482         update_io_button (_route, _width, true);
1483         panners.setup_pan ();
1484
1485         if (has_audio_outputs ()) {
1486                 panners.show_all ();
1487         } else {
1488                 panners.hide_all ();
1489         }
1490
1491 }
1492
1493 void
1494 MixerStrip::update_output_display ()
1495 {
1496         update_io_button (_route, _width, false);
1497         gpm.setup_meters ();
1498         panners.setup_pan ();
1499
1500         if (has_audio_outputs ()) {
1501                 panners.show_all ();
1502         } else {
1503                 panners.hide_all ();
1504         }
1505 }
1506
1507 void
1508 MixerStrip::fast_update ()
1509 {
1510         gpm.update_meters ();
1511 }
1512
1513 void
1514 MixerStrip::diskstream_changed ()
1515 {
1516         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&MixerStrip::update_diskstream_display, this));
1517 }
1518
1519 void
1520 MixerStrip::io_changed_proxy ()
1521 {
1522         Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_panner_choices));
1523         Glib::signal_idle().connect_once (sigc::mem_fun (*this, &MixerStrip::update_trim_control));
1524 }
1525
1526 void
1527 MixerStrip::port_connected_or_disconnected (boost::weak_ptr<Port> wa, boost::weak_ptr<Port> wb)
1528 {
1529         boost::shared_ptr<Port> a = wa.lock ();
1530         boost::shared_ptr<Port> b = wb.lock ();
1531
1532         if ((a && _route->input()->has_port (a)) || (b && _route->input()->has_port (b))) {
1533                 update_input_display ();
1534                 set_width_enum (_width, this);
1535         }
1536
1537         if ((a && _route->output()->has_port (a)) || (b && _route->output()->has_port (b))) {
1538                 update_output_display ();
1539                 set_width_enum (_width, this);
1540         }
1541 }
1542
1543 void
1544 MixerStrip::setup_comment_button ()
1545 {
1546         std::string comment = _route->comment();
1547
1548         set_tooltip (_comment_button, comment.empty() ? _("Click to add/edit comments") : _route->comment());
1549
1550         if (comment.empty ()) {
1551                 _comment_button.set_name ("generic button");
1552                 _comment_button.set_text (_width  == Wide ? _("Comments") : _("Cmt"));
1553                 return;
1554         }
1555
1556         _comment_button.set_name ("comment button");
1557
1558         string::size_type pos = comment.find_first_of (" \t\n");
1559         if (pos != string::npos) {
1560                 comment = comment.substr (0, pos);
1561         }
1562         if (comment.empty()) {
1563                 _comment_button.set_text (_width  == Wide ? _("Comments") : _("Cmt"));
1564         } else {
1565                 _comment_button.set_text (comment);
1566         }
1567 }
1568
1569 bool
1570 MixerStrip::select_route_group (GdkEventButton *ev)
1571 {
1572         using namespace Menu_Helpers;
1573
1574         if (ev->button == 1) {
1575
1576                 if (group_menu == 0) {
1577
1578                         PropertyList* plist = new PropertyList();
1579
1580                         plist->add (Properties::group_gain, true);
1581                         plist->add (Properties::group_mute, true);
1582                         plist->add (Properties::group_solo, true);
1583
1584                         group_menu = new RouteGroupMenu (_session, plist);
1585                 }
1586
1587                 WeakRouteList r;
1588                 r.push_back (route ());
1589                 group_menu->build (r);
1590
1591                 RouteGroup *rg = _route->route_group();
1592
1593                 Gtkmm2ext::anchored_menu_popup(group_menu->menu(), &group_button,
1594                                                rg ? rg->name() : _("No Group"),
1595                                                1, ev->time);
1596         }
1597
1598         return true;
1599 }
1600
1601 void
1602 MixerStrip::route_group_changed ()
1603 {
1604         ENSURE_GUI_THREAD (*this, &MixerStrip::route_group_changed)
1605
1606         RouteGroup *rg = _route->route_group();
1607
1608         if (rg) {
1609                 group_button.set_text (PBD::short_version (rg->name(), 5));
1610         } else {
1611                 switch (_width) {
1612                 case Wide:
1613                         group_button.set_text (_("Grp"));
1614                         break;
1615                 case Narrow:
1616                         group_button.set_text (_("~G"));
1617                         break;
1618                 }
1619         }
1620 }
1621
1622 void
1623 MixerStrip::route_color_changed ()
1624 {
1625         name_button.modify_bg (STATE_NORMAL, color());
1626         number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1627         reset_strip_style ();
1628 }
1629
1630 void
1631 MixerStrip::show_passthru_color ()
1632 {
1633         reset_strip_style ();
1634 }
1635
1636
1637 void
1638 MixerStrip::help_count_plugins (boost::weak_ptr<Processor> p)
1639 {
1640         boost::shared_ptr<Processor> processor (p.lock ());
1641         if (!processor || !processor->display_to_user()) {
1642                 return;
1643         }
1644         boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (processor);
1645 #ifdef MIXBUS
1646         if (pi && pi->is_channelstrip ()) {
1647                 return;
1648         }
1649 #endif
1650         if (pi) {
1651                 ++_plugin_insert_cnt;
1652         }
1653 }
1654 void
1655 MixerStrip::build_route_ops_menu ()
1656 {
1657         using namespace Menu_Helpers;
1658         route_ops_menu = new Menu;
1659         route_ops_menu->set_name ("ArdourContextMenu");
1660
1661         MenuList& items = route_ops_menu->items();
1662
1663         items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
1664
1665         items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
1666
1667         items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
1668
1669         items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
1670
1671         items.push_back (SeparatorElem());
1672
1673         if (!_route->is_master()) {
1674                 items.push_back (MenuElem (_("Save As Template..."), sigc::mem_fun(*this, &RouteUI::save_as_template)));
1675         }
1676         items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteUI::route_rename)));
1677         rename_menu_item = &items.back();
1678
1679         items.push_back (SeparatorElem());
1680         items.push_back (CheckMenuElem (_("Active")));
1681         Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1682         i->set_active (_route->active());
1683         i->set_sensitive(! _session->transport_rolling());
1684         i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
1685
1686         if (!Profile->get_mixbus ()) {
1687                 items.push_back (SeparatorElem());
1688                 items.push_back (CheckMenuElem (_("Strict I/O")));
1689                 i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1690                 i->set_active (_route->strict_io());
1691                 i->signal_activate().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*_route, &Route::set_strict_io), !_route->strict_io())));
1692         }
1693
1694         _plugin_insert_cnt = 0;
1695         _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::help_count_plugins));
1696         if (_plugin_insert_cnt > 0) {
1697                 items.push_back (SeparatorElem());
1698                 items.push_back (MenuElem (_("Pin Connections..."), sigc::mem_fun (*this, &RouteUI::manage_pins)));
1699         }
1700
1701         if (_route->the_instrument () && _route->the_instrument ()->output_streams().n_audio() > 2) {
1702                 // TODO ..->n_audio() > 1 && separate_output_groups) hard to check here every time.
1703                 items.push_back (MenuElem (_("Fan out to Busses"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), true, true)));
1704                 items.push_back (MenuElem (_("Fan out to Tracks"), sigc::bind (sigc::mem_fun (*this, &RouteUI::fan_out), false, true)));
1705         }
1706
1707         items.push_back (SeparatorElem());
1708         items.push_back (MenuElem (_("Adjust Latency..."), sigc::mem_fun (*this, &RouteUI::adjust_latency)));
1709
1710         items.push_back (SeparatorElem());
1711         items.push_back (CheckMenuElem (_("Protect Against Denormals"), sigc::mem_fun (*this, &RouteUI::toggle_denormal_protection)));
1712         denormal_menu_item = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
1713         denormal_menu_item->set_active (_route->denormal_protection());
1714
1715         if (_route) {
1716                 /* note that this relies on selection being shared across editor and
1717                    mixer (or global to the backend, in the future), which is the only
1718                    sane thing for users anyway.
1719                 */
1720
1721                 RouteTimeAxisView* rtav = PublicEditor::instance().get_route_view_by_route_id (_route->id());
1722                 if (rtav) {
1723                         Selection& selection (PublicEditor::instance().get_selection());
1724                         if (!selection.selected (rtav)) {
1725                                 selection.set (rtav);
1726                         }
1727
1728                         if (!_route->is_master()) {
1729                                 items.push_back (SeparatorElem());
1730                                 items.push_back (MenuElem (_("Duplicate..."), sigc::mem_fun (*this, &RouteUI::duplicate_selected_routes)));
1731                         }
1732
1733                         items.push_back (SeparatorElem());
1734                         items.push_back (MenuElem (_("Remove"), sigc::mem_fun(PublicEditor::instance(), &PublicEditor::remove_tracks)));
1735                 }
1736         }
1737 }
1738
1739 gboolean
1740 MixerStrip::name_button_button_press (GdkEventButton* ev)
1741 {
1742         if (ev->button == 1 || ev->button == 3) {
1743                 list_route_operations ();
1744
1745                 /* do not allow rename if the track is record-enabled */
1746                 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1747                 if (ev->button == 1) {
1748                         Gtkmm2ext::anchored_menu_popup(route_ops_menu, &name_button, "",
1749                                                        1, ev->time);
1750                 } else {
1751                         route_ops_menu->popup (3, ev->time);
1752                 }
1753
1754                 return true;
1755         }
1756
1757         return false;
1758 }
1759
1760 gboolean
1761 MixerStrip::number_button_button_press (GdkEventButton* ev)
1762 {
1763         if (  ev->button == 3 ) {
1764                 list_route_operations ();
1765
1766                 /* do not allow rename if the track is record-enabled */
1767                 rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value());
1768                 route_ops_menu->popup (1, ev->time);
1769
1770                 return true;
1771         }
1772
1773         return false;
1774 }
1775
1776 void
1777 MixerStrip::list_route_operations ()
1778 {
1779         delete route_ops_menu;
1780         build_route_ops_menu ();
1781 }
1782
1783 void
1784 MixerStrip::set_selected (bool yn)
1785 {
1786         AxisView::set_selected (yn);
1787
1788         if (selected()) {
1789                 global_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
1790                 global_frame.set_name ("MixerStripSelectedFrame");
1791         } else {
1792                 global_frame.set_shadow_type (Gtk::SHADOW_IN);
1793                 global_frame.set_name ("MixerStripFrame");
1794         }
1795
1796         global_frame.queue_draw ();
1797
1798 //      if (!yn)
1799 //              processor_box.deselect_all_processors();
1800 }
1801
1802 void
1803 MixerStrip::route_property_changed (const PropertyChange& what_changed)
1804 {
1805         if (what_changed.contains (ARDOUR::Properties::name)) {
1806                 name_changed ();
1807         }
1808 }
1809
1810 void
1811 MixerStrip::name_changed ()
1812 {
1813         switch (_width) {
1814                 case Wide:
1815                         name_button.set_text (_route->name());
1816                         break;
1817                 case Narrow:
1818                         name_button.set_text (PBD::short_version (_route->name(), 5));
1819                         break;
1820         }
1821
1822         set_tooltip (name_button, _route->name());
1823
1824         if (_session->config.get_track_name_number()) {
1825                 const int64_t track_number = _route->track_number ();
1826                 if (track_number == 0) {
1827                         number_label.set_text ("-");
1828                 } else {
1829                         number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
1830                 }
1831         } else {
1832                 number_label.set_text ("");
1833         }
1834 }
1835
1836 void
1837 MixerStrip::input_button_resized (Gtk::Allocation& alloc)
1838 {
1839         input_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1840 }
1841
1842 void
1843 MixerStrip::output_button_resized (Gtk::Allocation& alloc)
1844 {
1845         output_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1846 }
1847
1848 void
1849 MixerStrip::name_button_resized (Gtk::Allocation& alloc)
1850 {
1851         name_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1852 }
1853
1854 void
1855 MixerStrip::comment_button_resized (Gtk::Allocation& alloc)
1856 {
1857         _comment_button.set_layout_ellipsize_width (alloc.get_width() * PANGO_SCALE);
1858 }
1859
1860 bool
1861 MixerStrip::width_button_pressed (GdkEventButton* ev)
1862 {
1863         if (ev->button != 1) {
1864                 return false;
1865         }
1866
1867         if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier | Keyboard::TertiaryModifier)) && _mixer_owned) {
1868                 switch (_width) {
1869                 case Wide:
1870                         _mixer.set_strip_width (Narrow, true);
1871                         break;
1872
1873                 case Narrow:
1874                         _mixer.set_strip_width (Wide, true);
1875                         break;
1876                 }
1877         } else {
1878                 switch (_width) {
1879                 case Wide:
1880                         set_width_enum (Narrow, this);
1881                         break;
1882                 case Narrow:
1883                         set_width_enum (Wide, this);
1884                         break;
1885                 }
1886         }
1887
1888         return true;
1889 }
1890
1891 void
1892 MixerStrip::hide_clicked ()
1893 {
1894         // LAME fix to reset the button status for when it is redisplayed (part 1)
1895         hide_button.set_sensitive(false);
1896
1897         if (_embedded) {
1898                 Hiding(); /* EMIT_SIGNAL */
1899         } else {
1900                 _mixer.hide_strip (this);
1901         }
1902
1903         // (part 2)
1904         hide_button.set_sensitive(true);
1905 }
1906
1907 void
1908 MixerStrip::set_embedded (bool yn)
1909 {
1910         _embedded = yn;
1911 }
1912
1913 void
1914 MixerStrip::map_frozen ()
1915 {
1916         ENSURE_GUI_THREAD (*this, &MixerStrip::map_frozen)
1917
1918         boost::shared_ptr<AudioTrack> at = audio_track();
1919
1920         if (at) {
1921                 switch (at->freeze_state()) {
1922                 case AudioTrack::Frozen:
1923                         processor_box.set_sensitive (false);
1924                         hide_redirect_editors ();
1925                         break;
1926                 default:
1927                         processor_box.set_sensitive (true);
1928                         // XXX need some way, maybe, to retoggle redirect editors
1929                         break;
1930                 }
1931         } else {
1932                 processor_box.set_sensitive (true);
1933         }
1934         RouteUI::map_frozen ();
1935 }
1936
1937 void
1938 MixerStrip::hide_redirect_editors ()
1939 {
1940         _route->foreach_processor (sigc::mem_fun (*this, &MixerStrip::hide_processor_editor));
1941 }
1942
1943 void
1944 MixerStrip::hide_processor_editor (boost::weak_ptr<Processor> p)
1945 {
1946         boost::shared_ptr<Processor> processor (p.lock ());
1947         if (!processor) {
1948                 return;
1949         }
1950
1951         Gtk::Window* w = processor_box.get_processor_ui (processor);
1952
1953         if (w) {
1954                 w->hide ();
1955         }
1956 }
1957
1958 void
1959 MixerStrip::reset_strip_style ()
1960 {
1961         if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
1962
1963                 gpm.set_fader_name ("SendStripBase");
1964
1965         } else {
1966
1967                 if (is_midi_track()) {
1968                         if (_route->active()) {
1969                                 set_name ("MidiTrackStripBase");
1970                         } else {
1971                                 set_name ("MidiTrackStripBaseInactive");
1972                         }
1973                         gpm.set_fader_name ("MidiTrackFader");
1974                 } else if (is_audio_track()) {
1975                         if (_route->active()) {
1976                                 set_name ("AudioTrackStripBase");
1977                         } else {
1978                                 set_name ("AudioTrackStripBaseInactive");
1979                         }
1980                         gpm.set_fader_name ("AudioTrackFader");
1981                 } else {
1982                         if (_route->active()) {
1983                                 set_name ("AudioBusStripBase");
1984                         } else {
1985                                 set_name ("AudioBusStripBaseInactive");
1986                         }
1987                         gpm.set_fader_name ("AudioBusFader");
1988
1989                         /* (no MIDI busses yet) */
1990                 }
1991         }
1992 }
1993
1994
1995 void
1996 MixerStrip::engine_stopped ()
1997 {
1998 }
1999
2000 void
2001 MixerStrip::engine_running ()
2002 {
2003 }
2004
2005 string
2006 MixerStrip::meter_point_string (MeterPoint mp)
2007 {
2008         switch (_width) {
2009         case Wide:
2010                 switch (mp) {
2011                 case MeterInput:
2012                         return _("In");
2013                         break;
2014
2015                 case MeterPreFader:
2016                         return _("Pre");
2017                         break;
2018
2019                 case MeterPostFader:
2020                         return _("Post");
2021                         break;
2022
2023                 case MeterOutput:
2024                         return _("Out");
2025                         break;
2026
2027                 case MeterCustom:
2028                 default:
2029                         return _("Custom");
2030                         break;
2031                 }
2032                 break;
2033         case Narrow:
2034                 switch (mp) {
2035                 case MeterInput:
2036                         return S_("Meter|In");
2037                         break;
2038
2039                 case MeterPreFader:
2040                         return S_("Meter|Pr");
2041                         break;
2042
2043                 case MeterPostFader:
2044                         return S_("Meter|Po");
2045                         break;
2046
2047                 case MeterOutput:
2048                         return S_("Meter|O");
2049                         break;
2050
2051                 case MeterCustom:
2052                 default:
2053                         return S_("Meter|C");
2054                         break;
2055                 }
2056                 break;
2057         }
2058
2059         return string();
2060 }
2061
2062 /** Called when the monitor-section state */
2063 void
2064 MixerStrip::monitor_changed ()
2065 {
2066         assert (monitor_section_button);
2067         if (_session->monitor_active()) {
2068                 monitor_section_button->set_name ("master monitor section button active");
2069         } else {
2070                 monitor_section_button->set_name ("master monitor section button normal");
2071         }
2072 }
2073
2074 /** Called when the metering point has changed */
2075 void
2076 MixerStrip::meter_changed ()
2077 {
2078         meter_point_button.set_text (meter_point_string (_route->meter_point()));
2079         gpm.setup_meters ();
2080         // reset peak when meter point changes
2081         gpm.reset_peak_display();
2082 }
2083
2084 /** The bus that we are displaying sends to has changed, or been turned off.
2085  *  @param send_to New bus that we are displaying sends to, or 0.
2086  */
2087 void
2088 MixerStrip::bus_send_display_changed (boost::shared_ptr<Route> send_to)
2089 {
2090         RouteUI::bus_send_display_changed (send_to);
2091
2092         if (send_to) {
2093                 boost::shared_ptr<Send> send = _route->internal_send_for (send_to);
2094
2095                 if (send) {
2096                         show_send (send);
2097                 } else {
2098                         revert_to_default_display ();
2099                 }
2100         } else {
2101                 revert_to_default_display ();
2102         }
2103 }
2104
2105 void
2106 MixerStrip::drop_send ()
2107 {
2108         boost::shared_ptr<Send> current_send;
2109
2110         if (_current_delivery && ((current_send = boost::dynamic_pointer_cast<Send>(_current_delivery)) != 0)) {
2111                 current_send->set_metering (false);
2112         }
2113
2114         send_gone_connection.disconnect ();
2115         input_button.set_sensitive (true);
2116         output_button.set_sensitive (true);
2117         group_button.set_sensitive (true);
2118         set_invert_sensitive (true);
2119         meter_point_button.set_sensitive (true);
2120         mute_button->set_sensitive (true);
2121         solo_button->set_sensitive (true);
2122         solo_isolated_led->set_sensitive (true);
2123         solo_safe_led->set_sensitive (true);
2124         monitor_input_button->set_sensitive (true);
2125         monitor_disk_button->set_sensitive (true);
2126         _comment_button.set_sensitive (true);
2127         RouteUI::check_rec_enable_sensitivity ();
2128         set_button_names (); // update solo button visual state
2129 }
2130
2131 void
2132 MixerStrip::set_current_delivery (boost::shared_ptr<Delivery> d)
2133 {
2134         _current_delivery = d;
2135         DeliveryChanged (_current_delivery);
2136 }
2137
2138 void
2139 MixerStrip::show_send (boost::shared_ptr<Send> send)
2140 {
2141         assert (send != 0);
2142
2143         drop_send ();
2144
2145         set_current_delivery (send);
2146
2147         send->meter()->set_type(_route->shared_peak_meter()->get_type());
2148         send->set_metering (true);
2149         _current_delivery->DropReferences.connect (send_gone_connection, invalidator (*this), boost::bind (&MixerStrip::revert_to_default_display, this), gui_context());
2150
2151         gain_meter().set_controls (_route, send->meter(), send->amp(), send->gain_control());
2152         gain_meter().setup_meters ();
2153
2154         uint32_t const in = _current_delivery->pans_required();
2155         uint32_t const out = _current_delivery->pan_outs();
2156
2157         panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
2158         panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
2159         panner_ui().setup_pan ();
2160         panner_ui().set_send_drawing_mode (true);
2161         panner_ui().show_all ();
2162
2163         input_button.set_sensitive (false);
2164         group_button.set_sensitive (false);
2165         set_invert_sensitive (false);
2166         meter_point_button.set_sensitive (false);
2167         mute_button->set_sensitive (false);
2168         solo_button->set_sensitive (false);
2169         rec_enable_button->set_sensitive (false);
2170         solo_isolated_led->set_sensitive (false);
2171         solo_safe_led->set_sensitive (false);
2172         monitor_input_button->set_sensitive (false);
2173         monitor_disk_button->set_sensitive (false);
2174         _comment_button.set_sensitive (false);
2175
2176         if (boost::dynamic_pointer_cast<InternalSend>(send)) {
2177                 output_button.set_sensitive (false);
2178         }
2179
2180         reset_strip_style ();
2181 }
2182
2183 void
2184 MixerStrip::revert_to_default_display ()
2185 {
2186         drop_send ();
2187
2188         set_current_delivery (_route->main_outs ());
2189
2190         gain_meter().set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
2191         gain_meter().setup_meters ();
2192
2193         panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
2194         update_panner_choices();
2195         panner_ui().setup_pan ();
2196         panner_ui().set_send_drawing_mode (false);
2197
2198         if (has_audio_outputs ()) {
2199                 panners.show_all ();
2200         } else {
2201                 panners.hide_all ();
2202         }
2203
2204         reset_strip_style ();
2205 }
2206
2207 void
2208 MixerStrip::set_button_names ()
2209 {
2210         switch (_width) {
2211         case Wide:
2212                 mute_button->set_text (_("Mute"));
2213                 monitor_input_button->set_text (_("In"));
2214                 monitor_disk_button->set_text (_("Disk"));
2215                 if (monitor_section_button) {
2216                         monitor_section_button->set_text (_("Mon"));
2217                 }
2218
2219                 if (_route && _route->solo_safe_control()->solo_safe()) {
2220                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2221                 } else {
2222                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2223                 }
2224                 if (!Config->get_solo_control_is_listen_control()) {
2225                         solo_button->set_text (_("Solo"));
2226                 } else {
2227                         switch (Config->get_listen_position()) {
2228                         case AfterFaderListen:
2229                                 solo_button->set_text (_("AFL"));
2230                                 break;
2231                         case PreFaderListen:
2232                                 solo_button->set_text (_("PFL"));
2233                                 break;
2234                         }
2235                 }
2236                 solo_isolated_led->set_text (_("Iso"));
2237                 solo_safe_led->set_text (S_("SoloLock|Lock"));
2238                 break;
2239
2240         default:
2241                 mute_button->set_text (S_("Mute|M"));
2242                 monitor_input_button->set_text (S_("MonitorInput|I"));
2243                 monitor_disk_button->set_text (S_("MonitorDisk|D"));
2244                 if (monitor_section_button) {
2245                         monitor_section_button->set_text (S_("Mon|O"));
2246                 }
2247
2248                 if (_route && _route->solo_safe_control()->solo_safe()) {
2249                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2250                 } else {
2251                         solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2252                 }
2253                 if (!Config->get_solo_control_is_listen_control()) {
2254                         solo_button->set_text (S_("Solo|S"));
2255                 } else {
2256                         switch (Config->get_listen_position()) {
2257                         case AfterFaderListen:
2258                                 solo_button->set_text (S_("AfterFader|A"));
2259                                 break;
2260                         case PreFaderListen:
2261                                 solo_button->set_text (S_("Prefader|P"));
2262                                 break;
2263                         }
2264                 }
2265
2266                 solo_isolated_led->set_text (S_("SoloIso|I"));
2267                 solo_safe_led->set_text (S_("SoloLock|L"));
2268                 break;
2269         }
2270
2271         if (_route) {
2272                 meter_point_button.set_text (meter_point_string (_route->meter_point()));
2273         } else {
2274                 meter_point_button.set_text ("");
2275         }
2276 }
2277
2278 PluginSelector*
2279 MixerStrip::plugin_selector()
2280 {
2281         return _mixer.plugin_selector();
2282 }
2283
2284 void
2285 MixerStrip::hide_things ()
2286 {
2287         processor_box.hide_things ();
2288 }
2289
2290 bool
2291 MixerStrip::input_active_button_press (GdkEventButton*)
2292 {
2293         /* nothing happens on press */
2294         return true;
2295 }
2296
2297 bool
2298 MixerStrip::input_active_button_release (GdkEventButton* ev)
2299 {
2300         boost::shared_ptr<MidiTrack> mt = midi_track ();
2301
2302         if (!mt) {
2303                 return true;
2304         }
2305
2306         boost::shared_ptr<RouteList> rl (new RouteList);
2307
2308         rl->push_back (route());
2309
2310         _session->set_exclusive_input_active (rl, !mt->input_active(),
2311                                               Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)));
2312
2313         return true;
2314 }
2315
2316 void
2317 MixerStrip::midi_input_status_changed ()
2318 {
2319         if (midi_input_enable_button) {
2320                 boost::shared_ptr<MidiTrack> mt = midi_track ();
2321                 assert (mt);
2322                 midi_input_enable_button->set_active (mt->input_active ());
2323         }
2324 }
2325
2326 string
2327 MixerStrip::state_id () const
2328 {
2329         return string_compose ("strip %1", _route->id().to_s());
2330 }
2331
2332 void
2333 MixerStrip::parameter_changed (string p)
2334 {
2335         if (p == _visibility.get_state_name()) {
2336                 /* The user has made changes to the mixer strip visibility, so get
2337                    our VisibilityGroup to reflect these changes in our widgets.
2338                 */
2339                 _visibility.set_state (UIConfiguration::instance().get_mixer_strip_visibility ());
2340         } else if (p == "track-name-number") {
2341                 name_changed ();
2342         } else if (p == "use-monitor-bus") {
2343                 if (monitor_section_button) {
2344                         if (mute_button->get_parent()) {
2345                                 mute_button->get_parent()->remove(*mute_button);
2346                         }
2347                         if (monitor_section_button->get_parent()) {
2348                                 monitor_section_button->get_parent()->remove(*monitor_section_button);
2349                         }
2350                         if (Config->get_use_monitor_bus ()) {
2351                                 mute_solo_table.attach (*mute_button, 0, 1, 0, 1);
2352                                 mute_solo_table.attach (*monitor_section_button, 1, 2, 0, 1);
2353                                 mute_button->show();
2354                                 monitor_section_button->show();
2355                         } else {
2356                                 mute_solo_table.attach (*mute_button, 0, 2, 0, 1);
2357                                 mute_button->show();
2358                         }
2359                 }
2360         } else if (p == "track-name-number") {
2361                 update_track_number_visibility();
2362         }
2363 }
2364
2365 /** Called to decide whether the solo isolate / solo lock button visibility should
2366  *  be overridden from that configured by the user.  We do this for the master bus.
2367  *
2368  *  @return optional value that is present if visibility state should be overridden.
2369  */
2370 boost::optional<bool>
2371 MixerStrip::override_solo_visibility () const
2372 {
2373         if (_route && _route->is_master ()) {
2374                 return boost::optional<bool> (false);
2375         }
2376
2377         return boost::optional<bool> ();
2378 }
2379
2380 void
2381 MixerStrip::add_input_port (DataType t)
2382 {
2383         _route->input()->add_port ("", this, t);
2384 }
2385
2386 void
2387 MixerStrip::add_output_port (DataType t)
2388 {
2389         _route->output()->add_port ("", this, t);
2390 }
2391
2392 void
2393 MixerStrip::route_active_changed ()
2394 {
2395         reset_strip_style ();
2396 }
2397
2398 void
2399 MixerStrip::copy_processors ()
2400 {
2401         processor_box.processor_operation (ProcessorBox::ProcessorsCopy);
2402 }
2403
2404 void
2405 MixerStrip::cut_processors ()
2406 {
2407         processor_box.processor_operation (ProcessorBox::ProcessorsCut);
2408 }
2409
2410 void
2411 MixerStrip::paste_processors ()
2412 {
2413         processor_box.processor_operation (ProcessorBox::ProcessorsPaste);
2414 }
2415
2416 void
2417 MixerStrip::select_all_processors ()
2418 {
2419         processor_box.processor_operation (ProcessorBox::ProcessorsSelectAll);
2420 }
2421
2422 void
2423 MixerStrip::deselect_all_processors ()
2424 {
2425         processor_box.processor_operation (ProcessorBox::ProcessorsSelectNone);
2426 }
2427
2428 bool
2429 MixerStrip::delete_processors ()
2430 {
2431         return processor_box.processor_operation (ProcessorBox::ProcessorsDelete);
2432 }
2433
2434 void
2435 MixerStrip::toggle_processors ()
2436 {
2437         processor_box.processor_operation (ProcessorBox::ProcessorsToggleActive);
2438 }
2439
2440 void
2441 MixerStrip::ab_plugins ()
2442 {
2443         processor_box.processor_operation (ProcessorBox::ProcessorsAB);
2444 }
2445
2446 bool
2447 MixerStrip::level_meter_button_press (GdkEventButton* ev)
2448 {
2449         if (_current_delivery && boost::dynamic_pointer_cast<Send>(_current_delivery)) {
2450                 return false;
2451         }
2452         if (ev->button == 3) {
2453                 popup_level_meter_menu (ev);
2454                 return true;
2455         }
2456
2457         return false;
2458 }
2459
2460 void
2461 MixerStrip::popup_level_meter_menu (GdkEventButton* ev)
2462 {
2463         using namespace Gtk::Menu_Helpers;
2464
2465         Gtk::Menu* m = manage (new Menu);
2466         MenuList& items = m->items ();
2467
2468         RadioMenuItem::Group group;
2469
2470         _suspend_menu_callbacks = true;
2471         add_level_meter_item_point (items, group, _("Input"), MeterInput);
2472         add_level_meter_item_point (items, group, _("Pre Fader"), MeterPreFader);
2473         add_level_meter_item_point (items, group, _("Post Fader"), MeterPostFader);
2474         add_level_meter_item_point (items, group, _("Output"), MeterOutput);
2475         add_level_meter_item_point (items, group, _("Custom"), MeterCustom);
2476
2477         if (gpm.meter_channels().n_audio() == 0) {
2478                 m->popup (ev->button, ev->time);
2479                 _suspend_menu_callbacks = false;
2480                 return;
2481         }
2482
2483         RadioMenuItem::Group tgroup;
2484         items.push_back (SeparatorElem());
2485
2486         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak), MeterPeak);
2487         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterPeak0dB), MeterPeak0dB);
2488         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterKrms),  MeterKrms);
2489         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1DIN), MeterIEC1DIN);
2490         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC1NOR), MeterIEC1NOR);
2491         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2BBC), MeterIEC2BBC);
2492         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterIEC2EBU), MeterIEC2EBU);
2493         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK20), MeterK20);
2494         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK14), MeterK14);
2495         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterK12), MeterK12);
2496         add_level_meter_item_type (items, tgroup, ArdourMeter::meter_type_string(MeterVU),  MeterVU);
2497
2498         int _strip_type;
2499         if (_route->is_master()) {
2500                 _strip_type = 4;
2501         }
2502         else if (boost::dynamic_pointer_cast<AudioTrack>(_route) == 0
2503                         && boost::dynamic_pointer_cast<MidiTrack>(_route) == 0) {
2504                 /* non-master bus */
2505                 _strip_type = 3;
2506         }
2507         else if (boost::dynamic_pointer_cast<MidiTrack>(_route)) {
2508                 _strip_type = 2;
2509         }
2510         else {
2511                 _strip_type = 1;
2512         }
2513
2514         MeterType cmt = _route->meter_type();
2515         const std::string cmn = ArdourMeter::meter_type_string(cmt);
2516
2517         items.push_back (SeparatorElem());
2518         items.push_back (MenuElem (string_compose(_("Change all in Group to %1"), cmn),
2519                                 sigc::bind (SetMeterTypeMulti, -1, _route->route_group(), cmt)));
2520         items.push_back (MenuElem (string_compose(_("Change all to %1"), cmn),
2521                                 sigc::bind (SetMeterTypeMulti, 0, _route->route_group(), cmt)));
2522         items.push_back (MenuElem (string_compose(_("Change same track-type to %1"), cmn),
2523                                 sigc::bind (SetMeterTypeMulti, _strip_type, _route->route_group(), cmt)));
2524
2525         m->popup (ev->button, ev->time);
2526         _suspend_menu_callbacks = false;
2527 }
2528
2529 void
2530 MixerStrip::add_level_meter_item_point (Menu_Helpers::MenuList& items,
2531                 RadioMenuItem::Group& group, string const & name, MeterPoint point)
2532 {
2533         using namespace Menu_Helpers;
2534
2535         items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_point), point)));
2536         RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2537         i->set_active (_route->meter_point() == point);
2538 }
2539
2540 void
2541 MixerStrip::set_meter_point (MeterPoint p)
2542 {
2543         if (_suspend_menu_callbacks) return;
2544         _route->set_meter_point (p);
2545 }
2546
2547 void
2548 MixerStrip::add_level_meter_item_type (Menu_Helpers::MenuList& items,
2549                 RadioMenuItem::Group& group, string const & name, MeterType type)
2550 {
2551         using namespace Menu_Helpers;
2552
2553         items.push_back (RadioMenuElem (group, name, sigc::bind (sigc::mem_fun (*this, &MixerStrip::set_meter_type), type)));
2554         RadioMenuItem* i = dynamic_cast<RadioMenuItem *> (&items.back ());
2555         i->set_active (_route->meter_type() == type);
2556 }
2557
2558 void
2559 MixerStrip::set_meter_type (MeterType t)
2560 {
2561         if (_suspend_menu_callbacks) return;
2562         gpm.set_type (t);
2563 }
2564
2565 void
2566 MixerStrip::update_track_number_visibility ()
2567 {
2568         DisplaySuspender ds;
2569         bool show_label = _session->config.get_track_name_number();
2570
2571         if (_route && _route->is_master()) {
2572                 show_label = false;
2573         }
2574
2575         if (show_label) {
2576                 number_label.show ();
2577                 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
2578                 // except the width of the number label is subtracted from the name-hbox, so we
2579                 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
2580                 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
2581                 if (tnw & 1) --tnw;
2582                 number_label.set_size_request(tnw, -1);
2583                 number_label.show ();
2584         } else {
2585                 number_label.hide ();
2586         }
2587 }
2588
2589 Gdk::Color
2590 MixerStrip::color () const
2591 {
2592         return route_color ();
2593 }
2594
2595 bool
2596 MixerStrip::marked_for_display () const
2597 {
2598         return !_route->presentation_info().hidden();
2599 }
2600
2601 bool
2602 MixerStrip::set_marked_for_display (bool yn)
2603 {
2604         return RouteUI::mark_hidden (!yn);
2605 }