2 Copyright (C) 2002-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtkmm2ext/keyboard.h"
22 #include "ardour/bundle.h"
23 #include "canvas/colors.h"
25 #include "port_matrix_column_labels.h"
26 #include "port_matrix.h"
27 #include "port_matrix_body.h"
33 PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrix* m, PortMatrixBody* b)
34 : PortMatrixLabels (m, b),
41 PortMatrixColumnLabels::compute_dimensions ()
43 cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
44 cairo_t* cr = cairo_create (surface);
46 /* width of the longest bundle name */
47 _longest_bundle_name = 0;
48 /* width of the longest channel name */
49 _longest_channel_name = 0;
51 /* Compute dimensions using all port groups, so that we allow for the largest and hence
52 we can change between visible groups without the size of the labels jumping around.
55 for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
56 PortGroup::BundleList const c = _matrix->columns()->bundles();
57 for (PortGroup::BundleList::const_iterator j = c.begin (); j != c.end(); ++j) {
59 cairo_text_extents_t ext;
60 cairo_text_extents (cr, (*j)->bundle->name().c_str(), &ext);
61 if (ext.width > _longest_bundle_name) {
62 _longest_bundle_name = ext.width;
65 for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
67 if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
73 (*j)->bundle->channel_name (k).c_str(),
77 if (ext.width > _longest_channel_name) {
78 _longest_channel_name = ext.width;
85 cairo_text_extents_t ext;
86 cairo_text_extents (cr, X_("AQRjpy"), &ext);
87 _text_height = ext.height;
88 _descender_height = ext.height + ext.y_bearing;
90 /* width of the whole thing */
91 if (_matrix->visible_columns()) {
92 _width = group_size (_matrix->visible_columns()) * grid_spacing ();
98 cairo_surface_destroy (surface);
100 /* height of the whole thing */
102 int a = _longest_bundle_name + 4 * name_pad();
103 if (!_matrix->show_only_bundles()) {
104 a += _longest_channel_name;
107 _height = a * sin (angle()) + _text_height * cos (angle());
108 _overhang = _height / tan (angle ());
113 PortMatrixColumnLabels::basic_text_x_pos (int) const
115 return grid_spacing() / 2 +
116 _text_height / (2 * sin (angle ()));
120 PortMatrixColumnLabels::render (cairo_t* cr)
124 set_source_rgb (cr, background_colour());
125 cairo_rectangle (cr, 0, 0, _width, _height);
128 /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
133 PortGroup::BundleList const & bundles = _matrix->visible_columns()->bundles ();
134 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
136 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
137 render_bundle_name (cr, background_colour (), c, x, 0, (*i)->bundle);
139 if (_matrix->show_only_bundles()) {
142 x += _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()) * grid_spacing();
150 if (!_matrix->show_only_bundles()) {
154 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
156 uint32_t const C = _matrix->count_of_our_type ((*i)->bundle->nchannels ());
158 for (uint32_t j = 0; j < C; ++j) {
159 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
161 ARDOUR::BundleChannel bc (
163 (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
166 render_channel_name (cr, background_colour (), c, x, 0, bc);
171 x += grid_spacing ();
180 PortMatrixColumnLabels::component_to_parent_x (double x) const
182 return x - _body->xoffset() + _parent_rectangle.get_x();
186 PortMatrixColumnLabels::parent_to_component_x (double x) const
188 return x + _body->xoffset() - _parent_rectangle.get_x();
192 PortMatrixColumnLabels::component_to_parent_y (double y) const
194 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
195 return y + _parent_rectangle.get_y();
199 PortMatrixColumnLabels::parent_to_component_y (double y) const
201 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
202 return y - _parent_rectangle.get_y();
206 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
208 list<PortMatrixNode> const m = _body->mouseover ();
209 for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
211 ARDOUR::BundleChannel c = i->column;
212 ARDOUR::BundleChannel r = i->row;
214 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
215 add_channel_highlight (c);
216 } else if (c.bundle) {
217 _body->highlight_associated_channels (_matrix->column_index(), c);
222 vector<pair<double, double> >
223 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
225 vector<pair<double, double> > shape;
227 double const lc = _longest_channel_name + name_pad();
228 double const w = grid_spacing();
230 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
232 double x_ = xoff + _height / tan (angle()) + w;
234 shape.push_back (make_pair (x_, y_));
236 shape.push_back (make_pair (x_, y_));
237 x_ -= lc * cos (angle());
238 y_ += lc * sin (angle());
239 shape.push_back (make_pair (x_, y_));
240 x_ += w * pow (sin (angle()), 2);
241 y_ += w * sin (angle()) * cos (angle());
242 shape.push_back (make_pair (x_, y_));
247 double y_ = yoff + _height;
248 shape.push_back (make_pair (x_, y_));
250 shape.push_back (make_pair (x_, y_));
251 x_ += lc * cos (angle());
252 y_ -= lc * sin (angle());
253 shape.push_back (make_pair (x_, y_));
254 x_ -= grid_spacing() * pow (sin (angle()), 2);
255 y_ -= grid_spacing() * sin (angle()) * cos (angle());
256 shape.push_back (make_pair (x_, y_));
263 PortMatrixColumnLabels::render_bundle_name (
264 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
267 set_source_rgb (cr, bg_colour);
270 if (_matrix->show_only_bundles()) {
273 w = _matrix->count_of_our_type_min_1 (b->nchannels()) * grid_spacing();
282 cairo_move_to (cr, x_, y_);
284 cairo_line_to (cr, x_, y_);
285 x_ += _height / tan (angle ());
287 cairo_line_to (cr, x_, y_);
289 cairo_line_to (cr, x_, y_);
290 cairo_line_to (cr, xoff, y);
291 cairo_fill_preserve (cr);
292 set_source_rgb (cr, fg_colour);
293 cairo_set_line_width (cr, label_border_width());
296 Gdk::Color textcolor;
297 ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, ArdourCanvas::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
298 set_source_rgb (cr, textcolor);
300 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
302 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
305 if (_matrix->show_only_bundles()) {
308 rl = 3 * name_pad() + _longest_channel_name;
312 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
313 yoff + _height - q * cos (angle ()) - rl * sin (angle())
320 xoff + grid_spacing() - q * sin (angle ()),
321 yoff + _height - q * cos (angle ())
326 cairo_rotate (cr, -angle());
327 cairo_show_text (cr, b->name().c_str());
332 PortMatrixColumnLabels::render_channel_name (
333 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
336 vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
338 cairo_move_to (cr, shape[0].first, shape[0].second);
339 for (uint32_t i = 1; i < 4; ++i) {
340 cairo_line_to (cr, shape[i].first, shape[i].second);
342 cairo_line_to (cr, shape[0].first, shape[0].second);
344 set_source_rgb (cr, bg_colour);
345 cairo_fill_preserve (cr);
346 set_source_rgb (cr, fg_colour);
347 cairo_set_line_width (cr, label_border_width());
350 Gdk::Color textcolor;
351 ARDOUR_UI_UTILS::set_color_from_rgba(textcolor, ArdourCanvas::contrasting_text_color(ARDOUR_UI_UTILS::gdk_color_to_rgba(bg_colour)));
352 set_source_rgb (cr, textcolor);
354 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
356 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
360 xoff + grid_spacing() - q * sin (angle ()),
361 yoff + _height - q * cos (angle ())
367 double const rl = 3 * name_pad() + _longest_bundle_name;
370 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
371 yoff + _height - q * cos (angle ()) - rl * sin (angle())
375 if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
377 /* only plot the name if the bundle has more than one channel;
378 the name of a single channel is assumed to be redundant */
381 cairo_rotate (cr, -angle());
385 bc.bundle->channel_name(bc.channel).c_str()
393 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
395 return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
399 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
405 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
411 if (_matrix->show_only_bundles()) {
413 _body->queue_draw_area (
414 component_to_parent_x (channel_x (bc)) - 1,
415 component_to_parent_y (0) - 1,
416 grid_spacing() + _height * tan (angle()) + 2,
422 double const x = channel_x (bc);
423 double const lc = _longest_channel_name + name_pad();
424 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
426 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
428 _body->queue_draw_area (
429 component_to_parent_x (x) - 1,
430 component_to_parent_y (_height - h) - 1,
431 grid_spacing() + lc * cos (angle()) + 2,
435 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
437 double const x_ = x + _height / tan (angle()) - lc * cos (angle());
439 _body->queue_draw_area (
440 component_to_parent_x (x_) - 1,
441 component_to_parent_y (0) - 1,
442 grid_spacing() + lc * cos (angle()) + 2,
451 ARDOUR::BundleChannel
452 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
454 uint32_t const cx = p - (_height - o) * tan (angle ());
455 return PortMatrixComponent::position_to_channel (cx, o, group);
459 PortMatrixColumnLabels::button_press (double x, double y, GdkEventButton* ev)
461 ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
464 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
465 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
471 if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
472 _matrix->remove_channel (w);
473 } else if (ev->button == 3) {
474 _matrix->popup_menu (
476 ARDOUR::BundleChannel (),
483 PortMatrixColumnLabels::motion (double x, double y)
485 ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
488 _body->set_mouseover (PortMatrixNode ());
492 uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
495 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
496 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
499 /* if the mouse is over a bundle name, highlight all channels in the bundle */
501 list<PortMatrixNode> n;
503 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
504 if (!_matrix->should_show (w.bundle->channel_type (i))) {
508 ARDOUR::BundleChannel const bc (w.bundle, i);
509 n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
512 _body->set_mouseover (n);
516 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));