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 "ardour/bundle.h"
22 #include "ardour/types.h"
23 #include "port_matrix_column_labels.h"
24 #include "port_matrix.h"
25 #include "port_matrix_body.h"
30 PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrix* m, PortMatrixBody* b)
31 : PortMatrixLabels (m, b),
38 PortMatrixColumnLabels::compute_dimensions ()
40 GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, 24);
41 gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
42 cairo_t* cr = gdk_cairo_create (pm);
44 /* width of the longest bundle name */
45 _longest_bundle_name = 0;
46 /* width of the longest channel name */
47 _longest_channel_name = 0;
48 /* height of highest bit of text (apart from group names) */
50 /* width of the whole thing */
52 _highest_group_name = 0;
54 for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
55 PortGroup::BundleList const c = _matrix->columns()->bundles();
56 for (PortGroup::BundleList::const_iterator j = c.begin (); j != c.end(); ++j) {
58 cairo_text_extents_t ext;
59 cairo_text_extents (cr, j->bundle->name().c_str(), &ext);
60 if (ext.width > _longest_bundle_name) {
61 _longest_bundle_name = ext.width;
64 if (ext.height > _highest_text) {
65 _highest_text = ext.height;
68 for (uint32_t k = 0; k < j->bundle->nchannels (); ++k) {
72 j->bundle->channel_name (k).c_str(),
76 if (ext.width > _longest_channel_name) {
77 _longest_channel_name = ext.width;
80 if (ext.height > _highest_text) {
81 _highest_text = ext.height;
86 _width += group_size (*i) * grid_spacing ();
88 cairo_text_extents_t ext;
89 cairo_text_extents (cr, (*i)->name.c_str(), &ext);
90 if (ext.height > _highest_group_name) {
91 _highest_group_name = ext.height;
96 gdk_pixmap_unref (pm);
98 /* height of the whole thing */
100 int a = _longest_bundle_name + 4 * name_pad();
101 if (!_matrix->show_only_bundles()) {
102 a += _longest_channel_name;
105 double const parallelogram_height = a * sin (angle()) + _highest_text * cos (angle());
107 _height = parallelogram_height + _highest_group_name + 2 * name_pad();
109 _overhang = parallelogram_height / tan (angle ());
114 PortMatrixColumnLabels::basic_text_x_pos (int) const
116 return grid_spacing() / 2 +
117 _highest_text / (2 * sin (angle ()));
121 PortMatrixColumnLabels::render (cairo_t* cr)
125 set_source_rgb (cr, background_colour());
126 cairo_rectangle (cr, 0, 0, _width, _height);
129 /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
134 for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
136 if ((*i)->visible ()) {
138 PortGroup::BundleList const & bundles = (*i)->bundles ();
139 for (PortGroup::BundleList::const_iterator j = bundles.begin (); j != bundles.end(); ++j) {
141 Gdk::Color c = j->has_colour ? j->colour : get_a_bundle_colour (N);
142 render_bundle_name (cr, background_colour (), c, x, 0, j->bundle);
144 if (_matrix->show_only_bundles()) {
147 x += j->bundle->nchannels () * grid_spacing();
155 x += grid_spacing ();
163 if (!_matrix->show_only_bundles()) {
166 for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
168 if ((*i)->visible ()) {
170 PortGroup::BundleList const & bundles = (*i)->bundles ();
171 for (PortGroup::BundleList::const_iterator j = bundles.begin (); j != bundles.end(); ++j) {
173 for (uint32_t k = 0; k < j->bundle->nchannels(); ++k) {
174 Gdk::Color c = j->has_colour ? j->colour : get_a_bundle_colour (N);
175 render_channel_name (cr, background_colour (), c, x, 0, ARDOUR::BundleChannel (j->bundle, k));
184 x += grid_spacing ();
192 PortMatrixColumnLabels::component_to_parent_x (double x) const
194 return x - _body->xoffset() + _parent_rectangle.get_x();
198 PortMatrixColumnLabels::parent_to_component_x (double x) const
200 return x + _body->xoffset() - _parent_rectangle.get_x();
204 PortMatrixColumnLabels::component_to_parent_y (double y) const
206 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
207 return y + _parent_rectangle.get_y();
211 PortMatrixColumnLabels::parent_to_component_y (double y) const
213 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
214 return y - _parent_rectangle.get_y();
218 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
220 list<PortMatrixNode> const m = _body->mouseover ();
221 for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
223 ARDOUR::BundleChannel c = i->column;
224 ARDOUR::BundleChannel r = i->row;
226 if (c.bundle && r.bundle) {
227 add_channel_highlight (c);
228 } else if (c.bundle) {
229 _body->highlight_associated_channels (_matrix->column_index(), c);
234 vector<pair<double, double> >
235 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
237 vector<pair<double, double> > shape;
239 double const lc = _longest_channel_name + name_pad();
240 double const w = grid_spacing();
242 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
244 double x_ = xoff + slanted_height() / tan (angle()) + w;
246 shape.push_back (make_pair (x_, y_));
248 shape.push_back (make_pair (x_, y_));
249 x_ -= lc * cos (angle());
250 y_ += lc * sin (angle());
251 shape.push_back (make_pair (x_, y_));
252 x_ += w * pow (sin (angle()), 2);
253 y_ += w * sin (angle()) * cos (angle());
254 shape.push_back (make_pair (x_, y_));
259 double y_ = yoff + _height;
260 shape.push_back (make_pair (x_, y_));
262 shape.push_back (make_pair (x_, y_));
263 x_ += lc * cos (angle());
264 y_ -= lc * sin (angle());
265 shape.push_back (make_pair (x_, y_));
266 x_ -= grid_spacing() * pow (sin (angle()), 2);
267 y_ -= grid_spacing() * sin (angle()) * cos (angle());
268 shape.push_back (make_pair (x_, y_));
275 PortMatrixColumnLabels::render_bundle_name (
276 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
279 set_source_rgb (cr, bg_colour);
282 if (_matrix->show_only_bundles()) {
285 w = b->nchannels() * grid_spacing();
291 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
294 y += slanted_height();
298 cairo_move_to (cr, x_, y_);
300 cairo_line_to (cr, x_, y_);
301 x_ += slanted_height() / tan (angle ());
302 y_ -= slanted_height();
303 cairo_line_to (cr, x_, y_);
305 cairo_line_to (cr, x_, y_);
306 cairo_line_to (cr, xoff, y);
307 cairo_fill_preserve (cr);
308 set_source_rgb (cr, fg_colour);
309 cairo_set_line_width (cr, label_border_width());
312 set_source_rgb (cr, text_colour());
314 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
317 if (_matrix->show_only_bundles()) {
320 rl = 3 * name_pad() + _longest_channel_name;
324 xoff + basic_text_x_pos (0) + rl * cos (angle()),
325 yoff + _height - rl * sin (angle())
332 xoff + basic_text_x_pos (0) + name_pad() * cos (angle ()),
333 yoff + slanted_height() - name_pad() * sin (angle())
338 cairo_rotate (cr, -angle());
339 cairo_show_text (cr, b->name().c_str());
344 PortMatrixColumnLabels::render_channel_name (
345 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
348 vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
350 cairo_move_to (cr, shape[0].first, shape[0].second);
351 for (uint32_t i = 1; i < 4; ++i) {
352 cairo_line_to (cr, shape[i].first, shape[i].second);
354 cairo_line_to (cr, shape[0].first, shape[0].second);
356 set_source_rgb (cr, bg_colour);
357 cairo_fill_preserve (cr);
358 set_source_rgb (cr, fg_colour);
359 cairo_set_line_width (cr, label_border_width());
362 set_source_rgb (cr, text_colour());
364 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
368 xoff + basic_text_x_pos(bc.channel),
369 yoff + _height - name_pad() * sin (angle())
374 double const rl = 3 * name_pad() + _longest_bundle_name;
377 xoff + basic_text_x_pos(bc.channel) + rl * cos (angle ()),
378 yoff + slanted_height() - rl * sin (angle())
382 if (bc.bundle->nchannels() > 1) {
384 /* only plot the name if the bundle has more than one channel;
385 the name of a single channel is assumed to be redundant */
388 cairo_rotate (cr, -angle());
392 bc.bundle->channel_name(bc.channel).c_str()
400 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
402 return channel_to_position (bc, _matrix->columns()) * grid_spacing ();
406 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
412 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
418 if (_matrix->show_only_bundles()) {
420 _body->queue_draw_area (
421 component_to_parent_x (channel_x (bc)) - 1,
422 component_to_parent_y (0) - 1,
423 grid_spacing() + _height * tan (angle()) + 2,
429 double const x = channel_x (bc);
430 double const lc = _longest_channel_name + name_pad();
431 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
433 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
435 _body->queue_draw_area (
436 component_to_parent_x (x) - 1,
437 component_to_parent_y (_height - h) - 1,
438 grid_spacing() + lc * cos (angle()) + 2,
442 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
444 double const x_ = x + slanted_height() / tan (angle()) - lc * cos (angle());
446 _body->queue_draw_area (
447 component_to_parent_x (x_) - 1,
448 component_to_parent_y (0) - 1,
449 grid_spacing() + lc * cos (angle()) + 2,
458 pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel>
459 PortMatrixColumnLabels::position_to_group_and_channel (double p, double o, PortGroupList const * groups) const
462 uint32_t const gh = _highest_group_name + 2 * name_pad();
464 bool group_name = false;
465 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
466 if (o > (_height - gh)) {
470 cx = p - (_height - gh - o) * tan (angle ());
477 cx = p - (_height - o) * tan (angle ());
481 pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> w = PortMatrixComponent::position_to_group_and_channel (cx, o, groups);
484 w.second.bundle.reset ();
491 PortMatrixColumnLabels::button_press (double x, double y, int b, uint32_t t)
493 pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> gc = position_to_group_and_channel (x, y, _matrix->columns());
496 _matrix->popup_menu (
498 make_pair (boost::shared_ptr<PortGroup> (), ARDOUR::BundleChannel ()),
505 PortMatrixColumnLabels::draw_extra (cairo_t* cr)
507 PortMatrixLabels::draw_extra (cr);
509 /* PORT GROUP NAME */
514 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
515 x = component_to_parent_x (slanted_height() / tan (angle()));
516 y = component_to_parent_y ( _highest_group_name + name_pad());
518 x = component_to_parent_x (0);
519 y = component_to_parent_y (_height - name_pad());
523 for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
525 /* compute width of this group */
527 if (!(*i)->visible()) {
530 if (_matrix->show_only_bundles()) {
531 w = (*i)->bundles().size() * grid_spacing();
533 w = (*i)->total_channels() * grid_spacing();
542 set_source_rgb (cr, get_a_group_colour (g));
543 double const rh = _highest_group_name + 2 * name_pad();
544 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
545 cairo_rectangle (cr, x, component_to_parent_y (0), w, rh);
547 cairo_rectangle (cr, x, component_to_parent_y (_height - rh), w, rh);
551 /* x area available to draw the label in (trying to keep it visible) */
552 double const lx = max (x, double (_parent_rectangle.get_x ()));
553 double const rx = min (x + w, double (_parent_rectangle.get_width()));
555 /* hence what abbreviation (or not) we need for the group name */
556 string const upper = Glib::ustring ((*i)->name).uppercase ();
557 pair<string, double> const display = fit_to_pixels (cr, upper, rx - lx);
560 set_source_rgb (cr, text_colour());
561 cairo_move_to (cr, (lx + rx - display.second) / 2, y);
562 cairo_show_text (cr, display.first.c_str());
571 PortMatrixColumnLabels::motion (double x, double y)
573 pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> const w = position_to_group_and_channel (x, y, _matrix->columns());
575 if (w.second.bundle == 0) {
576 _body->set_mouseover (PortMatrixNode ());
580 uint32_t const bh = _highest_group_name + _longest_channel_name * sin (angle ()) + _highest_text / cos (angle ());
583 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
584 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
587 /* if the mouse is over a bundle name, highlight all channels in the bundle */
589 list<PortMatrixNode> n;
591 for (uint32_t i = 0; i < w.second.bundle->nchannels(); ++i) {
592 ARDOUR::BundleChannel const bc (w.second.bundle, i);
593 n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
596 _body->set_mouseover (n);
600 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w.second));