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