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"
32 PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrix* m, PortMatrixBody* b)
33 : PortMatrixLabels (m, b),
40 PortMatrixColumnLabels::compute_dimensions ()
42 cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
43 cairo_t* cr = cairo_create (surface);
45 /* width of the longest bundle name */
46 _longest_bundle_name = 0;
47 /* width of the longest channel name */
48 _longest_channel_name = 0;
50 /* Compute dimensions using all port groups, so that we allow for the largest and hence
51 we can change between visible groups without the size of the labels jumping around.
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 for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
66 if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
72 (*j)->bundle->channel_name (k).c_str(),
76 if (ext.width > _longest_channel_name) {
77 _longest_channel_name = ext.width;
84 cairo_text_extents_t ext;
85 cairo_text_extents (cr, X_("AQRjpy"), &ext);
86 _text_height = ext.height;
87 _descender_height = ext.height + ext.y_bearing;
89 /* width of the whole thing */
90 if (_matrix->visible_columns()) {
91 _width = group_size (_matrix->visible_columns()) * grid_spacing ();
97 cairo_surface_destroy (surface);
99 /* height of the whole thing */
101 int a = _longest_bundle_name + 4 * name_pad();
102 if (!_matrix->show_only_bundles()) {
103 a += _longest_channel_name;
106 _height = a * sin (angle()) + _text_height * cos (angle());
107 _overhang = _height / tan (angle ());
112 PortMatrixColumnLabels::basic_text_x_pos (int) const
114 return grid_spacing() / 2 +
115 _text_height / (2 * sin (angle ()));
119 PortMatrixColumnLabels::render (cairo_t* cr)
123 set_source_rgb (cr, background_colour());
124 cairo_rectangle (cr, 0, 0, _width, _height);
127 /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
132 PortGroup::BundleList const & bundles = _matrix->visible_columns()->bundles ();
133 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
135 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
136 render_bundle_name (cr, background_colour (), c, x, 0, (*i)->bundle);
138 if (_matrix->show_only_bundles()) {
141 x += _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()) * grid_spacing();
149 if (!_matrix->show_only_bundles()) {
153 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
155 uint32_t const C = _matrix->count_of_our_type ((*i)->bundle->nchannels ());
157 for (uint32_t j = 0; j < C; ++j) {
158 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
160 ARDOUR::BundleChannel bc (
162 (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
165 render_channel_name (cr, background_colour (), c, x, 0, bc);
170 x += grid_spacing ();
179 PortMatrixColumnLabels::component_to_parent_x (double x) const
181 return x - _body->xoffset() + _parent_rectangle.get_x();
185 PortMatrixColumnLabels::parent_to_component_x (double x) const
187 return x + _body->xoffset() - _parent_rectangle.get_x();
191 PortMatrixColumnLabels::component_to_parent_y (double y) const
193 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
194 return y + _parent_rectangle.get_y();
198 PortMatrixColumnLabels::parent_to_component_y (double y) const
200 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
201 return y - _parent_rectangle.get_y();
205 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
207 list<PortMatrixNode> const m = _body->mouseover ();
208 for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
210 ARDOUR::BundleChannel c = i->column;
211 ARDOUR::BundleChannel r = i->row;
213 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
214 add_channel_highlight (c);
215 } else if (c.bundle) {
216 _body->highlight_associated_channels (_matrix->column_index(), c);
221 vector<pair<double, double> >
222 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
224 vector<pair<double, double> > shape;
226 double const lc = _longest_channel_name + name_pad();
227 double const w = grid_spacing();
229 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
231 double x_ = xoff + _height / tan (angle()) + w;
233 shape.push_back (make_pair (x_, y_));
235 shape.push_back (make_pair (x_, y_));
236 x_ -= lc * cos (angle());
237 y_ += lc * sin (angle());
238 shape.push_back (make_pair (x_, y_));
239 x_ += w * pow (sin (angle()), 2);
240 y_ += w * sin (angle()) * cos (angle());
241 shape.push_back (make_pair (x_, y_));
246 double y_ = yoff + _height;
247 shape.push_back (make_pair (x_, y_));
249 shape.push_back (make_pair (x_, y_));
250 x_ += lc * cos (angle());
251 y_ -= lc * sin (angle());
252 shape.push_back (make_pair (x_, y_));
253 x_ -= grid_spacing() * pow (sin (angle()), 2);
254 y_ -= grid_spacing() * sin (angle()) * cos (angle());
255 shape.push_back (make_pair (x_, y_));
262 PortMatrixColumnLabels::render_bundle_name (
263 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
266 set_source_rgb (cr, bg_colour);
269 if (_matrix->show_only_bundles()) {
272 w = _matrix->count_of_our_type_min_1 (b->nchannels()) * grid_spacing();
281 cairo_move_to (cr, x_, y_);
283 cairo_line_to (cr, x_, y_);
284 x_ += _height / tan (angle ());
286 cairo_line_to (cr, x_, y_);
288 cairo_line_to (cr, x_, y_);
289 cairo_line_to (cr, xoff, y);
290 cairo_fill_preserve (cr);
291 set_source_rgb (cr, fg_colour);
292 cairo_set_line_width (cr, label_border_width());
295 set_source_rgb (cr, text_colour());
297 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
299 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
302 if (_matrix->show_only_bundles()) {
305 rl = 3 * name_pad() + _longest_channel_name;
309 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
310 yoff + _height - q * cos (angle ()) - rl * sin (angle())
317 xoff + grid_spacing() - q * sin (angle ()),
318 yoff + _height - q * cos (angle ())
323 cairo_rotate (cr, -angle());
324 cairo_show_text (cr, b->name().c_str());
329 PortMatrixColumnLabels::render_channel_name (
330 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
333 vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
335 cairo_move_to (cr, shape[0].first, shape[0].second);
336 for (uint32_t i = 1; i < 4; ++i) {
337 cairo_line_to (cr, shape[i].first, shape[i].second);
339 cairo_line_to (cr, shape[0].first, shape[0].second);
341 set_source_rgb (cr, bg_colour);
342 cairo_fill_preserve (cr);
343 set_source_rgb (cr, fg_colour);
344 cairo_set_line_width (cr, label_border_width());
347 set_source_rgb (cr, text_colour());
349 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
351 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
355 xoff + grid_spacing() - q * sin (angle ()),
356 yoff + _height - q * cos (angle ())
362 double const rl = 3 * name_pad() + _longest_bundle_name;
365 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
366 yoff + _height - q * cos (angle ()) - rl * sin (angle())
370 if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
372 /* only plot the name if the bundle has more than one channel;
373 the name of a single channel is assumed to be redundant */
376 cairo_rotate (cr, -angle());
380 bc.bundle->channel_name(bc.channel).c_str()
388 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
390 return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
394 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
400 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
406 if (_matrix->show_only_bundles()) {
408 _body->queue_draw_area (
409 component_to_parent_x (channel_x (bc)) - 1,
410 component_to_parent_y (0) - 1,
411 grid_spacing() + _height * tan (angle()) + 2,
417 double const x = channel_x (bc);
418 double const lc = _longest_channel_name + name_pad();
419 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
421 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
423 _body->queue_draw_area (
424 component_to_parent_x (x) - 1,
425 component_to_parent_y (_height - h) - 1,
426 grid_spacing() + lc * cos (angle()) + 2,
430 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
432 double const x_ = x + _height / tan (angle()) - lc * cos (angle());
434 _body->queue_draw_area (
435 component_to_parent_x (x_) - 1,
436 component_to_parent_y (0) - 1,
437 grid_spacing() + lc * cos (angle()) + 2,
446 ARDOUR::BundleChannel
447 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
449 uint32_t const cx = p - (_height - o) * tan (angle ());
450 return PortMatrixComponent::position_to_channel (cx, o, group);
454 PortMatrixColumnLabels::button_press (double x, double y, GdkEventButton* ev)
456 ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
459 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
460 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
466 if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
467 _matrix->remove_channel (w);
468 } else if (ev->button == 3) {
469 _matrix->popup_menu (
471 ARDOUR::BundleChannel (),
478 PortMatrixColumnLabels::motion (double x, double y)
480 ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
483 _body->set_mouseover (PortMatrixNode ());
487 uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
490 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
491 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
494 /* if the mouse is over a bundle name, highlight all channels in the bundle */
496 list<PortMatrixNode> n;
498 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
499 if (!_matrix->should_show (w.bundle->channel_type (i))) {
503 ARDOUR::BundleChannel const bc (w.bundle, i);
504 n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
507 _body->set_mouseover (n);
511 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));