2 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
3 * Copyright (C) 2011 Paul Davis
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.
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.
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.
20 #include <gtkmm/table.h>
21 #include <gtkmm/box.h>
22 #include <gtkmm/label.h>
24 #include "gtkmm2ext/utils.h"
25 #include "gtkmm2ext/rgb_macros.h"
27 #include "plugin_pin_dialog.h"
28 #include "gui_thread.h"
29 #include "ui_config.h"
33 using namespace ARDOUR;
37 using namespace Gtkmm2ext;
39 PluginPinDialog::PluginPinDialog (boost::shared_ptr<ARDOUR::PluginInsert> pi)
40 : ArdourWindow (string_compose (_("Pin Configuration: %1"), pi->name ()))
41 , _strict_io (_("Strict I/O"))
42 , _automatic (_("Automatic"))
43 , _add_plugin (_("+"))
44 , _del_plugin (_("-"))
45 , _add_output_audio (_("+"))
46 , _del_output_audio (_("-"))
47 , _add_output_midi (_("+"))
48 , _del_output_midi (_("-"))
51 , _position_valid (false)
52 , _ignore_updates (false)
54 assert (pi->owner ()); // Route
56 _pi->PluginIoReConfigure.connect (
57 _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context()
60 _pi->PluginMapChanged.connect (
61 _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context()
64 _pi->PluginConfigChanged.connect (
65 _plugin_connections, invalidator (*this), boost::bind (&PluginPinDialog::plugin_reconfigured, this), gui_context()
68 // TODO min. width depending on # of pins.
69 darea.set_size_request(600, 200);
70 _strict_io.set_sensitive (false);
74 Table* t = manage (new Table (4, 3));
75 t->set_border_width (0);
78 l = manage (new Label (_("Track/Bus:"), ALIGN_END));
79 t->attach (*l, 0, 1, r, r + 1);
80 l = manage (new Label ());
81 l->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
82 l->set_width_chars (24);
83 l->set_max_width_chars (24);
84 l->set_text (_route()->name ());
85 t->attach (*l, 1, 3, r, r + 1);
88 l = manage (new Label (_("Plugin:"), ALIGN_END));
89 t->attach (*l, 0, 1, r, r + 1);
90 l = manage (new Label ());
91 l->set_ellipsize (Pango::ELLIPSIZE_MIDDLE);
92 l->set_width_chars (24);
93 l->set_max_width_chars (24);
94 l->set_text (pi->name ());
95 t->attach (*l, 1, 3, r, r + 1);
98 l = manage (new Label (_("Settings:"), ALIGN_END));
99 t->attach (*l, 0, 1, r, r + 1);
100 t->attach (_strict_io, 1, 2, r, r + 1, FILL, SHRINK);
101 t->attach (_automatic, 2, 3, r, r + 1, FILL, SHRINK);
104 l = manage (new Label (_("Instances:"), ALIGN_END));
105 t->attach (*l, 0, 1, r, r + 1);
106 t->attach (_add_plugin, 1, 2, r, r + 1, SHRINK, SHRINK);
107 t->attach (_del_plugin, 2, 3, r, r + 1, SHRINK, SHRINK);
110 l = manage (new Label (_("Audio Out:"), ALIGN_END));
111 t->attach (*l, 0, 1, r, r + 1);
112 t->attach (_add_output_audio, 1, 2, r, r + 1, SHRINK, SHRINK);
113 t->attach (_del_output_audio, 2, 3, r, r + 1, SHRINK, SHRINK);
116 l = manage (new Label (_("Midi Out:"), ALIGN_END));
117 t->attach (*l, 0, 1, r, r + 1);
118 t->attach (_add_output_midi, 1, 2, r, r + 1, SHRINK, SHRINK);
119 t->attach (_del_output_midi, 2, 3, r, r + 1, SHRINK, SHRINK);
122 HBox* hbox = manage (new HBox);
123 hbox->pack_start (darea, true, true);
124 hbox->pack_start (*t, false, true);
126 VBox* vbox = manage (new VBox);
127 vbox->pack_start (*hbox, true, true);
131 plugin_reconfigured ();
133 darea.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
134 darea.signal_size_allocate().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_size_allocate));
135 darea.signal_expose_event().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_expose_event));
136 darea.signal_button_press_event().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_press_event));
137 darea.signal_button_release_event().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_button_release_event));
138 darea.signal_motion_notify_event().connect (sigc::mem_fun (*this, &PluginPinDialog::darea_motion_notify_event));
140 _automatic.signal_clicked.connect (sigc::mem_fun(*this, &PluginPinDialog::automatic_clicked));
141 _add_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun(*this, &PluginPinDialog::add_remove_plugin_clicked), true));
142 _del_plugin.signal_clicked.connect (sigc::bind (sigc::mem_fun(*this, &PluginPinDialog::add_remove_plugin_clicked), false));
144 _add_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun(*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::AUDIO));
145 _del_output_audio.signal_clicked.connect (sigc::bind (sigc::mem_fun(*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::AUDIO));
146 _add_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun(*this, &PluginPinDialog::add_remove_port_clicked), true, DataType::MIDI));
147 _del_output_midi.signal_clicked.connect (sigc::bind (sigc::mem_fun(*this, &PluginPinDialog::add_remove_port_clicked), false, DataType::MIDI));
150 PluginPinDialog::~PluginPinDialog()
155 PluginPinDialog::plugin_reconfigured ()
157 if (_ignore_updates) {
160 _n_plugins = _pi->get_count ();
161 _pi->configured_io (_in, _out);
162 _sinks = _pi->natural_input_streams ();
163 _sources = _pi->natural_output_streams ();
165 _del_plugin.set_sensitive (_n_plugins > 1);
166 _del_output_audio.set_sensitive (_out.n_audio () > 0 && _out.n_total () > 1);
167 _del_output_midi.set_sensitive (_out.n_midi () > 0 && _out.n_total () > 1);
168 _strict_io.set_active (_pi->strict_io());
174 PluginPinDialog::update_elements ()
181 for (uint32_t i = 0; i < _in.n_total (); ++i) {
182 _elements.push_back (CtrlWidget (Input, (i < _in.n_midi () ? DataType::MIDI : DataType::AUDIO), i));
185 for (uint32_t i = 0; i < _out.n_total (); ++i) {
186 _elements.push_back (CtrlWidget (Output, (i < _out.n_midi () ? DataType::MIDI : DataType::AUDIO), i));
189 for (uint32_t n = 0; n < _n_plugins; ++n) {
190 for (uint32_t i = 0; i < _sinks.n_total(); ++i) {
191 _elements.push_back (CtrlWidget (Sink, (i < _sinks.n_midi () ? DataType::MIDI : DataType::AUDIO), i, n));
193 for (uint32_t i = 0; i < _sources.n_total(); ++i) {
194 _elements.push_back (CtrlWidget (Source, (i < _sources.n_midi () ? DataType::MIDI : DataType::AUDIO), i, n));
197 _position_valid = false;
202 PluginPinDialog::update_element_pos ()
205 const double yc = rint (_height * .5);
206 const double bxh2 = 18;
207 const double bxw = rint ((_width * .9) / ((_n_plugins) + .2 * (_n_plugins - 1)));
208 const double bxw2 = rint (bxw * .5);
209 const double y_in = 40;
210 const double y_out = _height - 40;
212 _pin_box_size = rint (max (6., 8. * UIConfiguration::instance().get_ui_scale()));
214 for (CtrlElemList::iterator i = _elements.begin(); i != _elements.end(); ++i) {
217 i->x = rint ((i->e->id + 1) * _width / (1. + _in.n_total ())) - 5.5;
223 i->x = rint ((i->e->id + 1) * _width / (1. + _out.n_total ())) - 5.5;
230 const double x0 = rint ((i->e->ip + .5) * _width / (double)(_n_plugins)) - .5 - bxw2;
231 i->x = rint (x0 + (i->e->id + 1) * bxw / (1. + _sinks.n_total ())) - .5 - _pin_box_size * .5;
232 i->y = yc - bxh2 - _pin_box_size;
233 i->w = _pin_box_size + 1;
234 i->h = _pin_box_size;
239 const double x0 = rint ((i->e->ip + .5) * _width / (double)(_n_plugins)) - .5 - bxw2;
240 i->x = rint (x0 + (i->e->id + 1) * bxw / (1. + _sources.n_total ())) - .5 - _pin_box_size * .5;
242 i->w = _pin_box_size + 1;
243 i->h = _pin_box_size;
252 PluginPinDialog::set_color (cairo_t* cr, bool midi)
254 // see also gtk2_ardour/processor_box.cc
255 static const uint32_t audio_port_color = 0x4A8A0EFF; // Green
256 static const uint32_t midi_port_color = 0x960909FF; //Red
259 cairo_set_source_rgb (cr,
260 UINT_RGBA_R_FLT(midi_port_color),
261 UINT_RGBA_G_FLT(midi_port_color),
262 UINT_RGBA_B_FLT(midi_port_color));
264 cairo_set_source_rgb (cr,
265 UINT_RGBA_R_FLT(audio_port_color),
266 UINT_RGBA_G_FLT(audio_port_color),
267 UINT_RGBA_B_FLT(audio_port_color));
272 PluginPinDialog::draw_io_pin (cairo_t* cr, const CtrlWidget& w)
274 const double dir = (w.e->ct == Input) ? 1 : -1;
276 cairo_move_to (cr, w.x + 5.0, w.y + ((w.e->ct == Input) ? 25 : 0));
277 cairo_rel_line_to (cr, -5., -5. * dir);
278 cairo_rel_line_to (cr, 0., -25. * dir);
279 cairo_rel_line_to (cr, 10., 0.);
280 cairo_rel_line_to (cr, 0., 25. * dir);
281 cairo_close_path (cr);
283 cairo_set_line_width (cr, 1.0);
284 cairo_set_source_rgb (cr, 0, 0, 0);
285 cairo_stroke_preserve (cr);
287 set_color (cr, w.e->dt == DataType::MIDI);
288 if (w.e == _selection || w.e == _actor) {
289 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
290 } else if (w.prelight) {
291 cairo_fill_preserve (cr);
292 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
298 PluginPinDialog::draw_plugin_pin (cairo_t* cr, const CtrlWidget& w)
300 cairo_rectangle (cr, w.x, w.y, w.w, w.h);
301 set_color (cr, w.e->dt == DataType::MIDI);
302 if (w.e == _selection || w.e == _actor) {
303 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0);
304 } else if (w.prelight) {
305 cairo_fill_preserve (cr);
306 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
312 PluginPinDialog::is_valid_port (uint32_t i, uint32_t n_total, uint32_t n_midi, bool midi)
314 if (!midi) { i += n_midi; }
322 PluginPinDialog::pin_x_pos (uint32_t i, double x0, double width, uint32_t n_total, uint32_t n_midi, bool midi)
324 if (!midi) { i += n_midi; }
325 return rint (x0 + (i + 1) * width / (1. + n_total)) - .5;
329 PluginPinDialog::draw_connection (cairo_t* cr, double x0, double x1, double y0, double y1, bool midi, bool dashed)
331 const double bz = 2 * _pin_box_size;
333 cairo_move_to (cr, x0, y0);
334 cairo_curve_to (cr, x0, y0 + bz, x1, y1 - bz, x1, y1);
335 cairo_set_line_width (cr, 3.0);
336 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
337 cairo_set_source_rgb (cr, 1, 0, 0);
339 const double dashes[] = { 5, 7 };
340 cairo_set_dash (cr, dashes, 2, 0);
342 set_color (cr, midi);
345 cairo_set_dash (cr, 0, 0, 0);
350 PluginPinDialog::darea_expose_event (GdkEventExpose* ev)
352 Gtk::Allocation a = darea.get_allocation();
353 double const width = a.get_width();
354 double const height = a.get_height();
356 if (!_position_valid) {
359 update_element_pos ();
360 _position_valid = true;
363 cairo_t* cr = gdk_cairo_create (darea.get_window()->gobj());
364 cairo_rectangle (cr, ev->area.x, ev->area.y, ev->area.width, ev->area.height);
367 Gdk::Color const bg = get_style()->get_bg (STATE_NORMAL);
368 cairo_set_source_rgb (cr, bg.get_red_p (), bg.get_green_p (), bg.get_blue_p ());
369 cairo_rectangle (cr, 0, 0, width, height);
372 /* layout sizes -- TODO consolidate w/ update_element_pos() */
374 const double y_in = 40;
375 const double y_out = height - 40;
378 const double yc = rint (height * .5);
379 const double bxh2 = 18;
380 const double bxw = rint ((width * .9) / ((_n_plugins) + .2 * (_n_plugins - 1)));
381 const double bxw2 = rint (bxw * .5);
383 const uint32_t pc_in = _in.n_total();
384 const uint32_t pc_in_midi = _in.n_midi();
385 const uint32_t pc_out = _out.n_total();
386 const uint32_t pc_out_midi = _out.n_midi();
388 /* draw midi-bypass (behind) */
389 if (_pi->has_midi_bypass ()) {
390 double x0 = rint (width / (1. + pc_in)) - .5;
391 double x1 = rint (width / (1. + pc_out)) - .5;
392 draw_connection (cr, x0, x1, y_in, y_out, true, true);
395 /* plugins & connection wires */
396 for (uint32_t i = 0; i < _n_plugins; ++i) {
397 double x0 = rint ((i + .5) * width / (double)(_n_plugins)) - .5;
400 cairo_set_source_rgb (cr, .3, .3, .3);
401 rounded_rectangle (cr, x0 - bxw2, yc - bxh2, bxw, 2 * bxh2, 7);
404 const ChanMapping::Mappings in_map = _pi->input_map (i).mappings();
405 const ChanMapping::Mappings out_map = _pi->output_map (i).mappings();
407 for (ChanMapping::Mappings::const_iterator t = in_map.begin (); t != in_map.end (); ++t) {
408 bool is_midi = t->first == DataType::MIDI;
409 for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
410 uint32_t pn = (*c).first; // pin
411 uint32_t pb = (*c).second;
412 if (!is_valid_port (pb, pc_in, pc_in_midi, is_midi)) {
415 double c_x0 = pin_x_pos (pb, 0, width, pc_in, pc_in_midi, is_midi);
416 double c_x1 = pin_x_pos (pn, x0 - bxw2, bxw, _sinks.n_total (), _sinks.n_midi (), is_midi);
417 draw_connection (cr, c_x0, c_x1, y_in, yc - bxh2 - _pin_box_size, is_midi);
421 for (ChanMapping::Mappings::const_iterator t = out_map.begin (); t != out_map.end (); ++t) {
422 bool is_midi = t->first == DataType::MIDI;
423 for (ChanMapping::TypeMapping::const_iterator c = (*t).second.begin (); c != (*t).second.end () ; ++c) {
424 uint32_t pn = (*c).first; // pin
425 uint32_t pb = (*c).second;
426 if (!is_valid_port (pb, pc_out, pc_out_midi, is_midi)) {
429 double c_x0 = pin_x_pos (pn, x0 - bxw2, bxw, _sources.n_total (), _sources.n_midi (), is_midi);
430 double c_x1 = pin_x_pos (pb, 0, width, pc_out, pc_out_midi, is_midi);
431 draw_connection (cr, c_x0, c_x1, yc + bxh2 + _pin_box_size, y_out, is_midi);
437 for (CtrlElemList::const_iterator i = _elements.begin(); i != _elements.end(); ++i) {
441 draw_io_pin (cr, *i);
445 draw_plugin_pin (cr, *i);
455 PluginPinDialog::darea_size_allocate (Gtk::Allocation&)
457 _position_valid = false;
461 PluginPinDialog::darea_motion_notify_event (GdkEventMotion* ev)
463 bool changed = false;
465 for (CtrlElemList::iterator i = _elements.begin(); i != _elements.end(); ++i) {
466 if (ev->x >= i->x && ev->x <= i->x + i->w
467 && ev->y >= i->y && ev->y <= i->y + i->h)
469 if (!i->prelight) changed = true;
473 if (i->prelight) changed = true;
484 PluginPinDialog::darea_button_press_event (GdkEventButton* ev)
486 if (ev->type != GDK_BUTTON_PRESS) {
490 switch (ev->button) {
492 if (!_selection || (_selection && !_hover)) {
496 } else if (_selection && _hover && _selection != _hover) {
497 if (_selection->dt != _hover->dt) { _actor.reset (); }
498 else if (_selection->ct == Input && _hover->ct == Sink) { _actor = _hover; }
499 else if (_selection->ct == Sink && _hover->ct == Input) { _actor = _hover; }
500 else if (_selection->ct == Output && _hover->ct == Source) { _actor = _hover; }
501 else if (_selection->ct == Source && _hover->ct == Output) { _actor = _hover; }
519 PluginPinDialog::darea_button_release_event (GdkEventButton* ev)
521 if (_hover == _actor && _actor) {
523 assert (_selection->dt == _actor->dt);
524 if (_selection->ct == Input && _actor->ct == Sink) {
525 handle_input_action (_actor, _selection);
527 else if (_selection->ct == Sink && _actor->ct == Input) {
528 handle_input_action (_selection, _actor);
530 else if (_selection->ct == Output && _actor->ct == Source) {
531 handle_output_action (_actor, _selection);
533 else if (_selection->ct == Source && _actor->ct == Output) {
534 handle_output_action (_selection, _actor);
544 PluginPinDialog::handle_input_action (const CtrlElem &s, const CtrlElem &i)
546 const int pc = s->ip;
548 ChanMapping in_map (_pi->input_map (pc));
549 uint32_t idx = in_map.get (s->dt, s->id, &valid);
551 if (valid && idx == i->id) {
553 in_map.unset (s->dt, s->id);
554 _pi->set_input_map (pc, in_map);
558 in_map.set (s->dt, s->id, i->id);
559 _pi->set_input_map (pc, in_map);
563 in_map.unset (s->dt, s->id);
564 in_map.set (s->dt, s->id, i->id);
565 _pi->set_input_map (pc, in_map);
570 PluginPinDialog::handle_output_action (const CtrlElem &s, const CtrlElem &o)
572 const int pc = s->ip;
574 ChanMapping out_map (_pi->output_map (pc));
575 uint32_t idx = out_map.get (s->dt, s->id, &valid);
577 if (valid && idx == o->id) {
579 out_map.unset (s->dt, s->id);
580 _pi->set_output_map (pc, out_map);
585 out_map.unset (s->dt, s->id);
587 // disconnect other outputs
588 _ignore_updates = true;
589 for (uint32_t n = 0; n < _n_plugins; ++n) {
593 ChanMapping n_out_map (_pi->output_map (n));
594 idx = n_out_map.get_src (s->dt, o->id, &valid);
596 n_out_map.unset (s->dt, idx);
597 _pi->set_output_map (n, n_out_map);
600 _ignore_updates = false;
601 idx = out_map.get_src (s->dt, o->id, &valid);
603 out_map.unset (s->dt, idx);
606 out_map.set (s->dt, s->id, o->id);
607 _pi->set_output_map (pc, out_map);
612 PluginPinDialog::automatic_clicked ()
614 _route()->reset_plugin_insert (_pi);
618 PluginPinDialog::add_remove_plugin_clicked (bool add)
620 ChanCount out = _out;
621 assert (add || _n_plugins > 0);
622 _route()->customize_plugin_insert (_pi, _n_plugins + (add ? 1 : -1), out);
626 PluginPinDialog::add_remove_port_clicked (bool add, ARDOUR::DataType dt)
628 ChanCount out = _out;
629 assert (add || out.get (dt) > 0);
630 out.set (dt, out.get (dt) + (add ? 1 : -1));
631 _route()->customize_plugin_insert (_pi, _n_plugins, out);