various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / gtk2_ardour / port_matrix_column_labels.cc
blob30ff67b9b2bdb7d3d1007bbe92aff12b728ff157
1 /*
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.
20 #include <iostream>
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"
26 #include "utils.h"
28 #include "i18n.h"
30 using namespace std;
32 PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrix* m, PortMatrixBody* b)
33 : PortMatrixLabels (m, b),
34 _overhang (0)
39 void
40 PortMatrixColumnLabels::compute_dimensions ()
42 GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, 24);
43 gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
44 cairo_t* cr = gdk_cairo_create (pm);
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))) {
68 continue;
71 cairo_text_extents (
72 cr,
73 (*j)->bundle->channel_name (k).c_str(),
74 &ext
77 if (ext.width > _longest_channel_name) {
78 _longest_channel_name = ext.width;
84 /* height metrics */
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 ();
93 } else {
94 _width = 0;
97 cairo_destroy (cr);
98 g_object_unref (pm);
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 ());
109 _width += _overhang;
112 double
113 PortMatrixColumnLabels::basic_text_x_pos (int) const
115 return grid_spacing() / 2 +
116 _text_height / (2 * sin (angle ()));
119 void
120 PortMatrixColumnLabels::render (cairo_t* cr)
122 /* BACKGROUND */
124 set_source_rgb (cr, background_colour());
125 cairo_rectangle (cr, 0, 0, _width, _height);
126 cairo_fill (cr);
128 /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
130 double x = 0;
131 int N = 0;
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()) {
140 x += grid_spacing();
141 } else {
142 x += _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()) * grid_spacing();
145 ++N;
148 /* PORT NAMES */
150 if (!_matrix->show_only_bundles()) {
151 x = 0;
152 N = 0;
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);
160 render_channel_name (cr, background_colour (), c, x, 0, ARDOUR::BundleChannel ((*i)->bundle, j));
161 x += grid_spacing();
164 if (C == 0) {
165 x += grid_spacing ();
168 ++N;
173 double
174 PortMatrixColumnLabels::component_to_parent_x (double x) const
176 return x - _body->xoffset() + _parent_rectangle.get_x();
179 double
180 PortMatrixColumnLabels::parent_to_component_x (double x) const
182 return x + _body->xoffset() - _parent_rectangle.get_x();
185 double
186 PortMatrixColumnLabels::component_to_parent_y (double y) const
188 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
189 return y + _parent_rectangle.get_y();
192 double
193 PortMatrixColumnLabels::parent_to_component_y (double y) const
195 /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
196 return y - _parent_rectangle.get_y();
199 void
200 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
202 list<PortMatrixNode> const m = _body->mouseover ();
203 for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
205 ARDOUR::BundleChannel c = i->column;
206 ARDOUR::BundleChannel r = i->row;
208 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
209 add_channel_highlight (c);
210 } else if (c.bundle) {
211 _body->highlight_associated_channels (_matrix->column_index(), c);
216 vector<pair<double, double> >
217 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
219 vector<pair<double, double> > shape;
221 double const lc = _longest_channel_name + name_pad();
222 double const w = grid_spacing();
224 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
226 double x_ = xoff + _height / tan (angle()) + w;
227 double y_ = yoff;
228 shape.push_back (make_pair (x_, y_));
229 x_ -= w;
230 shape.push_back (make_pair (x_, y_));
231 x_ -= lc * cos (angle());
232 y_ += lc * sin (angle());
233 shape.push_back (make_pair (x_, y_));
234 x_ += w * pow (sin (angle()), 2);
235 y_ += w * sin (angle()) * cos (angle());
236 shape.push_back (make_pair (x_, y_));
238 } else {
240 double x_ = xoff;
241 double y_ = yoff + _height;
242 shape.push_back (make_pair (x_, y_));
243 x_ += w;
244 shape.push_back (make_pair (x_, y_));
245 x_ += lc * cos (angle());
246 y_ -= lc * sin (angle());
247 shape.push_back (make_pair (x_, y_));
248 x_ -= grid_spacing() * pow (sin (angle()), 2);
249 y_ -= grid_spacing() * sin (angle()) * cos (angle());
250 shape.push_back (make_pair (x_, y_));
253 return shape;
256 void
257 PortMatrixColumnLabels::render_bundle_name (
258 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
261 set_source_rgb (cr, bg_colour);
263 double w = 0;
264 if (_matrix->show_only_bundles()) {
265 w = grid_spacing ();
266 } else {
267 w = _matrix->count_of_our_type_min_1 (b->nchannels()) * grid_spacing();
270 double x_ = xoff;
272 uint32_t y = yoff;
273 y += _height;
275 double y_ = y;
276 cairo_move_to (cr, x_, y_);
277 x_ += w;
278 cairo_line_to (cr, x_, y_);
279 x_ += _height / tan (angle ());
280 y_ -= _height;
281 cairo_line_to (cr, x_, y_);
282 x_ -= w;
283 cairo_line_to (cr, x_, y_);
284 cairo_line_to (cr, xoff, y);
285 cairo_fill_preserve (cr);
286 set_source_rgb (cr, fg_colour);
287 cairo_set_line_width (cr, label_border_width());
288 cairo_stroke (cr);
290 set_source_rgb (cr, text_colour());
292 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
294 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
296 double rl = 0;
297 if (_matrix->show_only_bundles()) {
298 rl = name_pad();
299 } else {
300 rl = 3 * name_pad() + _longest_channel_name;
302 cairo_move_to (
304 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
305 yoff + _height - q * cos (angle ()) - rl * sin (angle())
308 } else {
310 cairo_move_to (
312 xoff + grid_spacing() - q * sin (angle ()),
313 yoff + _height - q * cos (angle ())
317 cairo_save (cr);
318 cairo_rotate (cr, -angle());
319 cairo_show_text (cr, b->name().c_str());
320 cairo_restore (cr);
323 void
324 PortMatrixColumnLabels::render_channel_name (
325 cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
328 vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
330 cairo_move_to (cr, shape[0].first, shape[0].second);
331 for (uint32_t i = 1; i < 4; ++i) {
332 cairo_line_to (cr, shape[i].first, shape[i].second);
334 cairo_line_to (cr, shape[0].first, shape[0].second);
336 set_source_rgb (cr, bg_colour);
337 cairo_fill_preserve (cr);
338 set_source_rgb (cr, fg_colour);
339 cairo_set_line_width (cr, label_border_width());
340 cairo_stroke (cr);
342 set_source_rgb (cr, text_colour());
344 double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
346 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
348 cairo_move_to (
350 xoff + grid_spacing() - q * sin (angle ()),
351 yoff + _height - q * cos (angle ())
355 } else {
357 double const rl = 3 * name_pad() + _longest_bundle_name;
358 cairo_move_to (
360 xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
361 yoff + _height - q * cos (angle ()) - rl * sin (angle())
365 if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
367 /* only plot the name if the bundle has more than one channel;
368 the name of a single channel is assumed to be redundant */
370 cairo_save (cr);
371 cairo_rotate (cr, -angle());
373 cairo_show_text (
375 bc.bundle->channel_name(bc.channel).c_str()
378 cairo_restore (cr);
382 double
383 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
385 return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
388 double
389 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
391 return 0;
394 void
395 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
397 if (!bc.bundle) {
398 return;
401 if (_matrix->show_only_bundles()) {
403 _body->queue_draw_area (
404 component_to_parent_x (channel_x (bc)) - 1,
405 component_to_parent_y (0) - 1,
406 grid_spacing() + _height * tan (angle()) + 2,
407 _height + 2
410 } else {
412 double const x = channel_x (bc);
413 double const lc = _longest_channel_name + name_pad();
414 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
416 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
418 _body->queue_draw_area (
419 component_to_parent_x (x) - 1,
420 component_to_parent_y (_height - h) - 1,
421 grid_spacing() + lc * cos (angle()) + 2,
422 h + 2
425 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
427 double const x_ = x + _height / tan (angle()) - lc * cos (angle());
429 _body->queue_draw_area (
430 component_to_parent_x (x_) - 1,
431 component_to_parent_y (0) - 1,
432 grid_spacing() + lc * cos (angle()) + 2,
433 h + 2
441 ARDOUR::BundleChannel
442 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
444 uint32_t const cx = p - (_height - o) * tan (angle ());
445 return PortMatrixComponent::position_to_channel (cx, o, group);
448 void
449 PortMatrixColumnLabels::button_press (double x, double y, int b, uint32_t t, guint)
451 ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
453 if (
454 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
455 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
458 w.channel = -1;
461 if (b == 3) {
462 _matrix->popup_menu (
464 ARDOUR::BundleChannel (),
470 void
471 PortMatrixColumnLabels::motion (double x, double y)
473 ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
475 if (w.bundle == 0) {
476 _body->set_mouseover (PortMatrixNode ());
477 return;
480 uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
482 if (
483 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
484 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
487 /* if the mouse is over a bundle name, highlight all channels in the bundle */
489 list<PortMatrixNode> n;
491 uint32_t const N = _matrix->count_of_our_type (w.bundle->nchannels ());
493 for (uint32_t i = 0; i < N; ++i) {
494 ARDOUR::BundleChannel const bc (w.bundle, i);
495 n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
498 _body->set_mouseover (n);
500 } else {
502 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));