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