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 "port_matrix_column_labels.h"
24 #include "port_matrix.h"
25 #include "port_matrix_body.h"
31 PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrix* m, PortMatrixBody* b)
32 : PortMatrixLabels (m, b),
39 PortMatrixColumnLabels::compute_dimensions ()
41 cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
42 cairo_t* cr = cairo_create (surface);
44 /* width of the longest bundle name */
45 _longest_bundle_name = 0;
46 /* width of the longest channel name */
47 _longest_channel_name = 0;
49 /* Compute dimensions using all port groups, so that we allow for the largest and hence
50 we can change between visible groups without the size of the labels jumping around.
53 for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
54 PortGroup::BundleList const c = _matrix->columns()->bundles();
55 for (PortGroup::BundleList::const_iterator j = c.begin (); j != c.end(); ++j) {
57 cairo_text_extents_t ext;
58 cairo_text_extents (cr, (*j)->bundle->name().c_str(), &ext);
59 if (ext.width > _longest_bundle_name) {
60 _longest_bundle_name = ext.width;
63 for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
65 if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
71 (*j)->bundle->channel_name (k).c_str(),
75 if (ext.width > _longest_channel_name) {
76 _longest_channel_name = ext.width;
83 cairo_text_extents_t ext;
84 cairo_text_extents (cr, X_("AQRjpy"), &ext);
85 _text_height = ext.height;
86 _descender_height = ext.height + ext.y_bearing;
88 /* width of the whole thing */
89 if (_matrix->visible_columns()) {
90 _width = group_size (_matrix->visible_columns()) * grid_spacing ();
96 cairo_surface_destroy (surface);
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 _height = a * sin (angle()) + _text_height * cos (angle());
106 _overhang = _height / tan (angle ());
111 PortMatrixColumnLabels::basic_text_x_pos (int) const
113 return grid_spacing() / 2 +
114 _text_height / (2 * sin (angle ()));
118 PortMatrixColumnLabels::render (cairo_t* cr)
122 set_source_rgb (cr, background_colour());
123 cairo_rectangle (cr, 0, 0, _width, _height);
126 /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
131 PortGroup::BundleList const & bundles = _matrix->visible_columns()->bundles ();
132 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
134 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
135 render_bundle_name (cr, background_colour (), c, x, 0, (*i)->bundle);
137 if (_matrix->show_only_bundles()) {
140 x += _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()) * grid_spacing();
148 if (!_matrix->show_only_bundles()) {
152 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
154 uint32_t const C = _matrix->count_of_our_type ((*i)->bundle->nchannels ());
156 for (uint32_t j = 0; j < C; ++j) {
157 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
159 ARDOUR::BundleChannel bc (
161 (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
164 render_channel_name (cr, background_colour (), c, x, 0, bc);
169 x += grid_spacing ();
178 PortMatrixColumnLabels::component_to_parent_x (double x) const
180 return x - _body->xoffset() + _parent_rectangle.get_x();
184 PortMatrixColumnLabels::parent_to_component_x (double x) const
186 return x + _body->xoffset() - _parent_rectangle.get_x();
190 PortMatrixColumnLabels::component_to_parent_y (double y) const
192 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
193 return y + _parent_rectangle.get_y();
197 PortMatrixColumnLabels::parent_to_component_y (double y) const
199 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
200 return y - _parent_rectangle.get_y();
204 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
206 list<PortMatrixNode> const m = _body->mouseover ();
207 for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
209 ARDOUR::BundleChannel c = i->column;
210 ARDOUR::BundleChannel r = i->row;
212 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
213 add_channel_highlight (c);
214 } else if (c.bundle) {
215 _body->highlight_associated_channels (_matrix->column_index(), c);
220 vector<pair<double, double> >
221 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
223 vector<pair<double, double> > shape;
225 double const lc = _longest_channel_name + name_pad();
226 double const w = grid_spacing();
228 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
230 double x_ = xoff + _height / tan (angle()) + w;
232 shape.push_back (make_pair (x_, y_));
234 shape.push_back (make_pair (x_, y_));
235 x_ -= lc * cos (angle());
236 y_ += lc * sin (angle());
237 shape.push_back (make_pair (x_, y_));
238 x_ += w * pow (sin (angle()), 2);
239 y_ += w * sin (angle()) * cos (angle());
240 shape.push_back (make_pair (x_, y_));
245 double y_ = yoff + _height;
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_ -= grid_spacing() * pow (sin (angle()), 2);
253 y_ -= grid_spacing() * sin (angle()) * cos (angle());
254 shape.push_back (make_pair (x_, y_));
261 PortMatrixColumnLabels::render_bundle_name (
262 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
265 set_source_rgb (cr, bg_colour);
268 if (_matrix->show_only_bundles()) {
271 w = _matrix->count_of_our_type_min_1 (b->nchannels()) * grid_spacing();
280 cairo_move_to (cr, x_, y_);
282 cairo_line_to (cr, x_, y_);
283 x_ += _height / tan (angle ());
285 cairo_line_to (cr, x_, y_);
287 cairo_line_to (cr, x_, y_);
288 cairo_line_to (cr, xoff, y);
289 cairo_fill_preserve (cr);
290 set_source_rgb (cr, fg_colour);
291 cairo_set_line_width (cr, label_border_width());
294 set_source_rgb (cr, text_colour());
296 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
298 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
301 if (_matrix->show_only_bundles()) {
304 rl = 3 * name_pad() + _longest_channel_name;
308 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
309 yoff + _height - q * cos (angle ()) - rl * sin (angle())
316 xoff + grid_spacing() - q * sin (angle ()),
317 yoff + _height - q * cos (angle ())
322 cairo_rotate (cr, -angle());
323 cairo_show_text (cr, b->name().c_str());
328 PortMatrixColumnLabels::render_channel_name (
329 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
332 vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
334 cairo_move_to (cr, shape[0].first, shape[0].second);
335 for (uint32_t i = 1; i < 4; ++i) {
336 cairo_line_to (cr, shape[i].first, shape[i].second);
338 cairo_line_to (cr, shape[0].first, shape[0].second);
340 set_source_rgb (cr, bg_colour);
341 cairo_fill_preserve (cr);
342 set_source_rgb (cr, fg_colour);
343 cairo_set_line_width (cr, label_border_width());
346 set_source_rgb (cr, text_colour());
348 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
350 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
354 xoff + grid_spacing() - q * sin (angle ()),
355 yoff + _height - q * cos (angle ())
361 double const rl = 3 * name_pad() + _longest_bundle_name;
364 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
365 yoff + _height - q * cos (angle ()) - rl * sin (angle())
369 if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
371 /* only plot the name if the bundle has more than one channel;
372 the name of a single channel is assumed to be redundant */
375 cairo_rotate (cr, -angle());
379 bc.bundle->channel_name(bc.channel).c_str()
387 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
389 return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
393 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
399 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
405 if (_matrix->show_only_bundles()) {
407 _body->queue_draw_area (
408 component_to_parent_x (channel_x (bc)) - 1,
409 component_to_parent_y (0) - 1,
410 grid_spacing() + _height * tan (angle()) + 2,
416 double const x = channel_x (bc);
417 double const lc = _longest_channel_name + name_pad();
418 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
420 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
422 _body->queue_draw_area (
423 component_to_parent_x (x) - 1,
424 component_to_parent_y (_height - h) - 1,
425 grid_spacing() + lc * cos (angle()) + 2,
429 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
431 double const x_ = x + _height / tan (angle()) - lc * cos (angle());
433 _body->queue_draw_area (
434 component_to_parent_x (x_) - 1,
435 component_to_parent_y (0) - 1,
436 grid_spacing() + lc * cos (angle()) + 2,
445 ARDOUR::BundleChannel
446 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
448 uint32_t const cx = p - (_height - o) * tan (angle ());
449 return PortMatrixComponent::position_to_channel (cx, o, group);
453 PortMatrixColumnLabels::button_press (double x, double y, GdkEventButton* ev)
455 ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
458 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
459 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
465 if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
466 _matrix->remove_channel (w);
467 } else if (ev->button == 3) {
468 _matrix->popup_menu (
470 ARDOUR::BundleChannel (),
477 PortMatrixColumnLabels::motion (double x, double y)
479 ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
482 _body->set_mouseover (PortMatrixNode ());
486 uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
489 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
490 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
493 /* if the mouse is over a bundle name, highlight all channels in the bundle */
495 list<PortMatrixNode> n;
497 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
498 if (!_matrix->should_show (w.bundle->channel_type (i))) {
502 ARDOUR::BundleChannel const bc (w.bundle, i);
503 n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
506 _body->set_mouseover (n);
510 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));