Fix port matrix dimensions on non-24-bit displays (#4077).
[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         GdkPixmap* pm = gdk_pixmap_new (NULL, 1, 1, gdk_visual_get_depth (gdk_visual_get_system ()));
43         gdk_drawable_set_colormap (pm, gdk_colormap_get_system());
44         cairo_t* cr = gdk_cairo_create (pm);
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         g_object_unref (pm);
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                                 render_channel_name (cr, background_colour (), c, x, 0, ARDOUR::BundleChannel ((*i)->bundle, j));
161                                 x += grid_spacing();
162                         }
163
164                         if (C == 0) {
165                                 x += grid_spacing ();
166                         }
167
168                         ++N;
169                 }
170         }
171 }
172
173 double
174 PortMatrixColumnLabels::component_to_parent_x (double x) const
175 {
176         return x - _body->xoffset() + _parent_rectangle.get_x();
177 }
178
179 double
180 PortMatrixColumnLabels::parent_to_component_x (double x) const
181 {
182         return x + _body->xoffset() - _parent_rectangle.get_x();
183 }
184
185 double
186 PortMatrixColumnLabels::component_to_parent_y (double y) const
187 {
188         /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
189         return y + _parent_rectangle.get_y();
190 }
191
192 double
193 PortMatrixColumnLabels::parent_to_component_y (double y) const
194 {
195         /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
196         return y - _parent_rectangle.get_y();
197 }
198
199 void
200 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
201 {
202         list<PortMatrixNode> const m = _body->mouseover ();
203         for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
204
205                 ARDOUR::BundleChannel c = i->column;
206                 ARDOUR::BundleChannel r = i->row;
207
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);
212                 }
213         }
214 }
215
216 vector<pair<double, double> >
217 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
218 {
219         vector<pair<double, double> > shape;
220
221         double const lc = _longest_channel_name + name_pad();
222         double const w = grid_spacing();
223
224         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
225
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_));
237
238         } else {
239
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_));
251         }
252
253         return shape;
254 }
255
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
259         )
260 {
261         set_source_rgb (cr, bg_colour);
262
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();
268         }
269
270         double x_ = xoff;
271
272         uint32_t y = yoff;
273         y += _height;
274
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);
289
290         set_source_rgb (cr, text_colour());
291
292         double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
293
294         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
295
296                 double rl = 0;
297                 if (_matrix->show_only_bundles()) {
298                         rl = name_pad();
299                 } else {
300                         rl = 3 * name_pad() + _longest_channel_name;
301                 }
302                 cairo_move_to (
303                         cr,
304                         xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
305                         yoff + _height - q * cos (angle ()) - rl * sin (angle())
306                         );
307
308         } else {
309
310                 cairo_move_to (
311                         cr,
312                         xoff + grid_spacing() - q * sin (angle ()),
313                         yoff + _height - q * cos (angle ())
314                         );
315         }
316
317         cairo_save (cr);
318         cairo_rotate (cr, -angle());
319         cairo_show_text (cr, b->name().c_str());
320         cairo_restore (cr);
321 }
322
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
326         )
327 {
328         vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
329
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);
333         }
334         cairo_line_to (cr, shape[0].first, shape[0].second);
335
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);
341
342         set_source_rgb (cr, text_colour());
343
344         double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
345
346         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
347
348                 cairo_move_to (
349                         cr,
350                         xoff + grid_spacing() - q * sin (angle ()),
351                         yoff + _height - q * cos (angle ())
352                         );
353
354
355         } else {
356
357                 double const rl = 3 * name_pad() + _longest_bundle_name;
358                 cairo_move_to (
359                         cr,
360                         xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
361                         yoff + _height - q * cos (angle ()) - rl * sin (angle())
362                         );
363         }
364
365         if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
366
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 */
369
370                 cairo_save (cr);
371                 cairo_rotate (cr, -angle());
372
373                 cairo_show_text (
374                         cr,
375                         bc.bundle->channel_name(bc.channel).c_str()
376                         );
377
378                 cairo_restore (cr);
379         }
380 }
381
382 double
383 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
384 {
385         return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
386 }
387
388 double
389 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
390 {
391         return 0;
392 }
393
394 void
395 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
396 {
397         if (!bc.bundle) {
398                 return;
399         }
400
401         if (_matrix->show_only_bundles()) {
402
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
408                         );
409
410         } else {
411
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());
415
416                 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
417
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
423                                 );
424
425                 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
426
427                         double const x_ = x + _height / tan (angle()) - lc * cos (angle());
428
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
434                                 );
435
436                 }
437
438         }
439 }
440
441 ARDOUR::BundleChannel
442 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
443 {
444         uint32_t const cx = p - (_height - o) * tan (angle ());
445         return PortMatrixComponent::position_to_channel (cx, o, group);
446 }
447
448 void
449 PortMatrixColumnLabels::button_press (double x, double y, int b, uint32_t t, guint)
450 {
451         ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
452
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 ())))
456                 ) {
457
458                 w.channel = -1;
459         }
460
461         if (b == 3) {
462                 _matrix->popup_menu (
463                         w,
464                         ARDOUR::BundleChannel (),
465                         t
466                         );
467         }
468 }
469
470 void
471 PortMatrixColumnLabels::motion (double x, double y)
472 {
473         ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
474
475         if (w.bundle == 0) {
476                 _body->set_mouseover (PortMatrixNode ());
477                 return;
478         }
479
480         uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
481
482         if (
483                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
484                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
485                 ) {
486
487                 /* if the mouse is over a bundle name, highlight all channels in the bundle */
488
489                 list<PortMatrixNode> n;
490
491                 uint32_t const N = _matrix->count_of_our_type (w.bundle->nchannels ());
492
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));
496                 }
497
498                 _body->set_mouseover (n);
499
500         } else {
501
502                 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));
503         }
504 }