NO-OP, whitespace
[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 "gtkmm2ext/utils.h"
25 #include "gtkmm2ext/rgb_macros.h"
26
27 #include "plugin_pin_dialog.h"
28 #include "gui_thread.h"
29 #include "ui_config.h"
30
31 #include "i18n.h"
32
33 using namespace ARDOUR;
34 using namespace PBD;
35 using namespace std;
36 using namespace Gtk;
37 using namespace Gtkmm2ext;
38
39 PluginPinDialog::PluginPinDialog (boost::shared_ptr<ARDOUR::PluginInsert> pi)
40         : ArdourWindow (string_compose (_("Pin Configuration: %1"), pi->name ()))
41         , _ind_strict_io (_("Strict I/O"))
42         , _ind_customized (_("Customized"))
43         , _rst_config (_("Configuration"))
44         , _rst_mapping (_("Connections"))
45         , _add_plugin (_("+"))
46         , _del_plugin (_("-"))
47         , _add_output_audio (_("+"))
48         , _del_output_audio (_("-"))
49         , _add_output_midi (_("+"))
50         , _del_output_midi (_("-"))
51         , _pi (pi)
52         , _pin_box_size (8)
53         , _min_width (300)
54         , _position_valid (false)
55         , _ignore_updates (false)
56 {
57         assert (pi->owner ()); // Route
58
59         _pi->PluginIoReConfigure.connect (
60                         _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context()
61                         );
62
63         _pi->PluginMapChanged.connect (
64                         _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context()
65                         );
66
67         _pi->PluginConfigChanged.connect (
68                         _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context()
69                         );
70
71         // needs a better way: insensitive indicators
72         _ind_strict_io.set_sensitive (false); // track status
73         _ind_strict_io.set_name ("rude solo");
74         _ind_customized.set_sensitive (false); // plugin status
75         _ind_customized.set_name ("rude solo");
76
77         Label* l;
78         int r = 0;
79         Table* t = manage (new Table (4, 3));
80         t->set_border_width (0);
81         t->set_spacings (4);
82
83         l = manage (new Label (_("Track/Bus:"), ALIGN_END));
84         t->attach (*l, 0, 1, r, r + 1);
85         l = manage (new Label ());
86         l->set_alignment (ALIGN_START);
87         l->set_padding (2, 1);
88         l->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
89         l->set_width_chars (24);
90         l->set_max_width_chars (24);
91         l->set_text (_route()->name ());
92         t->attach (*l, 1, 3, r, r + 1);
93         ++r;
94
95         l = manage (new Label (_("Plugin:"), ALIGN_END));
96         t->attach (*l, 0, 1, r, r + 1);
97         l = manage (new Label ());
98         l->set_alignment(ALIGN_START);
99         l->set_padding (2, 1);
100         l->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
101         l->set_width_chars (24);
102         l->set_max_width_chars (24);
103         l->set_text (pi->name ());
104         t->attach (*l, 1, 3, r, r + 1);
105         ++r;
106
107         l = manage (new Label (_("Status:"), ALIGN_END));
108         t->attach (*l, 0, 1, r, r + 1);
109         t->attach (_ind_strict_io, 1, 2, r, r + 1, FILL, SHRINK);
110         t->attach (_ind_customized, 2, 3, r, r + 1, FILL, SHRINK);
111         ++r;
112
113         l = manage (new Label (_("Reset:"), ALIGN_END));
114         t->attach (*l, 0, 1, r, r + 1);
115         t->attach (_rst_mapping, 1, 2, r, r + 1, FILL, SHRINK);
116         t->attach (_rst_config, 2, 3, r, r + 1, FILL, SHRINK);
117         ++r;
118
119         l = manage (new Label (_("Instances:"), ALIGN_END));
120         t->attach (*l, 0, 1, r, r + 1);
121         t->attach (_add_plugin, 1, 2, r, r + 1, SHRINK, SHRINK);
122         t->attach (_del_plugin, 2, 3, r, r + 1, SHRINK, SHRINK);
123         ++r;
124
125         l = manage (new Label (_("Audio Out:"), ALIGN_END));
126         t->attach (*l, 0, 1, r, r + 1);
127         t->attach (_add_output_audio, 1, 2, r, r + 1, SHRINK, SHRINK);
128         t->attach (_del_output_audio, 2, 3, r, r + 1, SHRINK, SHRINK);
129         ++r;
130
131         l = manage (new Label (_("Midi Out:"), ALIGN_END));
132         t->attach (*l, 0, 1, r, r + 1);
133         t->attach (_add_output_midi, 1, 2, r, r + 1, SHRINK, SHRINK);
134         t->attach (_del_output_midi, 2, 3, r, r + 1, SHRINK, SHRINK);
135         ++r;
136
137         HBox* hbox = manage (new HBox);
138         hbox->pack_start (darea, true, true);
139         hbox->pack_start (*t, false, true);
140
141         VBox* vbox = manage (new VBox);
142         vbox->pack_start (*hbox, true, true);
143         add (*vbox);
144         vbox->show_all ();
145
146         plugin_reconfigured ();
147
148         darea.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
149         darea.signal_size_request ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_request));
150         darea.signal_size_allocate ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_allocate));
151         darea.signal_expose_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_expose_event));
152         darea.signal_button_press_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_press_event));
153         darea.signal_button_release_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_release_event));
154         darea.signal_motion_notify_event ().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_motion_notify_event));
155
156         _rst_mapping.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::reset_mapping));
157         _rst_config.signal_clicked.connect (sigc::mem_fun (*this, &PluginPinDialog::reset_configuration));
158         _add_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_plugin_clicked), true));
159         _del_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_plugin_clicked), false));
160
161         _add_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::AUDIO));
162         _del_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::AUDIO));
163         _add_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::MIDI));
164         _del_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::MIDI));
165 }
166
167 PluginPinDialog::~PluginPinDialog ()
168 {
169 }
170
171 void
172 PluginPinDialog::plugin_reconfigured ()
173 {
174         if (_ignore_updates) {
175                 return;
176         }
177         _n_plugins = _pi->get_count ();
178         _pi->configured_io (_in, _out);
179         _sinks = _pi->natural_input_streams ();
180         _sources = _pi->natural_output_streams ();
181
182         _del_plugin.set_sensitive (_n_plugins > 1);
183         _del_output_audio.set_sensitive (_out.n_audio () > 0 && _out.n_total () > 1);
184         _del_output_midi.set_sensitive (_out.n_midi () > 0 && _out.n_total () > 1);
185         _ind_strict_io.set_active (_pi->strict_io ());
186         _ind_customized.set_active (_pi->custom_cfg ());
187
188         // calc minimum width
189         const uint32_t max_ports = std::max (_in.n_total (), _out.n_total ());
190         const uint32_t max_pins = std::max ((_sinks * _n_plugins).n_total (), (_sources * _n_plugins).n_total ());
191         uint32_t min_width = std::max (25 * max_ports, (uint32_t)(20 + _pin_box_size) * max_pins);
192         min_width = std::max (min_width, 64 * _n_plugins); // bxh2 = 18 ; aspect 16:9 (incl. 10% space)
193         min_width = std::max ((uint32_t)300, min_width);
194         min_width = 10 * ceilf (min_width / 10.f);
195         if (min_width != _min_width) {
196                 _min_width = min_width;
197                 darea.queue_resize ();
198         }
199
200         update_elements ();
201 }
202
203 void
204 PluginPinDialog::update_elements ()
205 {
206         _elements.clear ();
207         _hover.reset ();
208         _actor.reset ();
209         _selection.reset ();
210
211         for (uint32_t i = 0; i < _in.n_total (); ++i) {
212                 int id = (i < _in.n_midi ()) ? i : i - _in.n_midi ();
213                 _elements.push_back (CtrlWidget (Input, (i < _in.n_midi () ? DataType::MIDI : DataType::AUDIO), id));
214         }
215
216         for (uint32_t i = 0; i < _out.n_total (); ++i) {
217                 int id = (i < _out.n_midi ()) ? i : i - _out.n_midi ();
218                 _elements.push_back (CtrlWidget (Output, (i < _out.n_midi () ? DataType::MIDI : DataType::AUDIO), id));
219         }
220
221         for (uint32_t n = 0; n < _n_plugins; ++n) {
222                 for (uint32_t i = 0; i < _sinks.n_total (); ++i) {
223                         _elements.push_back (CtrlWidget (Sink, (i < _sinks.n_midi () ? DataType::MIDI : DataType::AUDIO), i, n));
224                 }
225                 for (uint32_t i = 0; i < _sources.n_total (); ++i) {
226                         _elements.push_back (CtrlWidget (Source, (i < _sources.n_midi () ? DataType::MIDI : DataType::AUDIO), i, n));
227                 }
228         }
229         _position_valid = false;
230         darea.queue_draw ();
231 }
232
233 void
234 PluginPinDialog::update_element_pos ()
235 {
236         /* layout sizes */
237         const double yc   = rint (_height * .5);
238         const double bxh2 = 18;
239         const double bxw  = rint ((_width * .9) / ((_n_plugins) + .2 * (_n_plugins - 1)));
240         const double bxw2 = rint (bxw * .5);
241         const double y_in = 40;
242         const double y_out = _height - 40;
243
244         _pin_box_size = rint (max (6., 8. * UIConfiguration::instance ().get_ui_scale ()));
245
246         for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
247                 switch (i->e->ct) {
248                         case Input:
249                                 {
250                                         uint32_t idx = i->e->id;
251                                         if (i->e->dt == DataType::AUDIO) { idx += _in.n_midi (); }
252                                         i->x = rint ((idx + 1) * _width / (1. + _in.n_total ())) - 5.5;
253                                         i->y = y_in - 25;
254                                         i->w = 10;
255                                         i->h = 25;
256                                 }
257                                 break;
258                         case Output:
259                                 {
260                                         uint32_t idx = i->e->id;
261                                         if (i->e->dt == DataType::AUDIO) { idx += _out.n_midi (); }
262                                         i->x = rint ((idx + 1) * _width / (1. + _out.n_total ())) - 5.5;
263                                         i->y = y_out;
264                                         i->w = 10;
265                                         i->h = 25;
266                                 }
267                                 break;
268                         case Sink:
269                                 {
270                                         const double x0 = rint ((i->e->ip + .5) * _width / (double)(_n_plugins)) - .5 - bxw2;
271                                         i->x = rint (x0 + (i->e->id + 1) * bxw / (1. + _sinks.n_total ())) - .5 - _pin_box_size * .5;
272                                         i->y = yc - bxh2 - _pin_box_size;
273                                         i->w = _pin_box_size + 1;
274                                         i->h = _pin_box_size;
275                                 }
276                                 break;
277                         case Source:
278                                 {
279                                         const double x0 = rint ((i->e->ip + .5) * _width / (double)(_n_plugins)) - .5 - bxw2;
280                                         i->x = rint (x0 + (i->e->id + 1) * bxw / (1. + _sources.n_total ())) - .5 - _pin_box_size * .5;
281                                         i->y = yc + bxh2;
282                                         i->w = _pin_box_size + 1;
283                                         i->h = _pin_box_size;
284                                 }
285                                 break;
286                 }
287         }
288
289 }
290
291 void
292 PluginPinDialog::set_color (cairo_t* cr, bool midi)
293 {
294         // see also gtk2_ardour/processor_box.cc
295         static const uint32_t audio_port_color = 0x4A8A0EFF; // Green
296         static const uint32_t midi_port_color = 0x960909FF; //Red
297
298         if (midi) {
299                 cairo_set_source_rgb (cr,
300                                 UINT_RGBA_R_FLT (midi_port_color),
301                                 UINT_RGBA_G_FLT (midi_port_color),
302                                 UINT_RGBA_B_FLT (midi_port_color));
303         } else {
304                 cairo_set_source_rgb (cr,
305                                 UINT_RGBA_R_FLT (audio_port_color),
306                                 UINT_RGBA_G_FLT (audio_port_color),
307                                 UINT_RGBA_B_FLT (audio_port_color));
308         }
309 }
310
311 void
312 PluginPinDialog::draw_io_pin (cairo_t* cr, const CtrlWidget& w)
313 {
314         const double dir = (w.e->ct == Input) ? 1 : -1;
315
316         cairo_move_to (cr, w.x + 5.0, w.y + ((w.e->ct == Input) ? 25 : 0));
317         cairo_rel_line_to (cr, -5.,  -5. * dir);
318         cairo_rel_line_to (cr,  0., -25. * dir);
319         cairo_rel_line_to (cr, 10.,   0.);
320         cairo_rel_line_to (cr,  0.,  25. * dir);
321         cairo_close_path  (cr);
322
323         cairo_set_line_width (cr, 1.0);
324         cairo_set_source_rgb (cr, 0, 0, 0);
325         cairo_stroke_preserve (cr);
326
327         set_color (cr, w.e->dt == DataType::MIDI);
328         if (w.e == _selection || w.e == _actor) {
329                 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
330         } else if (w.prelight) {
331                 cairo_fill_preserve (cr);
332                 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
333         }
334         cairo_fill (cr);
335 }
336
337 void
338 PluginPinDialog::draw_plugin_pin (cairo_t* cr, const CtrlWidget& w)
339 {
340         cairo_rectangle (cr, w.x, w.y, w.w, w.h);
341         set_color (cr, w.e->dt == DataType::MIDI);
342         if (w.e == _selection || w.e == _actor) {
343                 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
344         } else if (w.prelight) {
345                 cairo_fill_preserve (cr);
346                 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
347         }
348         cairo_fill (cr);
349 }
350
351 double
352 PluginPinDialog::pin_x_pos (uint32_t i, double x0, double width, uint32_t n_total, uint32_t n_midi, bool midi)
353 {
354         if (!midi) { i += n_midi; }
355         return rint (x0 + (i + 1) * width / (1. + n_total)) - .5;
356 }
357
358 void
359 PluginPinDialog::draw_connection (cairo_t* cr, double x0, double x1, double y0, double y1, bool midi, bool dashed)
360 {
361         const double bz = 2 * _pin_box_size;
362         const double bc = dashed ? 1.25 * _pin_box_size : 0;
363
364         cairo_move_to (cr, x0, y0);
365         cairo_curve_to (cr, x0 - bc, y0 + bz, x1 - bc, y1 - bz, x1, y1);
366         cairo_set_line_width (cr, 3.0);
367         cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
368         cairo_set_source_rgb (cr, 1, 0, 0);
369         if (dashed) {
370                 const double dashes[] = { 5, 7 };
371                 cairo_set_dash (cr, dashes, 2, 0);
372         }
373         set_color (cr, midi);
374         cairo_stroke (cr);
375         if (dashed) {
376                 cairo_set_dash (cr, 0, 0, 0);
377         }
378 }
379
380 bool
381 PluginPinDialog::darea_expose_event (GdkEventExpose* ev)
382 {
383         Gtk::Allocation a = darea.get_allocation ();
384         double const width = a.get_width ();
385         double const height = a.get_height ();
386
387         if (!_position_valid) {
388                 _width = width;
389                 _height = height;
390                 update_element_pos ();
391                 _position_valid = true;
392         }
393
394         cairo_t* cr = gdk_cairo_create (darea.get_window ()->gobj ());
395         cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
396         cairo_clip (cr);
397
398         Gdk::Color const bg = get_style ()->get_bg (STATE_NORMAL);
399         cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
400         cairo_rectangle (cr, 0, 0, width, height);
401         cairo_fill (cr);
402
403         /* layout sizes  -- TODO consolidate w/ update_element_pos() */
404         // i/o pins
405         const double y_in = 40;
406         const double y_out = height - 40;
407
408         // plugin box(es)
409         const double yc   = rint (height * .5);
410         const double bxh2 = 18;
411         const double bxw  = rint ((width * .9) / ((_n_plugins) + .2 * (_n_plugins - 1)));
412         const double bxw2 = rint (bxw * .5);
413
414         const uint32_t pc_in = _in.n_total ();
415         const uint32_t pc_in_midi = _in.n_midi ();
416         const uint32_t pc_out = _out.n_total ();
417         const uint32_t pc_out_midi = _out.n_midi ();
418
419         /* draw midi-bypass (behind) */
420         if (_pi->has_midi_bypass ()) {
421                 double x0 = rint (width / (1. + pc_in)) - .5;
422                 double x1 = rint (width / (1. + pc_out)) - .5;
423                 draw_connection (cr, x0, x1, y_in, y_out, true, true);
424         }
425
426         /* plugins & connection wires */
427         for (uint32_t i = 0; i < _n_plugins; ++i) {
428                 double x0 = rint ((i + .5) * width / (double)(_n_plugins)) - .5;
429
430                 /* plugin box */
431                 cairo_set_source_rgb (cr, .3, .3, .3);
432                 rounded_rectangle (cr, x0 - bxw2, yc - bxh2, bxw, 2 * bxh2, 7);
433                 cairo_fill (cr);
434
435                 const ChanMapping::Mappings in_map = _pi->input_map (i).mappings ();
436                 const ChanMapping::Mappings out_map = _pi->output_map (i).mappings ();
437
438                 for (ChanMapping::Mappings::const_iterator t = in_map.begin (); t != in_map.end (); ++t) {
439                         bool is_midi = t->first == DataType::MIDI;
440                         for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
441                                 uint32_t pn = (*c).first; // pin
442                                 uint32_t pb = (*c).second;
443                                 double c_x0 = pin_x_pos (pb, 0, width, pc_in, pc_in_midi, is_midi);
444                                 double c_x1 = pin_x_pos (pn, x0 - bxw2, bxw, _sinks.n_total (), _sinks.n_midi (), is_midi);
445                                 draw_connection (cr, c_x0, c_x1, y_in, yc - bxh2 - _pin_box_size, is_midi);
446                         }
447                 }
448
449                 for (ChanMapping::Mappings::const_iterator t = out_map.begin (); t != out_map.end (); ++t) {
450                         bool is_midi = t->first == DataType::MIDI;
451                         for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
452                                 uint32_t pn = (*c).first; // pin
453                                 uint32_t pb = (*c).second;
454                                 double c_x0 = pin_x_pos (pn, x0 - bxw2, bxw, _sources.n_total (), _sources.n_midi (), is_midi);
455                                 double c_x1 = pin_x_pos (pb, 0, width, pc_out, pc_out_midi, is_midi);
456                                 draw_connection (cr, c_x0, c_x1, yc + bxh2 + _pin_box_size, y_out, is_midi);
457                         }
458                 }
459         }
460
461         /* pins and ports */
462         for (CtrlElemList::const_iterator i = _elements.begin (); i != _elements.end (); ++i) {
463                 switch (i->e->ct) {
464                         case Input:
465                         case Output:
466                                 draw_io_pin (cr, *i);
467                                 break;
468                         case Sink:
469                         case Source:
470                                 draw_plugin_pin (cr, *i);
471                                 break;
472                 }
473         }
474
475         cairo_destroy (cr);
476         return true;
477 }
478
479 void
480 PluginPinDialog::darea_size_request (Gtk::Requisition* req)
481 {
482         req->width = _min_width;
483         req->height = 200;
484 }
485
486 void
487 PluginPinDialog::darea_size_allocate (Gtk::Allocation&)
488 {
489         _position_valid = false;
490 }
491
492 bool
493 PluginPinDialog::darea_motion_notify_event (GdkEventMotion* ev)
494 {
495         bool changed = false;
496         _hover.reset ();
497         for (CtrlElemList::iterator i = _elements.begin (); i != _elements.end (); ++i) {
498                 if (ev->x >= i->x && ev->x <= i->x + i->w
499                                 && ev->y >= i->y && ev->y <= i->y + i->h)
500                 {
501                         if (!i->prelight) changed = true;
502                         i->prelight = true;
503                         _hover = i->e;
504                 } else {
505                         if (i->prelight) changed = true;
506                         i->prelight = false;
507                 }
508         }
509         if (changed) {
510                 darea.queue_draw ();
511         }
512         return true;
513 }
514
515 bool
516 PluginPinDialog::darea_button_press_event (GdkEventButton* ev)
517 {
518         if (ev->type != GDK_BUTTON_PRESS) {
519                 return false;
520         }
521
522         switch (ev->button) {
523                 case 1:
524                         if (!_selection || (_selection && !_hover)) {
525                                 _selection = _hover;
526                                 _actor.reset ();
527                                 darea.queue_draw ();
528                         } else if (_selection && _hover && _selection != _hover) {
529                                 if (_selection->dt != _hover->dt) { _actor.reset (); }
530                                 else if (_selection->ct == Input && _hover->ct == Sink) { _actor = _hover; }
531                                 else if (_selection->ct == Sink && _hover->ct == Input) { _actor = _hover; }
532                                 else if (_selection->ct == Output && _hover->ct == Source) { _actor = _hover; }
533                                 else if (_selection->ct == Source && _hover->ct == Output) { _actor = _hover; }
534                                 if (!_actor) {
535                                 _selection = _hover;
536                                 }
537                                 darea.queue_draw ();
538                         }
539                 case 3:
540                         if (_hover) {
541                         }
542                         break;
543                 default:
544                         break;
545         }
546
547         return true;
548 }
549
550 bool
551 PluginPinDialog::darea_button_release_event (GdkEventButton* ev)
552 {
553         if (_hover == _actor && _actor) {
554                 assert (_selection);
555                 assert (_selection->dt == _actor->dt);
556                 if      (_selection->ct == Input && _actor->ct == Sink) {
557                         handle_input_action (_actor, _selection);
558                 }
559                 else if (_selection->ct == Sink && _actor->ct == Input) {
560                         handle_input_action (_selection, _actor);
561                 }
562                 else if (_selection->ct == Output && _actor->ct == Source) {
563                         handle_output_action (_actor, _selection);
564                 }
565                 else if (_selection->ct == Source && _actor->ct == Output) {
566                         handle_output_action (_selection, _actor);
567                 }
568                 _selection.reset ();
569         }
570         _actor.reset ();
571         darea.queue_draw ();
572         return true;
573 }
574
575 void
576 PluginPinDialog::handle_input_action (const CtrlElem &s, const CtrlElem &i)
577 {
578         const int pc = s->ip;
579         bool valid;
580         ChanMapping in_map (_pi->input_map (pc));
581         uint32_t idx = in_map.get (s->dt, s->id, &valid);
582
583         if (valid && idx == i->id) {
584                 // disconnect
585                 in_map.unset (s->dt, s->id);
586                 _pi->set_input_map (pc, in_map);
587         }
588         else if (!valid) {
589                 // connect
590                 in_map.set (s->dt, s->id, i->id);
591                 _pi->set_input_map (pc, in_map);
592         }
593         else {
594                 // reconnect
595                 in_map.unset (s->dt, s->id);
596                 in_map.set (s->dt, s->id, i->id);
597                 _pi->set_input_map (pc, in_map);
598         }
599 }
600
601 void
602 PluginPinDialog::handle_output_action (const CtrlElem &s, const CtrlElem &o)
603 {
604         const uint32_t pc = s->ip;
605         bool valid;
606         ChanMapping out_map (_pi->output_map (pc));
607         uint32_t idx = out_map.get (s->dt, s->id, &valid);
608
609         if (valid && idx == o->id) {
610                 // disconnect
611                 out_map.unset (s->dt, s->id);
612                 _pi->set_output_map (pc, out_map);
613         }
614         else {
615                 // disconnect source
616                 if (valid) {
617                         out_map.unset (s->dt, s->id);
618                 }
619                 // disconnect other outputs
620                 _ignore_updates = true;
621                 for (uint32_t n = 0; n < _n_plugins; ++n) {
622                         if (n == pc) {
623                                 continue;
624                         }
625                         ChanMapping n_out_map (_pi->output_map (n));
626                         idx = n_out_map.get_src (s->dt, o->id, &valid);
627                         if (valid) {
628                                 n_out_map.unset (s->dt, idx);
629                                 _pi->set_output_map (n, n_out_map);
630                         }
631                 }
632                 _ignore_updates = false;
633                 idx = out_map.get_src (s->dt, o->id, &valid);
634                 if (valid) {
635                         out_map.unset (s->dt, idx);
636                 }
637                 // connect
638                 out_map.set (s->dt, s->id, o->id);
639                 _pi->set_output_map (pc, out_map);
640         }
641 }
642
643 void
644 PluginPinDialog::reset_configuration ()
645 {
646         _route ()->reset_plugin_insert (_pi);
647 }
648
649 void
650 PluginPinDialog::reset_mapping ()
651 {
652         _pi->reset_map ();
653 }
654
655 void
656 PluginPinDialog::add_remove_plugin_clicked (bool add)
657 {
658         ChanCount out = _out;
659         assert (add || _n_plugins > 0);
660         _route ()->customize_plugin_insert (_pi, _n_plugins + (add ? 1 : -1),  out);
661 }
662
663 void
664 PluginPinDialog::add_remove_port_clicked (bool add, ARDOUR::DataType dt)
665 {
666         ChanCount out = _out;
667         assert (add || out.get (dt) > 0);
668         out.set (dt, out.get (dt) + (add ? 1 : -1));
669         _route ()->customize_plugin_insert (_pi, _n_plugins, out);
670 }