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"
37 PortMatrixRowLabels::PortMatrixRowLabels (PortMatrix* m, PortMatrixBody* b, PortMatrixColumnLabels& cols)
38 : PortMatrixLabels (m, b)
39 , _column_labels (cols)
45 PortMatrixRowLabels::compute_dimensions ()
47 cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
48 cairo_t* cr = cairo_create (surface);
50 _longest_port_name = 0;
51 _longest_bundle_name = 0;
53 /* Compute maximum dimensions using all port groups, so that we allow for the largest and hence
54 we can change between visible groups without the size of the labels jumping around.
57 for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
59 PortGroup::BundleList const r = (*i)->bundles ();
60 for (PortGroup::BundleList::const_iterator j = r.begin(); j != r.end(); ++j) {
62 for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
64 if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
68 cairo_text_extents_t ext;
69 cairo_text_extents (cr, (*j)->bundle->channel_name(k).c_str(), &ext);
70 if (ext.width > _longest_port_name) {
71 _longest_port_name = ext.width;
75 cairo_text_extents_t ext;
76 cairo_text_extents (cr, (*j)->bundle->name().c_str(), &ext);
77 if (ext.width > _longest_bundle_name) {
78 _longest_bundle_name = ext.width;
84 if (_matrix->visible_rows()) {
85 _height = group_size (_matrix->visible_rows()) * grid_spacing ();
91 cairo_surface_destroy (surface);
93 _width = _longest_bundle_name +
96 if (!_matrix->show_only_bundles()) {
97 _width += _longest_port_name;
98 _width += name_pad() * 2;
101 uint32_t needed_by_columns = _column_labels.dimensions().second * tan (angle());
103 if (_width < needed_by_columns) {
104 _longest_bundle_name += (needed_by_columns - _width);
105 _width = needed_by_columns;
111 PortMatrixRowLabels::render (cairo_t* cr)
115 set_source_rgb (cr, background_colour());
116 cairo_rectangle (cr, 0, 0, _width, _height);
119 /* BUNDLE AND PORT NAMES */
125 PortGroup::BundleList const & bundles = _matrix->visible_rows()->bundles ();
126 for (PortGroup::BundleList::const_iterator i = bundles.begin(); i != bundles.end(); ++i) {
127 render_bundle_name (cr, background_colour (), (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N), 0, y, (*i)->bundle);
129 if (!_matrix->show_only_bundles()) {
130 uint32_t const N = _matrix->count_of_our_type ((*i)->bundle->nchannels());
131 for (uint32_t j = 0; j < N; ++j) {
132 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (M);
133 ARDOUR::BundleChannel bc (
135 (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
138 render_channel_name (cr, background_colour (), c, 0, y, bc);
144 y += grid_spacing ();
156 PortMatrixRowLabels::button_press (double x, double y, GdkEventButton* ev)
158 ARDOUR::BundleChannel w = position_to_channel (y, x, _matrix->visible_rows());
161 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_longest_port_name + name_pad() * 2)) ||
162 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < (_longest_bundle_name + name_pad() * 2))
168 if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
169 _matrix->remove_channel (w);
170 } else if (ev->button == 3) {
171 _matrix->popup_menu (
172 ARDOUR::BundleChannel (),
180 PortMatrixRowLabels::component_to_parent_x (double x) const
182 /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
183 return x + _parent_rectangle.get_x();
187 PortMatrixRowLabels::parent_to_component_x (double x) const
189 /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
190 return x - _parent_rectangle.get_x();
194 PortMatrixRowLabels::component_to_parent_y (double y) const
196 return y - _body->yoffset() + _parent_rectangle.get_y();
200 PortMatrixRowLabels::parent_to_component_y (double y) const
202 return y + _body->yoffset() - _parent_rectangle.get_y();
207 PortMatrixRowLabels::bundle_name_x () const
211 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && !_matrix->show_only_bundles ()) {
212 x = _longest_port_name + name_pad() * 2;
219 PortMatrixRowLabels::port_name_x () const
221 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
222 return _longest_bundle_name + name_pad() * 2;
231 PortMatrixRowLabels::render_bundle_name (
232 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
235 double const x = bundle_name_x ();
237 int const n = _matrix->show_only_bundles() ? 1 : _matrix->count_of_our_type_min_1 (b->nchannels());
238 set_source_rgb (cr, bg_colour);
239 cairo_rectangle (cr, xoff + x, yoff, _longest_bundle_name + name_pad() * 2, grid_spacing() * n);
240 cairo_fill_preserve (cr);
241 set_source_rgb (cr, fg_colour);
242 cairo_set_line_width (cr, label_border_width ());
245 cairo_text_extents_t ext;
246 cairo_text_extents (cr, b->name().c_str(), &ext);
247 double const off = (grid_spacing() - ext.height) / 2;
249 Gdk::Color textcolor;
250 ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, Gtkmm2ext::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
251 set_source_rgb (cr, textcolor);
252 cairo_move_to (cr, xoff + x + name_pad(), yoff + name_pad() + off);
253 cairo_show_text (cr, b->name().c_str());
257 PortMatrixRowLabels::render_channel_name (
258 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
261 set_source_rgb (cr, bg_colour);
262 cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, grid_spacing());
263 cairo_fill_preserve (cr);
264 set_source_rgb (cr, fg_colour);
265 cairo_set_line_width (cr, label_border_width ());
268 if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
270 /* only plot the name if the bundle has more than one channel;
271 the name of a single channel is assumed to be redundant */
273 cairo_text_extents_t ext;
274 cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
275 double const off = (grid_spacing() - ext.height) / 2;
277 Gdk::Color textcolor;
278 ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, Gtkmm2ext::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
279 set_source_rgb (cr, textcolor);
280 cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
281 cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
286 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const &) const
292 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
294 return channel_to_position (bc, _matrix->visible_rows()) * grid_spacing ();
298 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
302 if (_matrix->show_only_bundles()) {
303 _body->queue_draw_area (
304 component_to_parent_x (bundle_name_x()) - 1,
305 component_to_parent_y (channel_y (bc)) - 1,
306 _longest_bundle_name + name_pad() * 2 + 2,
310 _body->queue_draw_area (
311 component_to_parent_x (port_name_x()) - 1,
312 component_to_parent_y (channel_y (bc)) - 1,
313 _longest_port_name + name_pad() * 2 + 2,
322 PortMatrixRowLabels::mouseover_changed (list<PortMatrixNode> const &)
324 list<PortMatrixNode> const m = _body->mouseover ();
325 for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
327 ARDOUR::BundleChannel c = i->column;
328 ARDOUR::BundleChannel r = i->row;
330 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
331 add_channel_highlight (r);
332 } else if (r.bundle) {
333 _body->highlight_associated_channels (_matrix->row_index(), r);
339 PortMatrixRowLabels::motion (double x, double y)
341 ARDOUR::BundleChannel const w = position_to_channel (y, x, _matrix->visible_rows());
343 uint32_t const bw = _longest_bundle_name + 2 * name_pad();
350 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < bw) ||
351 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_width - bw) && x < _width)
355 /* if the mouse is over a bundle name, highlight all channels in the bundle */
357 list<PortMatrixNode> n;
359 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
360 if (!_matrix->should_show (w.bundle->channel_type (i))) {
364 ARDOUR::BundleChannel const bc (w.bundle, i);
365 n.push_back (PortMatrixNode (bc, ARDOUR::BundleChannel ()));
368 _body->set_mouseover (n);
371 } else if (x < _width) {
373 _body->set_mouseover (PortMatrixNode (w, ARDOUR::BundleChannel ()));
381 /* not over any bundle */
382 _body->set_mouseover (PortMatrixNode ());