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