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