Pass whole GdkEventButton into port matrix button
[ardour.git] / gtk2_ardour / port_matrix_column_labels.cc
1 /*
2     Copyright (C) 2002-2009 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <iostream>
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"
27 #include "utils.h"
28
29 #include "i18n.h"
30
31 using namespace std;
32
33 PortMatrixColumnLabels::PortMatrixColumnLabels (PortMatrix* m, PortMatrixBody* b)
34         : PortMatrixLabels (m, b),
35           _overhang (0)
36 {
37
38 }
39
40 void
41 PortMatrixColumnLabels::compute_dimensions ()
42 {
43         cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
44         cairo_t* cr = cairo_create (surface);
45
46         /* width of the longest bundle name */
47         _longest_bundle_name = 0;
48         /* width of the longest channel name */
49         _longest_channel_name = 0;
50
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.
53         */
54
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) {
58
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;
63                         }
64
65                         for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
66
67                                 if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
68                                         continue;
69                                 }
70
71                                 cairo_text_extents (
72                                         cr,
73                                         (*j)->bundle->channel_name (k).c_str(),
74                                         &ext
75                                         );
76
77                                 if (ext.width > _longest_channel_name) {
78                                         _longest_channel_name = ext.width;
79                                 }
80                         }
81                 }
82         }
83
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;
89
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;
95         }
96
97         cairo_destroy (cr);
98         cairo_surface_destroy (surface);
99
100         /* height of the whole thing */
101
102         int a = _longest_bundle_name + 4 * name_pad();
103         if (!_matrix->show_only_bundles()) {
104                 a += _longest_channel_name;
105         }
106
107         _height =  a * sin (angle()) + _text_height * cos (angle());
108         _overhang = _height / tan (angle ());
109         _width += _overhang;
110 }
111
112 double
113 PortMatrixColumnLabels::basic_text_x_pos (int) const
114 {
115         return grid_spacing() / 2 +
116                 _text_height / (2 * sin (angle ()));
117 }
118
119 void
120 PortMatrixColumnLabels::render (cairo_t* cr)
121 {
122         /* BACKGROUND */
123
124         set_source_rgb (cr, background_colour());
125         cairo_rectangle (cr, 0, 0, _width, _height);
126         cairo_fill (cr);
127
128         /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
129
130         double x = 0;
131         int N = 0;
132
133         PortGroup::BundleList const & bundles = _matrix->visible_columns()->bundles ();
134         for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
135
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);
138
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();
143                 }
144
145                 ++N;
146         }
147
148         /* PORT NAMES */
149
150         if (!_matrix->show_only_bundles()) {
151                 x = 0;
152                 N = 0;
153
154                 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
155
156                         uint32_t const C = _matrix->count_of_our_type ((*i)->bundle->nchannels ());
157
158                         for (uint32_t j = 0; j < C; ++j) {
159                                 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
160
161                                 ARDOUR::BundleChannel bc (
162                                         (*i)->bundle,
163                                         (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
164                                         );
165                                 
166                                 render_channel_name (cr, background_colour (), c, x, 0, bc);
167                                 x += grid_spacing();
168                         }
169
170                         if (C == 0) {
171                                 x += grid_spacing ();
172                         }
173
174                         ++N;
175                 }
176         }
177 }
178
179 double
180 PortMatrixColumnLabels::component_to_parent_x (double x) const
181 {
182         return x - _body->xoffset() + _parent_rectangle.get_x();
183 }
184
185 double
186 PortMatrixColumnLabels::parent_to_component_x (double x) const
187 {
188         return x + _body->xoffset() - _parent_rectangle.get_x();
189 }
190
191 double
192 PortMatrixColumnLabels::component_to_parent_y (double y) const
193 {
194         /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
195         return y + _parent_rectangle.get_y();
196 }
197
198 double
199 PortMatrixColumnLabels::parent_to_component_y (double y) const
200 {
201         /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
202         return y - _parent_rectangle.get_y();
203 }
204
205 void
206 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
207 {
208         list<PortMatrixNode> const m = _body->mouseover ();
209         for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
210
211                 ARDOUR::BundleChannel c = i->column;
212                 ARDOUR::BundleChannel r = i->row;
213
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);
218                 }
219         }
220 }
221
222 vector<pair<double, double> >
223 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
224 {
225         vector<pair<double, double> > shape;
226
227         double const lc = _longest_channel_name + name_pad();
228         double const w = grid_spacing();
229
230         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
231
232                 double x_ = xoff + _height / tan (angle()) + w;
233                 double y_ = yoff;
234                 shape.push_back (make_pair (x_, y_));
235                 x_ -= w;
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_));
243
244         } else {
245
246                 double x_ = xoff;
247                 double y_ = yoff + _height;
248                 shape.push_back (make_pair (x_, y_));
249                 x_ += w;
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_));
257         }
258
259         return shape;
260 }
261
262 void
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
265         )
266 {
267         set_source_rgb (cr, bg_colour);
268
269         double w = 0;
270         if (_matrix->show_only_bundles()) {
271                 w = grid_spacing ();
272         } else {
273                 w = _matrix->count_of_our_type_min_1 (b->nchannels()) * grid_spacing();
274         }
275
276         double x_ = xoff;
277
278         uint32_t y = yoff;
279         y += _height;
280
281         double y_ = y;
282         cairo_move_to (cr, x_, y_);
283         x_ += w;
284         cairo_line_to (cr, x_, y_);
285         x_ += _height / tan (angle ());
286         y_ -= _height;
287         cairo_line_to (cr, x_, y_);
288         x_ -= w;
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());
294         cairo_stroke (cr);
295
296         set_source_rgb (cr, text_colour());
297
298         double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
299
300         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
301
302                 double rl = 0;
303                 if (_matrix->show_only_bundles()) {
304                         rl = name_pad();
305                 } else {
306                         rl = 3 * name_pad() + _longest_channel_name;
307                 }
308                 cairo_move_to (
309                         cr,
310                         xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
311                         yoff + _height - q * cos (angle ()) - rl * sin (angle())
312                         );
313
314         } else {
315
316                 cairo_move_to (
317                         cr,
318                         xoff + grid_spacing() - q * sin (angle ()),
319                         yoff + _height - q * cos (angle ())
320                         );
321         }
322
323         cairo_save (cr);
324         cairo_rotate (cr, -angle());
325         cairo_show_text (cr, b->name().c_str());
326         cairo_restore (cr);
327 }
328
329 void
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
332         )
333 {
334         vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
335
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);
339         }
340         cairo_line_to (cr, shape[0].first, shape[0].second);
341
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());
346         cairo_stroke (cr);
347
348         set_source_rgb (cr, text_colour());
349
350         double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
351
352         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
353
354                 cairo_move_to (
355                         cr,
356                         xoff + grid_spacing() - q * sin (angle ()),
357                         yoff + _height - q * cos (angle ())
358                         );
359
360
361         } else {
362
363                 double const rl = 3 * name_pad() + _longest_bundle_name;
364                 cairo_move_to (
365                         cr,
366                         xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
367                         yoff + _height - q * cos (angle ()) - rl * sin (angle())
368                         );
369         }
370
371         if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
372
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 */
375
376                 cairo_save (cr);
377                 cairo_rotate (cr, -angle());
378
379                 cairo_show_text (
380                         cr,
381                         bc.bundle->channel_name(bc.channel).c_str()
382                         );
383
384                 cairo_restore (cr);
385         }
386 }
387
388 double
389 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
390 {
391         return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
392 }
393
394 double
395 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
396 {
397         return 0;
398 }
399
400 void
401 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
402 {
403         if (!bc.bundle) {
404                 return;
405         }
406
407         if (_matrix->show_only_bundles()) {
408
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,
413                         _height + 2
414                         );
415
416         } else {
417
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());
421
422                 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
423
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,
428                                 h + 2
429                                 );
430
431                 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
432
433                         double const x_ = x + _height / tan (angle()) - lc * cos (angle());
434
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,
439                                 h + 2
440                                 );
441
442                 }
443
444         }
445 }
446
447 ARDOUR::BundleChannel
448 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
449 {
450         uint32_t const cx = p - (_height - o) * tan (angle ());
451         return PortMatrixComponent::position_to_channel (cx, o, group);
452 }
453
454 void
455 PortMatrixColumnLabels::button_press (double x, double y, GdkEventButton* ev)
456 {
457         ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
458
459         if (
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 ())))
462                 ) {
463
464                 w.channel = -1;
465         }
466
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 (
471                         w,
472                         ARDOUR::BundleChannel (),
473                         ev->time
474                         );
475         }
476 }
477
478 void
479 PortMatrixColumnLabels::motion (double x, double y)
480 {
481         ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
482
483         if (w.bundle == 0) {
484                 _body->set_mouseover (PortMatrixNode ());
485                 return;
486         }
487
488         uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
489
490         if (
491                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
492                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
493                 ) {
494
495                 /* if the mouse is over a bundle name, highlight all channels in the bundle */
496
497                 list<PortMatrixNode> n;
498
499                 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
500                         if (!_matrix->should_show (w.bundle->channel_type (i))) {
501                                 continue;
502                         }
503                         
504                         ARDOUR::BundleChannel const bc (w.bundle, i);
505                         n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
506                 }
507
508                 _body->set_mouseover (n);
509
510         } else {
511
512                 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));
513         }
514 }