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