2 * Copyright (C) 2009-2011 Carl Hetherington <carl@carlh.net>
3 * Copyright (C) 2009-2012 David Robillard <d@drobilla.net>
4 * Copyright (C) 2009-2016 Paul Davis <paul@linuxaudiosystems.com>
5 * Copyright (C) 2014-2017 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 "gtkmm2ext/keyboard.h"
24 #include "ardour/bundle.h"
25 #include "gtkmm2ext/colors.h"
27 #include "port_matrix_column_labels.h"
28 #include "port_matrix.h"
29 #include "port_matrix_body.h"
35 PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrix* m, PortMatrixBody* b)
36 : PortMatrixLabels (m, b),
43 PortMatrixColumnLabels::compute_dimensions ()
45 cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
46 cairo_t* cr = cairo_create (surface);
48 /* width of the longest bundle name */
49 _longest_bundle_name = 0;
50 /* width of the longest channel name */
51 _longest_channel_name = 0;
53 /* Compute 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->columns()->begin(); i != _matrix->columns()->end(); ++i) {
58 PortGroup::BundleList const c = _matrix->columns()->bundles();
59 for (PortGroup::BundleList::const_iterator j = c.begin (); j != c.end(); ++j) {
61 cairo_text_extents_t ext;
62 cairo_text_extents (cr, (*j)->bundle->name().c_str(), &ext);
63 if (ext.width > _longest_bundle_name) {
64 _longest_bundle_name = ext.width;
67 for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
69 if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
75 (*j)->bundle->channel_name (k).c_str(),
79 if (ext.width > _longest_channel_name) {
80 _longest_channel_name = ext.width;
87 cairo_text_extents_t ext;
88 cairo_text_extents (cr, X_("AQRjpy"), &ext);
89 _text_height = ext.height;
90 _descender_height = ext.height + ext.y_bearing;
92 /* width of the whole thing */
93 if (_matrix->visible_columns()) {
94 _width = group_size (_matrix->visible_columns()) * grid_spacing ();
100 cairo_surface_destroy (surface);
102 /* height of the whole thing */
104 int a = _longest_bundle_name + 4 * name_pad();
105 if (!_matrix->show_only_bundles()) {
106 a += _longest_channel_name;
109 _height = a * sin (angle()) + _text_height * cos (angle());
110 _overhang = _height / tan (angle ());
115 PortMatrixColumnLabels::basic_text_x_pos (int) const
117 return grid_spacing() / 2 +
118 _text_height / (2 * sin (angle ()));
122 PortMatrixColumnLabels::render (cairo_t* cr)
126 set_source_rgb (cr, background_colour());
127 cairo_rectangle (cr, 0, 0, _width, _height);
130 /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
135 PortGroup::BundleList const & bundles = _matrix->visible_columns()->bundles ();
136 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
138 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
139 render_bundle_name (cr, background_colour (), c, x, 0, (*i)->bundle);
141 if (_matrix->show_only_bundles()) {
144 x += _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()) * grid_spacing();
152 if (!_matrix->show_only_bundles()) {
156 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
158 uint32_t const C = _matrix->count_of_our_type ((*i)->bundle->nchannels ());
160 for (uint32_t j = 0; j < C; ++j) {
161 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
163 ARDOUR::BundleChannel bc (
165 (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
168 render_channel_name (cr, background_colour (), c, x, 0, bc);
173 x += grid_spacing ();
182 PortMatrixColumnLabels::component_to_parent_x (double x) const
184 return x - _body->xoffset() + _parent_rectangle.get_x();
188 PortMatrixColumnLabels::parent_to_component_x (double x) const
190 return x + _body->xoffset() - _parent_rectangle.get_x();
194 PortMatrixColumnLabels::component_to_parent_y (double y) const
196 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
197 return y + _parent_rectangle.get_y();
201 PortMatrixColumnLabels::parent_to_component_y (double y) const
203 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
204 return y - _parent_rectangle.get_y();
208 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
210 list<PortMatrixNode> const m = _body->mouseover ();
211 for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
213 ARDOUR::BundleChannel c = i->column;
214 ARDOUR::BundleChannel r = i->row;
216 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
217 add_channel_highlight (c);
218 } else if (c.bundle) {
219 _body->highlight_associated_channels (_matrix->column_index(), c);
224 vector<pair<double, double> >
225 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
227 vector<pair<double, double> > shape;
229 double const lc = _longest_channel_name + name_pad();
230 double const w = grid_spacing();
232 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
234 double x_ = xoff + _height / tan (angle()) + w;
236 shape.push_back (make_pair (x_, y_));
238 shape.push_back (make_pair (x_, y_));
239 x_ -= lc * cos (angle());
240 y_ += lc * sin (angle());
241 shape.push_back (make_pair (x_, y_));
242 x_ += w * pow (sin (angle()), 2);
243 y_ += w * sin (angle()) * cos (angle());
244 shape.push_back (make_pair (x_, y_));
249 double y_ = yoff + _height;
250 shape.push_back (make_pair (x_, y_));
252 shape.push_back (make_pair (x_, y_));
253 x_ += lc * cos (angle());
254 y_ -= lc * sin (angle());
255 shape.push_back (make_pair (x_, y_));
256 x_ -= grid_spacing() * pow (sin (angle()), 2);
257 y_ -= grid_spacing() * sin (angle()) * cos (angle());
258 shape.push_back (make_pair (x_, y_));
265 PortMatrixColumnLabels::render_bundle_name (
266 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
269 set_source_rgb (cr, bg_colour);
272 if (_matrix->show_only_bundles()) {
275 w = _matrix->count_of_our_type_min_1 (b->nchannels()) * grid_spacing();
284 cairo_move_to (cr, x_, y_);
286 cairo_line_to (cr, x_, y_);
287 x_ += _height / tan (angle ());
289 cairo_line_to (cr, x_, y_);
291 cairo_line_to (cr, x_, y_);
292 cairo_line_to (cr, xoff, y);
293 cairo_fill_preserve (cr);
294 set_source_rgb (cr, fg_colour);
295 cairo_set_line_width (cr, label_border_width());
298 Gdk::Color textcolor;
299 ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, Gtkmm2ext::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
300 set_source_rgb (cr, textcolor);
302 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
304 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
307 if (_matrix->show_only_bundles()) {
310 rl = 3 * name_pad() + _longest_channel_name;
314 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
315 yoff + _height - q * cos (angle ()) - rl * sin (angle())
322 xoff + grid_spacing() - q * sin (angle ()),
323 yoff + _height - q * cos (angle ())
328 cairo_rotate (cr, -angle());
329 cairo_show_text (cr, b->name().c_str());
334 PortMatrixColumnLabels::render_channel_name (
335 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
338 vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
340 cairo_move_to (cr, shape[0].first, shape[0].second);
341 for (uint32_t i = 1; i < 4; ++i) {
342 cairo_line_to (cr, shape[i].first, shape[i].second);
344 cairo_line_to (cr, shape[0].first, shape[0].second);
346 set_source_rgb (cr, bg_colour);
347 cairo_fill_preserve (cr);
348 set_source_rgb (cr, fg_colour);
349 cairo_set_line_width (cr, label_border_width());
352 Gdk::Color textcolor;
353 ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, Gtkmm2ext::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
354 set_source_rgb (cr, textcolor);
356 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
358 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
362 xoff + grid_spacing() - q * sin (angle ()),
363 yoff + _height - q * cos (angle ())
369 double const rl = 3 * name_pad() + _longest_bundle_name;
372 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
373 yoff + _height - q * cos (angle ()) - rl * sin (angle())
377 if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
379 /* only plot the name if the bundle has more than one channel;
380 the name of a single channel is assumed to be redundant */
383 cairo_rotate (cr, -angle());
387 bc.bundle->channel_name(bc.channel).c_str()
395 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
397 return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
401 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
407 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
413 if (_matrix->show_only_bundles()) {
415 _body->queue_draw_area (
416 component_to_parent_x (channel_x (bc)) - 1,
417 component_to_parent_y (0) - 1,
418 grid_spacing() + _height * tan (angle()) + 2,
424 double const x = channel_x (bc);
425 double const lc = _longest_channel_name + name_pad();
426 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
428 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
430 _body->queue_draw_area (
431 component_to_parent_x (x) - 1,
432 component_to_parent_y (_height - h) - 1,
433 grid_spacing() + lc * cos (angle()) + 2,
437 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
439 double const x_ = x + _height / tan (angle()) - lc * cos (angle());
441 _body->queue_draw_area (
442 component_to_parent_x (x_) - 1,
443 component_to_parent_y (0) - 1,
444 grid_spacing() + lc * cos (angle()) + 2,
453 ARDOUR::BundleChannel
454 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
456 uint32_t const cx = p - (_height - o) * tan (angle ());
457 return PortMatrixComponent::position_to_channel (cx, o, group);
461 PortMatrixColumnLabels::button_press (double x, double y, GdkEventButton* ev)
463 ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
466 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
467 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
473 if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
474 _matrix->remove_channel (w);
475 } else if (ev->button == 3) {
476 _matrix->popup_menu (
478 ARDOUR::BundleChannel (),
485 PortMatrixColumnLabels::motion (double x, double y)
487 ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
490 _body->set_mouseover (PortMatrixNode ());
494 uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
497 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
498 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
501 /* if the mouse is over a bundle name, highlight all channels in the bundle */
503 list<PortMatrixNode> n;
505 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
506 if (!_matrix->should_show (w.bundle->channel_type (i))) {
510 ARDOUR::BundleChannel const bc (w.bundle, i);
511 n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
514 _body->set_mouseover (n);
518 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));