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