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 "ardour/types.h"
24 #include "port_matrix_column_labels.h"
25 #include "port_matrix.h"
26 #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 set_source_rgb (cr, text_colour());
298 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
300 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
303 if (_matrix->show_only_bundles()) {
306 rl = 3 * name_pad() + _longest_channel_name;
310 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
311 yoff + _height - q * cos (angle ()) - rl * sin (angle())
318 xoff + grid_spacing() - q * sin (angle ()),
319 yoff + _height - q * cos (angle ())
324 cairo_rotate (cr, -angle());
325 cairo_show_text (cr, b->name().c_str());
330 PortMatrixColumnLabels::render_channel_name (
331 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
334 vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
336 cairo_move_to (cr, shape[0].first, shape[0].second);
337 for (uint32_t i = 1; i < 4; ++i) {
338 cairo_line_to (cr, shape[i].first, shape[i].second);
340 cairo_line_to (cr, shape[0].first, shape[0].second);
342 set_source_rgb (cr, bg_colour);
343 cairo_fill_preserve (cr);
344 set_source_rgb (cr, fg_colour);
345 cairo_set_line_width (cr, label_border_width());
348 set_source_rgb (cr, text_colour());
350 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
352 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
356 xoff + grid_spacing() - q * sin (angle ()),
357 yoff + _height - q * cos (angle ())
363 double const rl = 3 * name_pad() + _longest_bundle_name;
366 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
367 yoff + _height - q * cos (angle ()) - rl * sin (angle())
371 if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
373 /* only plot the name if the bundle has more than one channel;
374 the name of a single channel is assumed to be redundant */
377 cairo_rotate (cr, -angle());
381 bc.bundle->channel_name(bc.channel).c_str()
389 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
391 return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
395 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
401 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
407 if (_matrix->show_only_bundles()) {
409 _body->queue_draw_area (
410 component_to_parent_x (channel_x (bc)) - 1,
411 component_to_parent_y (0) - 1,
412 grid_spacing() + _height * tan (angle()) + 2,
418 double const x = channel_x (bc);
419 double const lc = _longest_channel_name + name_pad();
420 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
422 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
424 _body->queue_draw_area (
425 component_to_parent_x (x) - 1,
426 component_to_parent_y (_height - h) - 1,
427 grid_spacing() + lc * cos (angle()) + 2,
431 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
433 double const x_ = x + _height / tan (angle()) - lc * cos (angle());
435 _body->queue_draw_area (
436 component_to_parent_x (x_) - 1,
437 component_to_parent_y (0) - 1,
438 grid_spacing() + lc * cos (angle()) + 2,
447 ARDOUR::BundleChannel
448 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
450 uint32_t const cx = p - (_height - o) * tan (angle ());
451 return PortMatrixComponent::position_to_channel (cx, o, group);
455 PortMatrixColumnLabels::button_press (double x, double y, GdkEventButton* ev)
457 ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
460 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
461 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
467 if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
468 _matrix->remove_channel (w);
469 } else if (ev->button == 3) {
470 _matrix->popup_menu (
472 ARDOUR::BundleChannel (),
479 PortMatrixColumnLabels::motion (double x, double y)
481 ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
484 _body->set_mouseover (PortMatrixNode ());
488 uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
491 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
492 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
495 /* if the mouse is over a bundle name, highlight all channels in the bundle */
497 list<PortMatrixNode> n;
499 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
500 if (!_matrix->should_show (w.bundle->channel_type (i))) {
504 ARDOUR::BundleChannel const bc (w.bundle, i);
505 n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
508 _body->set_mouseover (n);
512 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));