Delete trailing whitespace
[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, 24);
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                 bool should_show_this_bundle = false;
137                 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
138                         if (_matrix->should_show ((*i)->bundle->channel_type (j))) {
139                                 should_show_this_bundle = true;
140                                 break;
141                         }
142                 }
143
144                 if (should_show_this_bundle) {
145                         Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
146                         render_bundle_name (cr, background_colour (), c, x, 0, (*i)->bundle);
147                 }
148
149                 if (_matrix->show_only_bundles()) {
150                         x += grid_spacing();
151                 } else {
152                         x += _matrix->count_of_our_type ((*i)->bundle->nchannels()) * grid_spacing();
153                 }
154
155                 ++N;
156         }
157
158         /* PORT NAMES */
159
160         if (!_matrix->show_only_bundles()) {
161                 x = 0;
162                 N = 0;
163
164                 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
165
166                         for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
167
168                                 if (!_matrix->should_show ((*i)->bundle->channel_type(j))) {
169                                         continue;
170                                 }
171
172                                 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
173                                 render_channel_name (cr, background_colour (), c, x, 0, ARDOUR::BundleChannel ((*i)->bundle, j));
174                                 x += grid_spacing();
175                         }
176
177                         ++N;
178                 }
179         }
180 }
181
182 double
183 PortMatrixColumnLabels::component_to_parent_x (double x) const
184 {
185         return x - _body->xoffset() + _parent_rectangle.get_x();
186 }
187
188 double
189 PortMatrixColumnLabels::parent_to_component_x (double x) const
190 {
191         return x + _body->xoffset() - _parent_rectangle.get_x();
192 }
193
194 double
195 PortMatrixColumnLabels::component_to_parent_y (double y) const
196 {
197         /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
198         return y + _parent_rectangle.get_y();
199 }
200
201 double
202 PortMatrixColumnLabels::parent_to_component_y (double y) const
203 {
204         /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
205         return y - _parent_rectangle.get_y();
206 }
207
208 void
209 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
210 {
211         list<PortMatrixNode> const m = _body->mouseover ();
212         for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
213
214                 ARDOUR::BundleChannel c = i->column;
215                 ARDOUR::BundleChannel r = i->row;
216
217                 if (c.bundle && r.bundle) {
218                         add_channel_highlight (c);
219                 } else if (c.bundle) {
220                         _body->highlight_associated_channels (_matrix->column_index(), c);
221                 }
222         }
223 }
224
225 vector<pair<double, double> >
226 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
227 {
228         vector<pair<double, double> > shape;
229
230         double const lc = _longest_channel_name + name_pad();
231         double const w = grid_spacing();
232
233         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
234
235                 double x_ = xoff + _height / tan (angle()) + w;
236                 double y_ = yoff;
237                 shape.push_back (make_pair (x_, y_));
238                 x_ -= w;
239                 shape.push_back (make_pair (x_, y_));
240                 x_ -= lc * cos (angle());
241                 y_ += lc * sin (angle());
242                 shape.push_back (make_pair (x_, y_));
243                 x_ += w * pow (sin (angle()), 2);
244                 y_ += w * sin (angle()) * cos (angle());
245                 shape.push_back (make_pair (x_, y_));
246
247         } else {
248
249                 double x_ = xoff;
250                 double y_ = yoff + _height;
251                 shape.push_back (make_pair (x_, y_));
252                 x_ += w;
253                 shape.push_back (make_pair (x_, y_));
254                 x_ += lc * cos (angle());
255                 y_ -= lc * sin (angle());
256                 shape.push_back (make_pair (x_, y_));
257                 x_ -= grid_spacing() * pow (sin (angle()), 2);
258                 y_ -= grid_spacing() * sin (angle()) * cos (angle());
259                 shape.push_back (make_pair (x_, y_));
260         }
261
262         return shape;
263 }
264
265 void
266 PortMatrixColumnLabels::render_bundle_name (
267         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
268         )
269 {
270         set_source_rgb (cr, bg_colour);
271
272         double w = 0;
273         if (_matrix->show_only_bundles()) {
274                 w = grid_spacing ();
275         } else {
276                 w = _matrix->count_of_our_type (b->nchannels()) * grid_spacing();
277         }
278
279         double x_ = xoff;
280
281         uint32_t y = yoff;
282         y += _height;
283
284         double y_ = y;
285         cairo_move_to (cr, x_, y_);
286         x_ += w;
287         cairo_line_to (cr, x_, y_);
288         x_ += _height / tan (angle ());
289         y_ -= _height;
290         cairo_line_to (cr, x_, y_);
291         x_ -= w;
292         cairo_line_to (cr, x_, y_);
293         cairo_line_to (cr, xoff, y);
294         cairo_fill_preserve (cr);
295         set_source_rgb (cr, fg_colour);
296         cairo_set_line_width (cr, label_border_width());
297         cairo_stroke (cr);
298
299         set_source_rgb (cr, text_colour());
300
301         double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
302
303         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
304
305                 double rl = 0;
306                 if (_matrix->show_only_bundles()) {
307                         rl = name_pad();
308                 } else {
309                         rl = 3 * name_pad() + _longest_channel_name;
310                 }
311                 cairo_move_to (
312                         cr,
313                         xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
314                         yoff + _height - q * cos (angle ()) - rl * sin (angle())
315                         );
316
317         } else {
318
319                 cairo_move_to (
320                         cr,
321                         xoff + grid_spacing() - q * sin (angle ()),
322                         yoff + _height - q * cos (angle ())
323                         );
324         }
325
326         cairo_save (cr);
327         cairo_rotate (cr, -angle());
328         cairo_show_text (cr, b->name().c_str());
329         cairo_restore (cr);
330 }
331
332 void
333 PortMatrixColumnLabels::render_channel_name (
334         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
335         )
336 {
337         vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
338
339         cairo_move_to (cr, shape[0].first, shape[0].second);
340         for (uint32_t i = 1; i < 4; ++i) {
341                 cairo_line_to (cr, shape[i].first, shape[i].second);
342         }
343         cairo_line_to (cr, shape[0].first, shape[0].second);
344
345         set_source_rgb (cr, bg_colour);
346         cairo_fill_preserve (cr);
347         set_source_rgb (cr, fg_colour);
348         cairo_set_line_width (cr, label_border_width());
349         cairo_stroke (cr);
350
351         set_source_rgb (cr, text_colour());
352
353         double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
354
355         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
356
357                 cairo_move_to (
358                         cr,
359                         xoff + grid_spacing() - q * sin (angle ()),
360                         yoff + _height - q * cos (angle ())
361                         );
362
363
364         } else {
365
366                 double const rl = 3 * name_pad() + _longest_bundle_name;
367                 cairo_move_to (
368                         cr,
369                         xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
370                         yoff + _height - q * cos (angle ()) - rl * sin (angle())
371                         );
372         }
373
374         if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
375
376                 /* only plot the name if the bundle has more than one channel;
377                    the name of a single channel is assumed to be redundant */
378
379                 cairo_save (cr);
380                 cairo_rotate (cr, -angle());
381
382                 cairo_show_text (
383                         cr,
384                         bc.bundle->channel_name(bc.channel).c_str()
385                         );
386
387                 cairo_restore (cr);
388         }
389 }
390
391 double
392 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
393 {
394         return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
395 }
396
397 double
398 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
399 {
400         return 0;
401 }
402
403 void
404 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
405 {
406         if (!bc.bundle) {
407                 return;
408         }
409
410         if (_matrix->show_only_bundles()) {
411
412                 _body->queue_draw_area (
413                         component_to_parent_x (channel_x (bc)) - 1,
414                         component_to_parent_y (0) - 1,
415                         grid_spacing() + _height * tan (angle()) + 2,
416                         _height + 2
417                         );
418
419         } else {
420
421                 double const x = channel_x (bc);
422                 double const lc = _longest_channel_name + name_pad();
423                 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
424
425                 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
426
427                         _body->queue_draw_area (
428                                 component_to_parent_x (x) - 1,
429                                 component_to_parent_y (_height - h) - 1,
430                                 grid_spacing() + lc * cos (angle()) + 2,
431                                 h + 2
432                                 );
433
434                 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
435
436                         double const x_ = x + _height / tan (angle()) - lc * cos (angle());
437
438                         _body->queue_draw_area (
439                                 component_to_parent_x (x_) - 1,
440                                 component_to_parent_y (0) - 1,
441                                 grid_spacing() + lc * cos (angle()) + 2,
442                                 h + 2
443                                 );
444
445                 }
446
447         }
448 }
449
450 ARDOUR::BundleChannel
451 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
452 {
453         uint32_t const cx = p - (_height - o) * tan (angle ());
454         return PortMatrixComponent::position_to_channel (cx, o, group);
455 }
456
457 void
458 PortMatrixColumnLabels::button_press (double x, double y, int b, uint32_t t, guint)
459 {
460         ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
461
462         if (
463                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
464                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
465                 ) {
466
467                 w.channel = -1;
468         }
469
470         if (b == 3) {
471                 _matrix->popup_menu (
472                         w,
473                         ARDOUR::BundleChannel (),
474                         t
475                         );
476         }
477 }
478
479 void
480 PortMatrixColumnLabels::motion (double x, double y)
481 {
482         ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
483
484         if (w.bundle == 0) {
485                 _body->set_mouseover (PortMatrixNode ());
486                 return;
487         }
488
489         uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
490
491         if (
492                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
493                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
494                 ) {
495
496                 /* if the mouse is over a bundle name, highlight all channels in the bundle */
497
498                 list<PortMatrixNode> n;
499
500                 for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
501
502                         if (!_matrix->should_show (w.bundle->channel_type(i))) {
503                                 continue;
504                         }
505
506                         ARDOUR::BundleChannel const bc (w.bundle, i);
507                         n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
508                 }
509
510                 _body->set_mouseover (n);
511
512         } else {
513
514                 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));
515         }
516 }