2 * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
3 * Copyright (C) 2009-2011 David Robillard <d@drobilla.net>
4 * Copyright (C) 2009-2016 Paul Davis <paul@linuxaudiosystems.com>
5 * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <boost/weak_ptr.hpp>
25 #include "gtkmm2ext/keyboard.h"
26 #include "ardour/bundle.h"
27 #include "gtkmm2ext/colors.h"
29 #include "port_matrix_row_labels.h"
30 #include "port_matrix_column_labels.h"
31 #include "port_matrix.h"
32 #include "port_matrix_body.h"
33 #include "ui_config.h"
38 PortMatrixRowLabels::PortMatrixRowLabels (PortMatrix* m, PortMatrixBody* b, PortMatrixColumnLabels& cols)
39 : PortMatrixLabels (m, b)
40 , _column_labels (cols)
46 PortMatrixRowLabels::compute_dimensions ()
48 cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
49 cairo_t* cr = cairo_create (surface);
50 cairo_set_font_size (cr, UIConfiguration::instance().get_ui_scale() * 10);
52 _longest_port_name = 0;
53 _longest_bundle_name = 0;
55 /* Compute maximum dimensions using all port groups, so that we allow for the largest and hence
56 we can change between visible groups without the size of the labels jumping around.
59 for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
61 PortGroup::BundleList const r = (*i)->bundles ();
62 for (PortGroup::BundleList::const_iterator j = r.begin(); j != r.end(); ++j) {
64 for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
66 if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
70 cairo_text_extents_t ext;
71 cairo_text_extents (cr, (*j)->bundle->channel_name(k).c_str(), &ext);
72 if (ext.width > _longest_port_name) {
73 _longest_port_name = ext.width;
77 cairo_text_extents_t ext;
78 cairo_text_extents (cr, (*j)->bundle->name().c_str(), &ext);
79 if (ext.width > _longest_bundle_name) {
80 _longest_bundle_name = ext.width;
86 if (_matrix->visible_rows()) {
87 _height = group_size (_matrix->visible_rows()) * grid_spacing ();
93 cairo_surface_destroy (surface);
95 _width = _longest_bundle_name +
98 if (!_matrix->show_only_bundles()) {
99 _width += _longest_port_name;
100 _width += name_pad() * 2;
103 uint32_t needed_by_columns = _column_labels.dimensions().second * tan (angle());
105 if (_width < needed_by_columns) {
106 _longest_bundle_name += (needed_by_columns - _width);
107 _width = needed_by_columns;
113 PortMatrixRowLabels::render (cairo_t* cr)
117 set_source_rgb (cr, background_colour());
118 cairo_rectangle (cr, 0, 0, _width, _height);
121 /* BUNDLE AND PORT NAMES */
127 PortGroup::BundleList const & bundles = _matrix->visible_rows()->bundles ();
128 for (PortGroup::BundleList::const_iterator i = bundles.begin(); i != bundles.end(); ++i) {
129 render_bundle_name (cr, background_colour (), (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N), 0, y, (*i)->bundle);
131 if (!_matrix->show_only_bundles()) {
132 uint32_t const N = _matrix->count_of_our_type ((*i)->bundle->nchannels());
133 for (uint32_t j = 0; j < N; ++j) {
134 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (M);
135 ARDOUR::BundleChannel bc (
137 (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
140 render_channel_name (cr, background_colour (), c, 0, y, bc);
146 y += grid_spacing ();
158 PortMatrixRowLabels::button_press (double x, double y, GdkEventButton* ev)
160 ARDOUR::BundleChannel w = position_to_channel (y, x, _matrix->visible_rows());
163 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_longest_port_name + name_pad() * 2)) ||
164 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < (_longest_bundle_name + name_pad() * 2))
170 if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
171 _matrix->remove_channel (w);
172 } else if (ev->button == 3) {
173 _matrix->popup_menu (
174 ARDOUR::BundleChannel (),
182 PortMatrixRowLabels::component_to_parent_x (double x) const
184 /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
185 return x + _parent_rectangle.get_x();
189 PortMatrixRowLabels::parent_to_component_x (double x) const
191 /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
192 return x - _parent_rectangle.get_x();
196 PortMatrixRowLabels::component_to_parent_y (double y) const
198 return y - _body->yoffset() + _parent_rectangle.get_y();
202 PortMatrixRowLabels::parent_to_component_y (double y) const
204 return y + _body->yoffset() - _parent_rectangle.get_y();
209 PortMatrixRowLabels::bundle_name_x () const
213 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && !_matrix->show_only_bundles ()) {
214 x = _longest_port_name + name_pad() * 2;
221 PortMatrixRowLabels::port_name_x () const
223 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
224 return _longest_bundle_name + name_pad() * 2;
233 PortMatrixRowLabels::render_bundle_name (
234 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
237 double const x = bundle_name_x ();
239 int const n = _matrix->show_only_bundles() ? 1 : _matrix->count_of_our_type_min_1 (b->nchannels());
240 set_source_rgb (cr, bg_colour);
241 cairo_rectangle (cr, xoff + x, yoff, _longest_bundle_name + name_pad() * 2, grid_spacing() * n);
242 cairo_fill_preserve (cr);
243 set_source_rgb (cr, fg_colour);
244 cairo_set_line_width (cr, label_border_width ());
247 cairo_text_extents_t ext;
248 cairo_text_extents (cr, b->name().c_str(), &ext);
249 double const off = (grid_spacing() - ext.height) / 2;
251 Gdk::Color textcolor;
252 ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, Gtkmm2ext::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
253 set_source_rgb (cr, textcolor);
254 cairo_move_to (cr, xoff + x + name_pad(), yoff + name_pad() + off);
255 cairo_show_text (cr, b->name().c_str());
259 PortMatrixRowLabels::render_channel_name (
260 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
263 set_source_rgb (cr, bg_colour);
264 cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, grid_spacing());
265 cairo_fill_preserve (cr);
266 set_source_rgb (cr, fg_colour);
267 cairo_set_line_width (cr, label_border_width ());
270 if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
272 /* only plot the name if the bundle has more than one channel;
273 the name of a single channel is assumed to be redundant */
275 cairo_text_extents_t ext;
276 cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
277 double const off = (grid_spacing() - ext.height) / 2;
279 Gdk::Color textcolor;
280 ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, Gtkmm2ext::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
281 set_source_rgb (cr, textcolor);
282 cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
283 cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
288 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const &) const
294 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
296 return channel_to_position (bc, _matrix->visible_rows()) * grid_spacing ();
300 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
304 if (_matrix->show_only_bundles()) {
305 _body->queue_draw_area (
306 component_to_parent_x (bundle_name_x()) - 1,
307 component_to_parent_y (channel_y (bc)) - 1,
308 _longest_bundle_name + name_pad() * 2 + 2,
312 _body->queue_draw_area (
313 component_to_parent_x (port_name_x()) - 1,
314 component_to_parent_y (channel_y (bc)) - 1,
315 _longest_port_name + name_pad() * 2 + 2,
324 PortMatrixRowLabels::mouseover_changed (list<PortMatrixNode> const &)
326 list<PortMatrixNode> const m = _body->mouseover ();
327 for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
329 ARDOUR::BundleChannel c = i->column;
330 ARDOUR::BundleChannel r = i->row;
332 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
333 add_channel_highlight (r);
334 } else if (r.bundle) {
335 _body->highlight_associated_channels (_matrix->row_index(), r);
341 PortMatrixRowLabels::motion (double x, double y)
343 ARDOUR::BundleChannel const w = position_to_channel (y, x, _matrix->visible_rows());
345 uint32_t const bw = _longest_bundle_name + 2 * name_pad();
352 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < bw) ||
353 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_width - bw) && x < _width)
357 /* if the mouse is over a bundle name, highlight all channels in the bundle */
359 list<PortMatrixNode> n;
361 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
362 if (!_matrix->should_show (w.bundle->channel_type (i))) {
366 ARDOUR::BundleChannel const bc (w.bundle, i);
367 n.push_back (PortMatrixNode (bc, ARDOUR::BundleChannel ()));
370 _body->set_mouseover (n);
373 } else if (x < _width) {
375 _body->set_mouseover (PortMatrixNode (w, ARDOUR::BundleChannel ()));
383 /* not over any bundle */
384 _body->set_mouseover (PortMatrixNode ());