+ if (_input) {
+ if (can_coalesce ()) {
+ expose_coalesced_input_map (cr, width, height);
+ } else {
+ expose_input_map (cr, width, height);
+ }
+ } else {
+ expose_output_map (cr, width, height);
+ }
+
+ cairo_destroy(cr);
+ return true;
+}
+
+void
+ProcessorEntry::RoutingIcon::expose_coalesced_input_map (cairo_t* cr, const double width, const double height)
+{
+ const uint32_t pc_in = _sinks.n_total();
+ const uint32_t pc_in_midi = _sinks.n_midi();
+
+ for (uint32_t i = 0; i < pc_in; ++i) {
+ const bool is_midi = i < pc_in_midi;
+ bool valid_in;
+ uint32_t pn = is_midi ? i : i - pc_in_midi;
+ DataType dt = is_midi ? DataType::MIDI : DataType::AUDIO;
+ uint32_t idx = _in_map.get (dt, pn, &valid_in);
+ if (!valid_in) {
+ double x = pin_x_pos (i, width, pc_in, 0, is_midi);
+ draw_gnd (cr, x, 0, height, is_midi);
+ continue;
+ }
+ if (idx >= _in.get (dt)) {
+ // side-chain, probably
+ double x = pin_x_pos (i, width, pc_in, 0, is_midi);
+ draw_sidechain (cr, x, 0, height, is_midi);
+ continue;
+ }
+ double c_x0;
+ double c_x1 = pin_x_pos (i, width, pc_in, 0, false);
+
+ if (_fed_by) {
+ bool valid_src;
+ uint32_t src = _f_out_map.get_src (dt, idx, &valid_src);
+ if (!valid_src) {
+ double x = pin_x_pos (i, width, pc_in, 0, false);
+ bool valid_thru;
+ _f_thru_map.get (dt, idx, &valid_thru);
+ if (valid_thru) {
+ draw_thru_src (cr, x, 0, height, is_midi);
+ } else {
+ draw_gnd (cr, x, 0, height, is_midi);
+ }
+ continue;
+ }
+ c_x0 = pin_x_pos (src, width, _f_sources.n_total(), _f_sources.n_midi(), is_midi);
+ } else {
+ c_x0 = pin_x_pos (idx, width, _in.n_total(), _in.n_midi(), is_midi);
+ }
+ draw_connection (cr, c_x0, c_x1, 0, height, is_midi);
+ }
+}
+
+void
+ProcessorEntry::RoutingIcon::expose_input_map (cairo_t* cr, const double width, const double height)
+{
+ const uint32_t n_in = _in.n_total();
+ const uint32_t n_in_midi = _in.n_midi();
+ const uint32_t pc_in = _sinks.n_total();
+ const uint32_t pc_in_midi = _sinks.n_midi();
+
+ // draw inputs to this
+ for (uint32_t i = 0; i < pc_in; ++i) {
+ const bool is_midi = i < pc_in_midi;
+ bool valid_in;
+ uint32_t pn = is_midi ? i : i - pc_in_midi;
+ DataType dt = is_midi ? DataType::MIDI : DataType::AUDIO;
+ uint32_t idx = _in_map.get (dt, pn, &valid_in);
+ // check if it's fed
+ bool valid_src = true;
+ if (valid_in && idx < _in.get (dt) && _fed_by) {
+ bool valid_out;
+ bool valid_thru;
+ _f_out_map.get_src (dt, idx, &valid_out);
+ _f_thru_map.get (dt, idx, &valid_thru);
+ if (!valid_out && !valid_thru) {
+ valid_src = false;
+ }
+ }
+ if (!valid_in || !valid_src) {
+ double x = pin_x_pos (i, width, pc_in, 0, is_midi);
+ draw_gnd (cr, x, 0, height, is_midi);
+ continue;
+ }
+ if (idx >= _in.get (dt)) {
+ // side-chain, probably
+ double x = pin_x_pos (i, width, pc_in, 0, is_midi);
+ draw_sidechain (cr, x, 0, height, is_midi);
+ continue;
+ }
+ double c_x1 = pin_x_pos (i, width, pc_in, 0, false);
+ double c_x0 = pin_x_pos (idx, width, n_in, n_in_midi, is_midi);
+ draw_connection (cr, c_x0, c_x1, 0, height, is_midi);
+ }
+
+ // draw reverse thru
+ for (uint32_t i = 0; i < n_in; ++i) {
+ const bool is_midi = i < n_in_midi;
+ bool valid_thru;
+ uint32_t pn = is_midi ? i : i - n_in_midi;
+ DataType dt = is_midi ? DataType::MIDI : DataType::AUDIO;
+ _thru_map.get_src (dt, pn, &valid_thru);
+ if (!valid_thru) {
+ continue;
+ }
+ double x = pin_x_pos (i, width, n_in, 0, is_midi);
+ draw_thru_sink (cr, x, 0, height, is_midi);
+ }
+}
+
+void
+ProcessorEntry::RoutingIcon::expose_output_map (cairo_t* cr, const double width, const double height)
+{
+ int dh = std::max (4.f, rintf(4.f * UIConfiguration::instance().get_ui_scale()));
+ double ht = _terminal ? height - dh : height;
+
+ // draw outputs of this
+ const uint32_t pc_out = _sources.n_total();
+ const uint32_t pc_out_midi = _sources.n_midi();
+ const uint32_t n_out = _out.n_total();
+ const uint32_t n_out_midi = _out.n_midi();
+
+ for (uint32_t i = 0; i < pc_out; ++i) {
+ const bool is_midi = i < pc_out_midi;
+ bool valid_out;
+ uint32_t pn = is_midi ? i : i - pc_out_midi;
+ DataType dt = is_midi ? DataType::MIDI : DataType::AUDIO;
+ uint32_t idx = _out_map.get (dt, pn, &valid_out);
+ if (!valid_out) {
+ continue;
+ }
+ // skip connections that are not used in the next's input
+ if (_feeding) {
+ bool valid_thru, valid_sink;
+ _i_in_map.get_src (dt, idx, &valid_sink);
+ _i_thru_map.get_src (dt, idx, &valid_thru);
+ if (!valid_thru && !valid_sink) {
+ if (!is_midi || i != 0) { // special case midi-bypass
+ continue;
+ }
+ }
+ }
+ double c_x0 = pin_x_pos (i, width, pc_out, 0, false);
+ double c_x1 = pin_x_pos (idx, width, n_out, n_out_midi, is_midi);
+ draw_connection (cr, c_x0, c_x1, 0, ht, is_midi);
+ }
+
+ for (uint32_t i = 0; i < n_out; ++i) {
+ const bool is_midi = i < n_out_midi;
+ uint32_t pn = is_midi ? i : i - n_out_midi;
+ DataType dt = is_midi ? DataType::MIDI : DataType::AUDIO;
+ double x = pin_x_pos (i, width, n_out, 0, is_midi);
+
+ if (!_terminal) {
+ bool valid_thru_f = false;
+ // skip connections that are not used in the next's input
+ if (_feeding) {
+ bool valid_sink;
+ _i_in_map.get_src (dt, pn, &valid_sink);
+ _i_thru_map.get_src (dt, pn, &valid_thru_f);
+ if (!valid_thru_f && !valid_sink) {
+ if (!is_midi || i != 0) { // special case midi-bypass
+ continue;
+ }
+ }
+ }
+
+ bool valid_src;
+ _out_map.get_src (dt, pn, &valid_src);
+ if (!valid_src) {
+ bool valid_thru;
+ uint32_t idx = _thru_map.get (dt, pn, &valid_thru);
+ if (valid_thru) {
+ if (idx >= _in.get (dt)) {
+ draw_sidechain (cr, x, 0, height, is_midi);
+ } else {
+ draw_thru_src (cr, x, 0, height, is_midi);
+ }
+ } else if (valid_thru_f){
+ // gnd is part of input, unless it's a thru input
+ // (also only true if !coalesced into one small display)
+ draw_gnd (cr, x, 0, height, is_midi);
+ }
+ }
+ } else {
+ // terminal node, add arrows
+ bool valid_src;
+ _out_map.get_src (dt, pn, &valid_src);
+ if (!valid_src) {
+ bool valid_thru;
+ uint32_t idx = _thru_map.get (dt, pn, &valid_thru);
+ if (valid_thru) {
+ if (idx >= _in.get (dt)) {
+ draw_sidechain (cr, x, 0, height - dh, is_midi);
+ } else {
+ draw_thru_src (cr, x, 0, height - dh, is_midi);
+ }
+ } else {
+ draw_gnd (cr, x, 0, height - dh, is_midi);
+ }
+ }
+
+ set_routing_color (cr, is_midi);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to (cr, x, height - dh);
+ cairo_line_to (cr, x, height - 2);
+ cairo_stroke (cr);
+
+ const double ar = dh - 1;
+ cairo_move_to (cr, x - ar, height - ar);
+ cairo_line_to (cr, x , height - .5);
+ cairo_line_to (cr, x + ar, height - ar);
+ cairo_line_to (cr, x , height - ar * .5);
+ cairo_close_path (cr);
+ cairo_fill_preserve (cr);
+ cairo_stroke (cr);
+ }
+ }
+}
+
+ProcessorEntry::PluginInlineDisplay::PluginInlineDisplay (ProcessorEntry& e, boost::shared_ptr<ARDOUR::Plugin> p, uint32_t max_height)
+ : PluginDisplay (p, max_height)
+ , _entry (e)
+ , _scroll (false)
+ , _given_max_height (max_height)
+{
+ std::string postfix = string_compose(_("\n%1+double-click to toggle inline-display"), Keyboard::tertiary_modifier_name ());
+
+ if (_plug->has_editor()) {
+ set_tooltip (*this,
+ string_compose (_("<b>%1</b>\nDouble-click to show GUI.\n%2+double-click to show generic GUI.%3"), e.name (Wide), Keyboard::primary_modifier_name (), postfix));
+ } else {
+ set_tooltip (*this,
+ string_compose (_("<b>%1</b>\nDouble-click to show generic GUI.%2"), e.name (Wide), postfix));
+ }
+}
+
+
+bool
+ProcessorEntry::PluginInlineDisplay::on_button_press_event (GdkEventButton *ev)
+{
+ assert (_entry.processor ());
+
+ boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_entry.processor());
+ // duplicated code :(
+ // consider some tweaks to pass this up to the DnDVBox somehow:
+ // select processor, then call (private)
+ //_entry._parent->processor_button_press_event (ev, &_entry);
+ if (pi && pi->plugin() && pi->plugin()->has_inline_display()
+ && Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)
+ && ev->button == 1
+ && ev->type == GDK_2BUTTON_PRESS) {
+ _entry.toggle_inline_display_visibility ();
+ return true;
+ }
+ else if (Keyboard::is_edit_event (ev) || (ev->button == 1 && ev->type == GDK_2BUTTON_PRESS)) {
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
+ _entry._parent->generic_edit_processor (_entry.processor ());
+ } else {
+ _entry._parent->edit_processor (_entry.processor ());
+ }
+ return true;
+ }
+ return false;
+}
+
+void
+ProcessorEntry::PluginInlineDisplay::on_size_request (Requisition* req)
+{
+ req->width = 56;
+ req->height = _cur_height;
+}
+
+
+void
+ProcessorEntry::PluginInlineDisplay::update_height_alloc (uint32_t inline_height)
+{
+ /* work-around scroll-bar + aspect ratio
+ * show inline-view -> height changes -> scrollbar gets added
+ * -> width changes -> inline-view, fixed aspect ratio -> height changes
+ * -> scroll bar is removed [-> width changes ; repeat ]
+ */
+ uint32_t shm = std::min (_max_height, inline_height);
+ bool sc = false;
+ Gtk::Container* pr = get_parent();
+ for (uint32_t i = 0; i < 4 && pr; ++i) {
+ // VBox, EventBox, ViewPort, ScrolledWindow
+ pr = pr->get_parent();
+ }
+ Gtk::ScrolledWindow* sw = dynamic_cast<Gtk::ScrolledWindow*> (pr);
+ if (sw) {
+ const Gtk::VScrollbar* vsb = sw->get_vscrollbar();
+ sc = vsb && vsb->is_visible();
+ }
+
+ if (shm != _cur_height) {
+ queue_resize ();
+ if (!_scroll && sc) {
+ _max_height = shm;