Populate Sidechain connection menu with non-feeback sources.
[ardour.git] / gtk2_ardour / plugin_pin_dialog.cc
1 /*
2  * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3  * Copyright (C) 2011 Paul Davis
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19
20 #include <gtkmm/table.h>
21 #include <gtkmm/box.h>
22 #include <gtkmm/label.h>
23
24 #include "pbd/replace_all.h"
25
26 #include "gtkmm2ext/utils.h"
27 #include "gtkmm2ext/rgb_macros.h"
28
29 #include "ardour/audioengine.h"
30 #include "ardour/plugin.h"
31 #include "ardour/port.h"
32 #include "ardour/session.h"
33
34 #include "plugin_pin_dialog.h"
35 #include "gui_thread.h"
36 #include "tooltips.h"
37 #include "ui_config.h"
38
39 #include "i18n.h"
40
41 using namespace ARDOUR;
42 using namespace PBD;
43 using namespace std;
44 using namespace Gtk;
45 using namespace Gtkmm2ext;
46
47 PluginPinDialog::PluginPinDialog (boost::shared_ptr<ARDOUR::PluginInsert> pi)
48         : ArdourWindow (string_compose (_("Pin Configuration: %1"), pi->name ()))
49         , _rst_config (_("Reset"))
50         , _rst_mapping (_("Reset"))
51         , _tgl_sidechain (_("Side Chain"))
52         , _add_plugin (_("+"))
53         , _del_plugin (_("-"))
54         , _add_output_audio (_("+"))
55         , _del_output_audio (_("-"))
56         , _add_output_midi (_("+"))
57         , _del_output_midi (_("-"))
58         , _add_sc_audio (_("A+"))
59         , _add_sc_midi (_("M+"))
60         , _pi (pi)
61         , _pin_box_size (10)
62         , _width (0)
63         , _height (0)
64         , _innerwidth (0)
65         , _margin_x (28)
66         , _margin_y (40)
67         , _min_width (300)
68         , _min_height (200)
69         , _n_inputs (0)
70         , _n_sidechains (0)
71         , _position_valid (false)
72         , _ignore_updates (false)
73         , _sidechain_selector (0)
74 {
75         assert (pi->owner ()); // Route
76
77         _pi->PluginIoReConfigure.connect (
78                         _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
79                         );
80
81         _pi->PluginMapChanged.connect (
82                         _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
83                         );
84
85         _pi->PluginConfigChanged.connect (
86                         _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context ()
87                         );
88
89         _pin_box_size = 2 * ceil (max (8., 10. * UIConfiguration::instance ().get_ui_scale ()) * .5);
90         _margin_x = 2 * ceil (max (24., 28. * UIConfiguration::instance ().get_ui_scale ()) * .5);
91         _margin_y = 2 * ceil (max (36., 40. * UIConfiguration::instance ().get_ui_scale ()) * .5);
92
93         _tgl_sidechain.set_name ("pinrouting sidechain");
94
95         _pm_size_group  = SizeGroup::create (SIZE_GROUP_BOTH);
96         _add_plugin.set_tweaks (ArdourButton::Square);
97         _del_plugin.set_tweaks (ArdourButton::Square);
98         _pm_size_group->add_widget (_add_plugin);
99         _pm_size_group->add_widget (_del_plugin);
100         _pm_size_group->add_widget (_add_output_audio);
101         _pm_size_group->add_widget (_del_output_audio);
102         _pm_size_group->add_widget (_add_output_midi);
103         _pm_size_group->add_widget (_del_output_midi);
104
105         _sc_size_group  = SizeGroup::create (SIZE_GROUP_BOTH);
106         _sc_size_group->add_widget (_add_sc_audio);
107         _sc_size_group->add_widget (_add_sc_midi);
108
109         Label* l;
110         Gtk::Separator *sep;
111         int r = 0;
112         Table* tl = manage (new Table (9, 2));
113         tl->set_border_width (0);
114         tl->set_spacings (2);
115
116         Table* tr = manage (new Table (4, 3));
117         tr->set_border_width (0);
118         tr->set_spacings (2);
119
120         /* left side table */
121         l = manage (new Label (_("<b>Config</b>"), ALIGN_CENTER));
122         l->set_use_markup ();
123         tl->attach (*l, 0, 2, r, r + 1, FILL, SHRINK);
124         ++r;
125         tl->attach (_rst_config, 0, 2, r, r + 1, FILL, SHRINK);
126         ++r;
127
128         sep = manage (new HSeparator ());
129         tl->attach (*sep, 0, 2, r, r + 1, FILL|EXPAND, FILL|EXPAND, 0, 4);
130         ++r;
131
132         l = manage (new Label (_("Instances"), ALIGN_CENTER));
133         tl->attach (*l, 0, 2, r, r + 1, FILL, SHRINK);
134         ++r;
135         tl->attach (_add_plugin, 0, 1, r, r + 1, SHRINK, SHRINK);
136         tl->attach (_del_plugin, 1, 2, r, r + 1, SHRINK, SHRINK);
137         ++r;
138
139         l = manage (new Label (_("Audio Out"), ALIGN_CENTER));
140         tl->attach (*l, 0, 2, r, r + 1, FILL, SHRINK);
141         ++r;
142         tl->attach (_add_output_audio, 0, 1, r, r + 1, SHRINK, SHRINK);
143         tl->attach (_del_output_audio, 1, 2, r, r + 1, SHRINK, SHRINK);
144         ++r;
145
146         l = manage (new Label (_("Midi Out"), ALIGN_CENTER));
147         tl->attach (*l, 0, 2, r, r + 1, FILL, SHRINK);
148         ++r;
149         tl->attach (_add_output_midi, 0, 1, r, r + 1, SHRINK, SHRINK);
150         tl->attach (_del_output_midi, 1, 2, r, r + 1, SHRINK, SHRINK);
151         ++r;
152
153         /* right side table */
154         r = 0;
155         l = manage (new Label (_("<b>Connections</b>"), ALIGN_CENTER));
156         l->set_use_markup ();
157         tr->attach (*l, 0, 2, r, r + 1, FILL, SHRINK);
158         ++r;
159         tr->attach (_rst_mapping, 0, 2, r, r + 1, FILL, SHRINK);
160         ++r;
161
162         sep = manage (new HSeparator ());
163         tr->attach (*sep, 0, 2, r, r + 1, FILL|EXPAND, SHRINK, 0, 4);
164         ++r;
165
166         tr->attach (_tgl_sidechain, 0, 2, r, r + 1, FILL, SHRINK);
167         ++r;
168
169         _sidechain_tbl = manage (new Gtk::Table ());
170         _sidechain_tbl->set_spacings (2);
171         tr->attach (*_sidechain_tbl, 0, 2, r, r + 1, EXPAND|FILL, EXPAND|FILL, 0, 2);
172         ++r;
173
174         tr->attach (_add_sc_audio, 0, 1, r, r + 1, FILL, SHRINK);
175         tr->attach (_add_sc_midi, 1, 2, r, r + 1, FILL, SHRINK);
176         ++r;
177
178         HBox* hbox = manage (new HBox);
179         hbox->set_spacing (4);
180         hbox->pack_start (*tl, false, false);
181         hbox->pack_start (darea, true, true);
182         hbox->pack_start (*tr, false, false);
183
184         VBox* vbox = manage (new VBox);
185         vbox->pack_start (*hbox, true, true);
186         set_border_width (4);
187         add (*vbox);
188         vbox->show_all ();
189
190         plugin_reconfigured ();
191
192         darea.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
193         darea.signal_size_request ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_request));
194         darea.signal_size_allocate ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_allocate));
195         darea.signal_expose_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_expose_event));
196         darea.signal_button_press_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_press_event));
197         darea.signal_button_release_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_release_event));
198         darea.signal_motion_notify_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_motion_notify_event));
199
200         _tgl_sidechain.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::toggle_sidechain));
201
202         _rst_mapping.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::reset_mapping));
203         _rst_config.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::reset_configuration));
204         _add_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_plugin_clicked), true));
205         _del_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_plugin_clicked), false));
206
207         _add_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::AUDIO));
208         _del_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::AUDIO));
209         _add_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::MIDI));
210         _del_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::MIDI));
211         _add_sc_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_sidechain_port), DataType::AUDIO));
212         _add_sc_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_sidechain_port), DataType::MIDI));
213 }
214
215 PluginPinDialog::~PluginPinDialog ()
216 {
217         delete _sidechain_selector;
218 }
219
220 void
221 PluginPinDialog::plugin_reconfigured ()
222 {
223         ENSURE_GUI_THREAD (*this, &PluginPinDialog::plugin_reconfigured);
224         if (_ignore_updates) {
225                 return;
226         }
227         _n_plugins = _pi->get_count ();
228         _pi->configured_io (_in, _out);
229         _ins = _pi->internal_streams (); // with sidechain
230         _sinks = _pi->natural_input_streams ();
231         _sources = _pi->natural_output_streams ();
232
233         _del_plugin.set_sensitive (_n_plugins > 1);
234         _del_output_audio.set_sensitive (_out.n_audio () > 0 && _out.n_total () > 1);
235         _del_output_midi.set_sensitive (_out.n_midi () > 0 && _out.n_total () > 1);
236         _tgl_sidechain.set_active (_pi->has_sidechain ());
237         _add_sc_audio.set_sensitive (_pi->has_sidechain ());
238         _add_sc_midi.set_sensitive (_pi->has_sidechain ());
239
240         if (_pi->custom_cfg ()) {
241                 _rst_config.set_name ("pinrouting custom");
242         } else {
243                 _rst_config.set_name ("generic button");
244         }
245
246         if (!_pi->has_sidechain () && _sidechain_selector) {
247                 delete _sidechain_selector;
248                 _sidechain_selector = 0;
249         }
250
251         _io_connection.disconnect ();
252         refill_sidechain_table ();
253
254         /* update elements */
255
256         _elements.clear ();
257         _hover.reset ();
258         _actor.reset ();
259         _selection.reset ();
260
261         _n_inputs = _n_sidechains = 0;
262
263         for (uint32_t i = 0; i < _ins.n_total (); ++i) {
264                 DataType dt = i < _ins.n_midi () ? DataType::MIDI : DataType::AUDIO;
265                 uint32_t id = dt == DataType::MIDI ? i : i - _ins.n_midi ();
266                 bool sidechain = id >= _in.get (dt) ? true : false;
267                 if (sidechain) {
268                         ++_n_sidechains;
269                 } else {
270                         ++_n_inputs;
271                 }
272
273                 CtrlWidget cw (CtrlWidget (Input, dt, id, 0, sidechain));
274                 _elements.push_back (cw);
275         }
276
277         for (uint32_t i = 0; i < _out.n_total (); ++i) {
278                 int id = (i < _out.n_midi ()) ? i : i - _out.n_midi ();
279                 _elements.push_back (CtrlWidget (Output, (i < _out.n_midi () ? DataType::MIDI : DataType::AUDIO), id));
280         }
281
282         for (uint32_t n = 0; n < _n_plugins; ++n) {
283                 boost::shared_ptr<Plugin> plugin = _pi->plugin (n);
284                 for (uint32_t i = 0; i < _sinks.n_total (); ++i) {
285                         DataType dt (_sinks.n_midi () ? DataType::MIDI : DataType::AUDIO);
286                         int idx = (i < _sinks.n_midi ()) ? i : i - _sinks.n_midi ();
287                         const Plugin::IOPortDescription& iod (plugin->describe_io_port (dt, true, idx));
288                         CtrlWidget cw (CtrlWidget (Sink, dt, i, n, iod.is_sidechain));
289                         _elements.push_back (cw);
290                 }
291                 for (uint32_t i = 0; i < _sources.n_total (); ++i) {
292                         _elements.push_back (CtrlWidget (Source, (i < _sources.n_midi () ? DataType::MIDI : DataType::AUDIO), i, n));
293                 }
294         }
295
296         /* calc minimum size */
297         const uint32_t max_ports = std::max (_ins.n_total (), _out.n_total ());
298         const uint32_t max_pins = std::max ((_sinks * _n_plugins).n_total (), (_sources * _n_plugins).n_total ());
299         uint32_t min_width = std::max (25 * max_ports, (uint32_t)(20 + _pin_box_size) * max_pins);
300         min_width = std::max (min_width, (uint32_t)ceilf (_margin_y * .45 * _n_plugins * 16. / 9.)); // 16 : 9 aspect
301         min_width = std::max ((uint32_t)300, min_width);
302
303         min_width = 50 + 10 * ceilf (min_width / 10.f);
304
305         uint32_t min_height = 3.5 * _margin_y + 2 * (_n_sidechains + 1) * _pin_box_size;
306         min_height = std::max ((uint32_t)200, min_height);
307         min_height = 4 * ceilf (min_height / 4.f);
308
309         if (min_width != _min_width || min_height != _min_height) {
310                 _min_width = min_width;
311                 _min_height = min_height;
312                 darea.queue_resize ();
313         }
314
315         _position_valid = false;
316         darea.queue_draw ();
317 }
318
319 void
320 PluginPinDialog::refill_sidechain_table ()
321 {
322         Table_Helpers::TableList& kids = _sidechain_tbl->children ();
323         for (Table_Helpers::TableList::iterator i = kids.begin (); i != kids.end ();) {
324                 i = kids.erase (i);
325         }
326         _sidechain_tbl->resize (1, 1);
327         if (!_pi->has_sidechain () && _sidechain_selector) {
328                 return;
329         }
330         boost::shared_ptr<IO> io = _pi->sidechain_input ();
331         if (!io) {
332                 return;
333         }
334
335         uint32_t r = 0;
336         PortSet& p (io->ports ());
337         bool can_remove = p.num_ports () > 1;
338         for (PortSet::iterator i = p.begin (DataType::MIDI); i != p.end (DataType::MIDI); ++i, ++r) {
339                 add_port_to_table (*i, r, can_remove);
340         }
341         for (PortSet::iterator i = p.begin (DataType::AUDIO); i != p.end (DataType::AUDIO); ++i, ++r) {
342                 add_port_to_table (*i, r, can_remove);
343         }
344         _sidechain_tbl->show_all ();
345
346         io->changed.connect (
347                         _io_connection, invalidator (*this), boost::bind (&PluginPinDialog::io_changed_proxy, this), gui_context ()
348                         );
349 }
350
351 void
352 PluginPinDialog::add_port_to_table (boost::shared_ptr<Port> p, uint32_t r, bool can_remove)
353 {
354         std::string lbl;
355         std::string tip = p->name ();
356         std::vector<std::string> cns;
357         p->get_connections (cns);
358
359         // TODO proper labels, see MixerStrip::update_io_button()
360         if (cns.size () == 0) {
361                 lbl = "-";
362         } else if (cns.size () > 1) {
363                 lbl = "...";
364                 tip += " &lt;- ";
365         } else {
366                 lbl = cns[0];
367                 tip += " &lt;- ";
368                 if (lbl.find ("system:") == 0) {
369                         lbl = AudioEngine::instance ()->get_pretty_name_by_name (lbl);
370                         if (lbl.empty ()) {
371                                 lbl = cns[0].substr (7);
372                         }
373                 }
374         }
375         for (std::vector<std::string>::const_iterator i = cns.begin(); i != cns.end(); ++i) {
376                 tip += *i;
377                 tip += " ";
378         }
379
380         ArdourButton *pb = manage (new ArdourButton (lbl));
381         pb->set_text_ellipsize (Pango::ELLIPSIZE_MIDDLE);
382         pb->set_layout_ellipsize_width (56 * PANGO_SCALE);
383         ARDOUR_UI_UTILS::set_tooltip (*pb, tip);
384         _sidechain_tbl->attach (*pb, 0, 1, r, r +1 , EXPAND|FILL, SHRINK);
385
386         pb->signal_button_press_event ().connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::sc_input_press), boost::weak_ptr<Port> (p)), false);
387         pb->signal_button_release_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::sc_input_release), false);
388
389         pb = manage (new ArdourButton ("-"));
390         _sidechain_tbl->attach (*pb, 1, 2, r, r +1 , FILL, SHRINK);
391         if (can_remove) {
392                 pb->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::remove_port), boost::weak_ptr<Port> (p)));
393         } else {
394                 pb->set_sensitive (false);
395         }
396 }
397
398 void
399 PluginPinDialog::update_element_pos ()
400 {
401         /* layout sizes */
402         _innerwidth = _width - 2. * _margin_x;
403
404         const double yc   = rint (_height * .5);
405         const double bxh2 = rint (_margin_y * .45); // TODO grow?
406         const double bxw  = rint ((_innerwidth * .95) / ((_n_plugins) + .2 * (_n_plugins - 1)));
407         const double bxw2 = rint (bxw * .5);
408         const double y_in = _margin_y;
409         const double y_out = _height - _margin_y;
410
411         _bxw2 = bxw2;
412         _bxh2 = bxh2;
413
414         const double dx = _pin_box_size * .5;
415
416         uint32_t sc_cnt = 0;
417         for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
418                 switch (i->e->ct) {
419                         case Input:
420                                 if (i->e->sc) {
421                                         i->x = _innerwidth + _margin_x - dx;
422                                         i->y = y_in + (sc_cnt + .5) * _pin_box_size;
423                                         i->h = _pin_box_size;
424                                         i->w = 1.5 * _pin_box_size;
425                                         ++ sc_cnt;
426                                 } else {
427                                         uint32_t idx = i->e->id;
428                                         if (i->e->dt == DataType::AUDIO) { idx += _in.n_midi (); }
429                                         i->x = rint ((idx + 1) * _width / (1. + _n_inputs)) - 0.5 - dx;
430                                         i->w = _pin_box_size;
431                                         i->h = 1.5 * _pin_box_size;
432                                         i->y = y_in - i->h;
433                                 }
434                                 break;
435                         case Output:
436                                 {
437                                         uint32_t idx = i->e->id;
438                                         if (i->e->dt == DataType::AUDIO) { idx += _out.n_midi (); }
439                                         i->x = rint ((idx + 1) * _width / (1. + _out.n_total ())) - 0.5 - dx;
440                                         i->y = y_out;
441                                         i->w = _pin_box_size;
442                                         i->h = 1.5 * _pin_box_size;
443                                 }
444                                 break;
445                         case Sink:
446                                 {
447                                         const double x0 = rint ((i->e->ip + .5) * _innerwidth / (double)(_n_plugins)) - .5 - bxw2;
448                                         i->x = _margin_x + rint (x0 + (i->e->id + 1) * bxw / (1. + _sinks.n_total ())) - .5 - dx;
449                                         i->y = yc - bxh2 - dx;
450                                         i->w = _pin_box_size;
451                                         i->h = _pin_box_size;
452                                 }
453                                 break;
454                         case Source:
455                                 {
456                                         const double x0 = rint ((i->e->ip + .5) * _innerwidth / (double)(_n_plugins)) - .5 - bxw2;
457                                         i->x = _margin_x + rint (x0 + (i->e->id + 1) * bxw / (1. + _sources.n_total ())) - .5 - dx;
458                                         i->y = yc + bxh2 - dx;
459                                         i->w = _pin_box_size;
460                                         i->h = _pin_box_size;
461                                 }
462                                 break;
463                 }
464         }
465 }
466
467 void
468 PluginPinDialog::set_color (cairo_t* cr, bool midi)
469 {
470         // see also gtk2_ardour/processor_box.cc
471         static const uint32_t audio_port_color = 0x4A8A0EFF; // Green
472         static const uint32_t midi_port_color = 0x960909FF; //Red
473
474         if (midi) {
475                 cairo_set_source_rgb (cr,
476                                 UINT_RGBA_R_FLT (midi_port_color),
477                                 UINT_RGBA_G_FLT (midi_port_color),
478                                 UINT_RGBA_B_FLT (midi_port_color));
479         } else {
480                 cairo_set_source_rgb (cr,
481                                 UINT_RGBA_R_FLT (audio_port_color),
482                                 UINT_RGBA_G_FLT (audio_port_color),
483                                 UINT_RGBA_B_FLT (audio_port_color));
484         }
485 }
486
487 void
488 PluginPinDialog::draw_io_pin (cairo_t* cr, const CtrlWidget& w)
489 {
490
491         if (w.e->sc) {
492                 const double dy = w.h * .5;
493                 const double dx = w.w - dy;
494                 cairo_move_to (cr, w.x, w.y + dy);
495                 cairo_rel_line_to (cr,  dy, -dy);
496                 cairo_rel_line_to (cr,  dx,  0);
497                 cairo_rel_line_to (cr,   0,  w.h);
498                 cairo_rel_line_to (cr, -dx,  0);
499         } else {
500                 const double dir = (w.e->ct == Input) ? 1 : -1;
501                 const double dx = w.w * .5;
502                 const double dy = w.h - dx;
503
504                 cairo_move_to (cr, w.x + dx, w.y + ((w.e->ct == Input) ? w.h : 0));
505                 cairo_rel_line_to (cr,     -dx, -dx * dir);
506                 cairo_rel_line_to (cr,      0., -dy * dir);
507                 cairo_rel_line_to (cr, 2. * dx,        0.);
508                 cairo_rel_line_to (cr,      0.,  dy * dir);
509         }
510         cairo_close_path  (cr);
511
512         cairo_set_line_width (cr, 1.0);
513         cairo_set_source_rgb (cr, 0, 0, 0);
514         cairo_stroke_preserve (cr);
515
516         set_color (cr, w.e->dt == DataType::MIDI);
517
518         if (w.e->sc) {
519                 assert (w.e->ct == Input);
520                 cairo_fill_preserve (cr);
521                 cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.4);
522         }
523
524         if (w.e == _selection || w.e == _actor) {
525                 cairo_fill_preserve (cr);
526                 cairo_set_source_rgba (cr, 0.9, 0.9, 1.0, 0.6);
527         } else if (w.prelight) {
528                 cairo_fill_preserve (cr);
529                 cairo_set_source_rgba (cr, 0.9, 0.9, 0.9, 0.3);
530         }
531         cairo_fill (cr);
532 }
533
534 void
535 PluginPinDialog::draw_plugin_pin (cairo_t* cr, const CtrlWidget& w)
536 {
537         const double dx = w.w * .5;
538         const double dy = w.h * .5;
539
540         cairo_move_to (cr, w.x + dx, w.y);
541         cairo_rel_line_to (cr, -dx,  dy);
542         cairo_rel_line_to (cr,  dx,  dy);
543         cairo_rel_line_to (cr,  dx, -dy);
544         cairo_close_path  (cr);
545
546         cairo_set_line_width (cr, 1.0);
547         cairo_set_source_rgb (cr, 0, 0, 0);
548         cairo_stroke_preserve (cr);
549
550         set_color (cr, w.e->dt == DataType::MIDI);
551
552         if (w.e->sc) {
553                 assert (w.e->ct == Sink);
554                 cairo_fill_preserve (cr);
555                 cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.4);
556         }
557
558         if (w.e == _selection || w.e == _actor) {
559                 cairo_fill_preserve (cr);
560                 cairo_set_source_rgba (cr, 0.9, 0.9, 1.0, 0.6);
561         } else if (w.prelight) {
562                 cairo_fill_preserve (cr);
563                 cairo_set_source_rgba (cr, 0.9, 0.9, 0.9, 0.3);
564         }
565         cairo_fill (cr);
566 }
567
568 double
569 PluginPinDialog::pin_x_pos (uint32_t i, double x0, double width, uint32_t n_total, uint32_t n_midi, bool midi)
570 {
571         if (!midi) { i += n_midi; }
572         return rint (x0 + (i + 1) * width / (1. + n_total)) - .5;
573 }
574
575 const PluginPinDialog::CtrlWidget&
576 PluginPinDialog::get_io_ctrl (CtrlType ct, DataType dt, uint32_t id, uint32_t ip) const
577 {
578         for (CtrlElemList::const_iterator i = _elements.begin (); i != _elements.end (); ++i) {
579                 if (i->e->ct == ct && i->e->dt == dt && i->e->id == id && i->e->ip == ip) {
580                         return *i;
581                 }
582         }
583         fatal << string_compose (_("programming error: %1"),
584                         X_("Invalid Plugin I/O Port."))
585                 << endmsg;
586         abort (); /*NOTREACHED*/
587         static CtrlWidget screw_old_compilers (Input, DataType::NIL, 0);
588         return screw_old_compilers;
589 }
590
591 void
592 PluginPinDialog::edge_coordinates (const CtrlWidget& w, double &x, double &y)
593 {
594         switch (w.e->ct) {
595                 case Input:
596                         if (w.e->sc) {
597                                 x = w.x;
598                                 y = w.y + w.h * .5;
599                         } else {
600                                 x = w.x + w.w * .5;
601                                 y = w.y + w.h;
602                         }
603                         break;
604                 case Output:
605                         x = w.x + w.w * .5;
606                         y = w.y;
607                         break;
608                 case Sink:
609                         x = w.x + w.w * .5;
610                         y = w.y;
611                         break;
612                 case Source:
613                         x = w.x + w.w * .5;
614                         y = w.y + w.h;
615                         break;
616         }
617 }
618
619 void
620 PluginPinDialog::draw_connection (cairo_t* cr, double x0, double x1, double y0, double y1, bool midi, bool horiz, bool dashed)
621 {
622         const double bz = 2 * _pin_box_size;
623         const double bc = (dashed && x0 == x1) ? 1.25 * _pin_box_size : 0;
624
625         cairo_move_to (cr, x0, y0);
626         if (horiz) {
627                 cairo_curve_to (cr, x0 - bz, y0 + bc, x1 - bc, y1 - bz, x1, y1);
628         } else {
629                 cairo_curve_to (cr, x0 - bc, y0 + bz, x1 - bc, y1 - bz, x1, y1);
630         }
631         cairo_set_line_width (cr, 3.0);
632         cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
633         cairo_set_source_rgb (cr, 1, 0, 0);
634         if (dashed) {
635                 const double dashes[] = { 5, 7 };
636                 cairo_set_dash (cr, dashes, 2, 0);
637         }
638         set_color (cr, midi);
639         cairo_stroke (cr);
640         if (dashed) {
641                 cairo_set_dash (cr, 0, 0, 0);
642         }
643 }
644
645 void
646 PluginPinDialog::draw_connection (cairo_t* cr, const CtrlWidget& w0, const CtrlWidget& w1, bool dashed)
647 {
648         double x0, x1, y0, y1;
649         edge_coordinates (w0, x0, y0);
650         edge_coordinates (w1, x1, y1);
651         assert (w0.e->dt == w1.e->dt);
652         draw_connection (cr, x0, x1, y0, y1, w0.e->dt == DataType::MIDI, w0.e->sc, dashed);
653 }
654
655
656 bool
657 PluginPinDialog::darea_expose_event (GdkEventExpose* ev)
658 {
659         Gtk::Allocation a = darea.get_allocation ();
660         double const width = a.get_width ();
661         double const height = a.get_height ();
662
663         if (!_position_valid) {
664                 _width = width;
665                 _height = height;
666                 update_element_pos ();
667                 _position_valid = true;
668         }
669
670         cairo_t* cr = gdk_cairo_create (darea.get_window ()->gobj ());
671         cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
672         cairo_clip (cr);
673
674         Gdk::Color const bg = get_style ()->get_bg (STATE_NORMAL);
675         cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
676         cairo_rectangle (cr, 0, 0, width, height);
677         cairo_fill (cr);
678
679         const double yc = rint (_height * .5);
680
681         /* processor box */
682         rounded_rectangle (cr, _margin_x, _margin_y - _pin_box_size * .5, _innerwidth, _height - 2 * _margin_y + _pin_box_size, 7);
683         cairo_set_line_width (cr, 1.0);
684         cairo_set_source_rgb (cr, .1, .1, .3);
685         cairo_stroke_preserve (cr);
686         cairo_set_source_rgb (cr, .3, .3, .3);
687         cairo_fill (cr);
688
689         /* draw midi-bypass (behind) */
690         if (_pi->has_midi_bypass ()) {
691                 const CtrlWidget& cw0 = get_io_ctrl (Input, DataType::MIDI, 0);
692                 const CtrlWidget& cw1 = get_io_ctrl (Output, DataType::MIDI, 0);
693                 draw_connection (cr, cw0, cw1, true);
694         }
695
696         /* labels */
697         Glib::RefPtr<Pango::Layout> layout;
698         layout = Pango::Layout::create (get_pango_context ());
699
700         layout->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
701         layout->set_width (_height * PANGO_SCALE);
702
703         int text_width;
704         int text_height;
705
706         layout->set_text (_route ()->name ());
707         layout->get_pixel_size (text_width, text_height);
708         cairo_save (cr);
709         cairo_move_to (cr, .5 * (_margin_x - text_height), .5 * (_height + text_width));
710         cairo_rotate (cr, M_PI * -.5);
711         cairo_set_source_rgba (cr, 1., 1., 1., 1.);
712         pango_cairo_show_layout (cr, layout->gobj ());
713         cairo_new_path (cr);
714         cairo_restore (cr);
715
716         layout->set_width ((_innerwidth - 2 * _pin_box_size) * PANGO_SCALE);
717         layout->set_text (_pi->name ());
718         layout->get_pixel_size (text_width, text_height);
719         cairo_move_to (cr, _margin_x + _innerwidth - text_width - _pin_box_size * .5, _height - _margin_y - text_height);
720         cairo_set_source_rgba (cr, 1., 1., 1., 1.);
721         pango_cairo_show_layout (cr, layout->gobj ());
722
723         if (_pi->strict_io ()) {
724                 layout->set_text (_("Strict I/O"));
725                 layout->get_pixel_size (text_width, text_height);
726                 const double sx0 = _margin_x + .5 * (_innerwidth - text_width);
727                 const double sy0 = _height - 3 - text_height;
728
729                 rounded_rectangle (cr, sx0 - 2, sy0 - 1, text_width + 4, text_height + 2, 7);
730                 cairo_set_source_rgba (cr, .4, .3, .1, 1.);
731                 cairo_fill (cr);
732
733                 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
734                 cairo_move_to (cr, sx0, sy0);
735                 cairo_set_source_rgba (cr, 1., 1., 1., 1.);
736                 pango_cairo_show_layout (cr, layout->gobj ());
737         }
738
739
740         /* plugins & connection wires */
741         for (uint32_t i = 0; i < _n_plugins; ++i) {
742                 double x0 = _margin_x + rint ((i + .5) * _innerwidth / (double)(_n_plugins)) - .5;
743
744                 /* plugin box */
745                 cairo_set_source_rgb (cr, .5, .5, .5);
746                 rounded_rectangle (cr, x0 - _bxw2, yc - _bxh2, 2 * _bxw2, 2 * _bxh2, 7);
747                 cairo_fill (cr);
748
749                 const ChanMapping::Mappings in_map = _pi->input_map (i).mappings ();
750                 const ChanMapping::Mappings out_map = _pi->output_map (i).mappings ();
751
752                 for (ChanMapping::Mappings::const_iterator t = in_map.begin (); t != in_map.end (); ++t) {
753                         for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
754                                 const CtrlWidget& cw0 = get_io_ctrl (Input, t->first, c->second);
755                                 const CtrlWidget& cw1 = get_io_ctrl (Sink, t->first, c->first, i);
756                                 draw_connection (cr, cw0, cw1);
757                         }
758                 }
759
760                 for (ChanMapping::Mappings::const_iterator t = out_map.begin (); t != out_map.end (); ++t) {
761                         for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
762                                 const CtrlWidget& cw0 = get_io_ctrl (Source, t->first, c->first, i);
763                                 const CtrlWidget& cw1 = get_io_ctrl (Output, t->first, c->second);
764                                 draw_connection (cr, cw0, cw1);
765                         }
766                 }
767         }
768
769         /* pins and ports */
770         for (CtrlElemList::const_iterator i = _elements.begin (); i != _elements.end (); ++i) {
771                 switch (i->e->ct) {
772                         case Input:
773                         case Output:
774                                 draw_io_pin (cr, *i);
775                                 break;
776                         case Sink:
777                         case Source:
778                                 draw_plugin_pin (cr, *i);
779                                 break;
780                 }
781         }
782
783         cairo_destroy (cr);
784         return true;
785 }
786
787 void
788 PluginPinDialog::darea_size_request (Gtk::Requisition* req)
789 {
790         req->width = _min_width;
791         req->height = _min_height;
792 }
793
794 void
795 PluginPinDialog::darea_size_allocate (Gtk::Allocation&)
796 {
797         _position_valid = false;
798 }
799
800 bool
801 PluginPinDialog::darea_motion_notify_event (GdkEventMotion* ev)
802 {
803         bool changed = false;
804         _hover.reset ();
805         for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
806                 if (ev->x >= i->x && ev->x <= i->x + i->w
807                                 && ev->y >= i->y && ev->y <= i->y + i->h)
808                 {
809                         if (!i->prelight) changed = true;
810                         i->prelight = true;
811                         _hover = i->e;
812                 } else {
813                         if (i->prelight) changed = true;
814                         i->prelight = false;
815                 }
816         }
817         if (changed) {
818                 darea.queue_draw ();
819         }
820         return true;
821 }
822
823 bool
824 PluginPinDialog::darea_button_press_event (GdkEventButton* ev)
825 {
826         if (ev->type != GDK_BUTTON_PRESS) {
827                 return false;
828         }
829
830         switch (ev->button) {
831                 case 1:
832                         if (!_selection || (_selection && !_hover)) {
833                                 _selection = _hover;
834                                 _actor.reset ();
835                                 darea.queue_draw ();
836                         } else if (_selection && _hover && _selection != _hover) {
837                                 if (_selection->dt != _hover->dt) { _actor.reset (); }
838                                 else if (_selection->ct == Input && _hover->ct == Sink) { _actor = _hover; }
839                                 else if (_selection->ct == Sink && _hover->ct == Input) { _actor = _hover; }
840                                 else if (_selection->ct == Output && _hover->ct == Source) { _actor = _hover; }
841                                 else if (_selection->ct == Source && _hover->ct == Output) { _actor = _hover; }
842                                 if (!_actor) {
843                                 _selection = _hover;
844                                 }
845                                 darea.queue_draw ();
846                         }
847                         break;
848                 case 3:
849                         if (_selection != _hover) {
850                                 _selection = _hover;
851                                 darea.queue_draw ();
852                         }
853                         _actor.reset ();
854                         break;
855                 default:
856                         break;
857         }
858
859         return true;
860 }
861
862 bool
863 PluginPinDialog::darea_button_release_event (GdkEventButton* ev)
864 {
865         if (_hover == _actor && _actor && ev->button == 1) {
866                 assert (_selection);
867                 assert (_selection->dt == _actor->dt);
868                 if      (_selection->ct == Input && _actor->ct == Sink) {
869                         handle_input_action (_actor, _selection);
870                 }
871                 else if (_selection->ct == Sink && _actor->ct == Input) {
872                         handle_input_action (_selection, _actor);
873                 }
874                 else if (_selection->ct == Output && _actor->ct == Source) {
875                         handle_output_action (_actor, _selection);
876                 }
877                 else if (_selection->ct == Source && _actor->ct == Output) {
878                         handle_output_action (_selection, _actor);
879                 }
880                 _selection.reset ();
881         } else if (_hover == _selection && _selection && ev->button == 3) {
882                 handle_disconnect (_selection);
883         }
884         _actor.reset ();
885         darea.queue_draw ();
886         return true;
887 }
888
889 void
890 PluginPinDialog::handle_input_action (const CtrlElem &s, const CtrlElem &i)
891 {
892         const int pc = s->ip;
893         bool valid;
894         ChanMapping in_map (_pi->input_map (pc));
895         uint32_t idx = in_map.get (s->dt, s->id, &valid);
896
897         if (valid && idx == i->id) {
898                 // disconnect
899                 in_map.unset (s->dt, s->id);
900                 _pi->set_input_map (pc, in_map);
901         }
902         else if (!valid) {
903                 // connect
904                 in_map.set (s->dt, s->id, i->id);
905                 _pi->set_input_map (pc, in_map);
906         }
907         else {
908                 // reconnect
909                 in_map.unset (s->dt, s->id);
910                 in_map.set (s->dt, s->id, i->id);
911                 _pi->set_input_map (pc, in_map);
912         }
913 }
914
915 void
916 PluginPinDialog::handle_output_action (const CtrlElem &s, const CtrlElem &o)
917 {
918         const uint32_t pc = s->ip;
919         bool valid;
920         ChanMapping out_map (_pi->output_map (pc));
921         uint32_t idx = out_map.get (s->dt, s->id, &valid);
922
923         if (valid && idx == o->id) {
924                 // disconnect
925                 out_map.unset (s->dt, s->id);
926                 _pi->set_output_map (pc, out_map);
927         }
928         else {
929                 // disconnect source
930                 if (valid) {
931                         out_map.unset (s->dt, s->id);
932                 }
933                 // disconnect other outputs
934                 _ignore_updates = true;
935                 for (uint32_t n = 0; n < _n_plugins; ++n) {
936                         if (n == pc) {
937                                 continue;
938                         }
939                         ChanMapping n_out_map (_pi->output_map (n));
940                         idx = n_out_map.get_src (s->dt, o->id, &valid);
941                         if (valid) {
942                                 n_out_map.unset (s->dt, idx);
943                                 _pi->set_output_map (n, n_out_map);
944                         }
945                 }
946                 _ignore_updates = false;
947                 idx = out_map.get_src (s->dt, o->id, &valid);
948                 if (valid) {
949                         out_map.unset (s->dt, idx);
950                 }
951                 // connect
952                 out_map.set (s->dt, s->id, o->id);
953                 _pi->set_output_map (pc, out_map);
954         }
955 }
956
957 void
958 PluginPinDialog::handle_disconnect (const CtrlElem &e)
959 {
960         _ignore_updates = true;
961         bool changed = false;
962         bool valid;
963
964         switch (e->ct) {
965                 case Input:
966                         for (uint32_t n = 0; n < _n_plugins; ++n) {
967                                 ChanMapping map (_pi->input_map (n));
968                                 for (uint32_t i = 0; i < _sinks.n_total (); ++i) {
969                                         uint32_t idx = map.get (e->dt, i, &valid);
970                                         if (valid && idx == e->id) {
971                                                 map.unset (e->dt, i);
972                                                 changed = true;
973                                         }
974                                 }
975                                 _pi->set_input_map (n, map);
976                         }
977                         break;
978                 case Sink:
979                         {
980                                 ChanMapping map (_pi->input_map (e->ip));
981                                 map.get (e->dt, e->id, &valid);
982                                 if (valid) {
983                                         map.unset (e->dt, e->id);
984                                         _pi->set_input_map (e->ip, map);
985                                         changed = true;
986                                 }
987                         }
988                         break;
989                 case Source:
990                         {
991                                 ChanMapping map (_pi->output_map (e->ip));
992                                 map.get (e->dt, e->id, &valid);
993                                 if (valid) {
994                                         map.unset (e->dt, e->id);
995                                         _pi->set_output_map (e->ip, map);
996                                         changed = true;
997                                 }
998                         }
999                         break;
1000                 case Output:
1001                         for (uint32_t n = 0; n < _n_plugins; ++n) {
1002                                 ChanMapping map (_pi->output_map (n));
1003                                 for (uint32_t i = 0; i < _sources.n_total (); ++i) {
1004                                         uint32_t idx = map.get (e->dt, i, &valid);
1005                                         if (valid && idx == e->id) {
1006                                                 map.unset (e->dt, i);
1007                                                 changed = true;
1008                                         }
1009                                 }
1010                                 _pi->set_output_map (n, map);
1011                         }
1012                         break;
1013         }
1014         _ignore_updates = false;
1015         if (changed) {
1016                 plugin_reconfigured ();
1017         }
1018 }
1019
1020 void
1021 PluginPinDialog::toggle_sidechain ()
1022 {
1023         if (_session && _session->actively_recording()) { return; }
1024         _route ()->add_remove_sidechain (_pi, !_pi->has_sidechain ());
1025 }
1026
1027 void
1028 PluginPinDialog::connect_sidechain ()
1029 {
1030         if (!_session) { return; }
1031
1032         if (_sidechain_selector == 0) {
1033                 _sidechain_selector = new IOSelectorWindow (_session, _pi->sidechain_input ());
1034         }
1035
1036         if (_sidechain_selector->is_visible ()) {
1037                 _sidechain_selector->get_toplevel ()->get_window ()->raise ();
1038         } else {
1039                 _sidechain_selector->present ();
1040         }
1041 }
1042
1043 void
1044 PluginPinDialog::reset_configuration ()
1045 {
1046         _route ()->reset_plugin_insert (_pi);
1047 }
1048
1049 void
1050 PluginPinDialog::reset_mapping ()
1051 {
1052         _pi->reset_map ();
1053 }
1054
1055 void
1056 PluginPinDialog::add_remove_plugin_clicked (bool add)
1057 {
1058         if (_session && _session->actively_recording()) { return; }
1059         ChanCount out = _out;
1060         assert (add || _n_plugins > 0);
1061         _route ()->customize_plugin_insert (_pi, _n_plugins + (add ? 1 : -1),  out);
1062 }
1063
1064 void
1065 PluginPinDialog::add_remove_port_clicked (bool add, ARDOUR::DataType dt)
1066 {
1067         if (_session && _session->actively_recording()) { return; }
1068         ChanCount out = _out;
1069         assert (add || out.get (dt) > 0);
1070         out.set (dt, out.get (dt) + (add ? 1 : -1));
1071         _route ()->customize_plugin_insert (_pi, _n_plugins, out);
1072 }
1073
1074 void
1075 PluginPinDialog::add_sidechain_port (DataType dt)
1076 {
1077         if (_session && _session->actively_recording()) { return; }
1078         boost::shared_ptr<IO> io = _pi->sidechain_input ();
1079         if (!io) {
1080                 return;
1081         }
1082         io->add_port ("", this, dt);
1083 }
1084
1085 void
1086 PluginPinDialog::remove_port (boost::weak_ptr<ARDOUR::Port> wp)
1087 {
1088         if (_session && _session->actively_recording()) { return; }
1089         boost::shared_ptr<ARDOUR::Port> p = wp.lock ();
1090         boost::shared_ptr<IO> io = _pi->sidechain_input ();
1091         if (!io || !p) {
1092                 return;
1093         }
1094         io->remove_port (p, this);
1095 }
1096
1097 void
1098 PluginPinDialog::disconnect_port (boost::weak_ptr<ARDOUR::Port> wp)
1099 {
1100         if (_session && _session->actively_recording()) { return; }
1101         boost::shared_ptr<ARDOUR::Port> p = wp.lock ();
1102         boost::shared_ptr<IO> io = _pi->sidechain_input ();
1103         if (!io || !p) {
1104                 return;
1105         }
1106         p->disconnect_all ();
1107         io_changed_proxy ();
1108 }
1109
1110 void
1111 PluginPinDialog::connect_port (boost::weak_ptr<ARDOUR::Port> wp0, boost::weak_ptr<ARDOUR::Port> wp1)
1112 {
1113         if (_session && _session->actively_recording()) { return; }
1114         boost::shared_ptr<ARDOUR::Port> p0 = wp0.lock ();
1115         boost::shared_ptr<ARDOUR::Port> p1 = wp1.lock ();
1116         boost::shared_ptr<IO> io = _pi->sidechain_input ();
1117         if (!io || !p0 || !p1) {
1118                 return;
1119         }
1120         p0->connect (p1->name ());
1121         io_changed_proxy ();
1122 }
1123
1124 bool
1125 PluginPinDialog::sc_input_release (GdkEventButton *ev)
1126 {
1127         if (_session && _session->actively_recording()) { return false; }
1128         if (ev->button == 3) {
1129                 connect_sidechain ();
1130         }
1131         return false;
1132 }
1133
1134 struct RouteCompareByName {
1135         bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
1136                 return a->name().compare (b->name()) < 0;
1137         }
1138 };
1139
1140 bool
1141 PluginPinDialog::sc_input_press (GdkEventButton *ev, boost::weak_ptr<ARDOUR::Port> wp)
1142 {
1143         using namespace Menu_Helpers;
1144         if (!_session || _session->actively_recording()) { return false; }
1145         if (!_session->engine().connected()) { return false; }
1146
1147         if (ev->button == 1) {
1148                 MenuList& citems = input_menu.items();
1149                 input_menu.set_name ("ArdourContextMenu");
1150                 citems.clear();
1151
1152                 boost::shared_ptr<Port> p = wp.lock ();
1153                 if (p && p->connected ()) {
1154                         citems.push_back (MenuElem (_("Disconnect"), sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::disconnect_port), wp)));
1155                         citems.push_back (SeparatorElem());
1156                 }
1157
1158                 // TODO add system inputs, too ?!
1159
1160                 boost::shared_ptr<ARDOUR::RouteList> routes = _session->get_routes ();
1161                 RouteList copy = *routes;
1162                 copy.sort (RouteCompareByName ());
1163                 uint32_t added = 0;
1164                 for (ARDOUR::RouteList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
1165                         added += maybe_add_route_to_input_menu (*i, p->type (), wp);
1166                 }
1167
1168                 if (added > 0) {
1169                         citems.push_back (SeparatorElem());
1170                 }
1171                 citems.push_back (MenuElem (_("Routing Grid"), sigc::mem_fun (*this, &PluginPinDialog::connect_sidechain)));
1172                 input_menu.popup (1, ev->time);
1173         }
1174         return false;
1175 }
1176
1177 uint32_t
1178 PluginPinDialog::maybe_add_route_to_input_menu (boost::shared_ptr<Route> r, DataType dt, boost::weak_ptr<Port> wp)
1179 {
1180         uint32_t added = 0;
1181         using namespace Menu_Helpers;
1182         if (r->output () == _route()->output()) {
1183                 return added;
1184         }
1185
1186         if (_route ()->feeds_according_to_graph (r)) {
1187                 return added;
1188         }
1189
1190         MenuList& citems = input_menu.items();
1191         const IOVector& iov (r->all_outputs());
1192
1193         for (IOVector::const_iterator o = iov.begin(); o != iov.end(); ++o) {
1194                 boost::shared_ptr<IO> op = o->lock();
1195                 if (!op) {
1196                         continue;
1197                 }
1198                 PortSet& p (op->ports ());
1199                 for (PortSet::iterator i = p.begin (dt); i != p.end (dt); ++i) {
1200                         std::string n = i->name ();
1201                         replace_all (n, "_", " ");
1202                         citems.push_back (MenuElem (n, sigc::bind (sigc::mem_fun(*this, &PluginPinDialog::connect_port), wp, boost::weak_ptr<Port> (*i))));
1203                         ++added;
1204                 }
1205         }
1206         return added;
1207 }
1208
1209 void
1210 PluginPinDialog::io_changed_proxy ()
1211 {
1212         Glib::signal_idle().connect_once (sigc::mem_fun (*this, &PluginPinDialog::plugin_reconfigured));
1213 }