Remove use of i18n macros in headers. Prevents our gettext.h being included before...
[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 (); ++k) {
66
67                                 cairo_text_extents (
68                                         cr,
69                                         (*j)->bundle->channel_name (k).c_str(),
70                                         &ext
71                                         );
72
73                                 if (ext.width > _longest_channel_name) {
74                                         _longest_channel_name = ext.width;
75                                 }
76                         }
77                 }
78         }
79
80         /* height metrics */
81         cairo_text_extents_t ext;
82         cairo_text_extents (cr, X_("AQRjpy"), &ext);
83         _text_height = ext.height;
84         _descender_height = ext.height + ext.y_bearing;
85
86         /* width of the whole thing */
87         if (_matrix->visible_columns()) {
88                 _width = group_size (_matrix->visible_columns()) * grid_spacing ();
89         } else {
90                 _width = 0;
91         }
92
93         cairo_destroy (cr);
94         gdk_pixmap_unref (pm);
95
96         /* height of the whole thing */
97
98         int a = _longest_bundle_name + 4 * name_pad();
99         if (!_matrix->show_only_bundles()) {
100                 a += _longest_channel_name;
101         }
102
103         _height =  a * sin (angle()) + _text_height * cos (angle());
104         _overhang = _height / tan (angle ());
105         _width += _overhang;
106 }
107
108 double
109 PortMatrixColumnLabels::basic_text_x_pos (int) const
110 {
111         return grid_spacing() / 2 +
112                 _text_height / (2 * sin (angle ()));
113 }
114
115 void
116 PortMatrixColumnLabels::render (cairo_t* cr)
117 {
118         /* BACKGROUND */
119
120         set_source_rgb (cr, background_colour());
121         cairo_rectangle (cr, 0, 0, _width, _height);
122         cairo_fill (cr);
123
124         /* BUNDLE PARALLELOGRAM-TYPE-THING AND NAME */
125
126         double x = 0;
127         int N = 0;
128
129         PortGroup::BundleList const & bundles = _matrix->visible_columns()->bundles ();
130         for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
131
132                 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
133                 render_bundle_name (cr, background_colour (), c, x, 0, (*i)->bundle);
134
135                 if (_matrix->show_only_bundles()) {
136                         x += grid_spacing();
137                 } else {
138                         x += (*i)->bundle->nchannels () * grid_spacing();
139                 }
140                 
141                 ++N;
142         }
143
144         /* PORT NAMES */
145
146         if (!_matrix->show_only_bundles()) {
147                 x = 0;
148                 N = 0;
149
150                 for (PortGroup::BundleList::const_iterator i = bundles.begin (); i != bundles.end(); ++i) {
151
152                         for (uint32_t j = 0; j < (*i)->bundle->nchannels(); ++j) {
153                                 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N);
154                                 render_channel_name (cr, background_colour (), c, x, 0, ARDOUR::BundleChannel ((*i)->bundle, j));
155                                 x += grid_spacing();
156                         }
157                         
158                         ++N;
159                 }
160         }
161 }
162
163 double
164 PortMatrixColumnLabels::component_to_parent_x (double x) const
165 {
166         return x - _body->xoffset() + _parent_rectangle.get_x();
167 }
168
169 double
170 PortMatrixColumnLabels::parent_to_component_x (double x) const
171 {
172         return x + _body->xoffset() - _parent_rectangle.get_x();
173 }
174
175 double
176 PortMatrixColumnLabels::component_to_parent_y (double y) const
177 {
178         /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
179         return y + _parent_rectangle.get_y();
180 }
181
182 double
183 PortMatrixColumnLabels::parent_to_component_y (double y) const
184 {
185         /* Column labels don't scroll vertically, so y conversion does not depend on yoffset */
186         return y - _parent_rectangle.get_y();
187 }
188
189 void
190 PortMatrixColumnLabels::mouseover_changed (list<PortMatrixNode> const &)
191 {
192         list<PortMatrixNode> const m = _body->mouseover ();
193         for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
194
195                 ARDOUR::BundleChannel c = i->column;
196                 ARDOUR::BundleChannel r = i->row;
197                 
198                 if (c.bundle && r.bundle) {
199                         add_channel_highlight (c);
200                 } else if (c.bundle) {
201                         _body->highlight_associated_channels (_matrix->column_index(), c);
202                 }
203         }
204 }
205
206 vector<pair<double, double> >
207 PortMatrixColumnLabels::port_name_shape (double xoff, double yoff) const
208 {
209         vector<pair<double, double> > shape;
210
211         double const lc = _longest_channel_name + name_pad();
212         double const w = grid_spacing();
213
214         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
215
216                 double x_ = xoff + _height / tan (angle()) + w;
217                 double y_ = yoff;
218                 shape.push_back (make_pair (x_, y_));
219                 x_ -= w;
220                 shape.push_back (make_pair (x_, y_));
221                 x_ -= lc * cos (angle());
222                 y_ += lc * sin (angle());
223                 shape.push_back (make_pair (x_, y_));
224                 x_ += w * pow (sin (angle()), 2);
225                 y_ += w * sin (angle()) * cos (angle());
226                 shape.push_back (make_pair (x_, y_));
227
228         } else {
229
230                 double x_ = xoff;
231                 double y_ = yoff + _height;
232                 shape.push_back (make_pair (x_, y_));
233                 x_ += w;
234                 shape.push_back (make_pair (x_, y_));
235                 x_ += lc * cos (angle());
236                 y_ -= lc * sin (angle());
237                 shape.push_back (make_pair (x_, y_));
238                 x_ -= grid_spacing() * pow (sin (angle()), 2);
239                 y_ -= grid_spacing() * sin (angle()) * cos (angle());
240                 shape.push_back (make_pair (x_, y_));
241         }
242
243         return shape;
244 }
245
246 void
247 PortMatrixColumnLabels::render_bundle_name (
248         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
249         )
250 {
251         set_source_rgb (cr, bg_colour);
252
253         double w = 0;
254         if (_matrix->show_only_bundles()) {
255                 w = grid_spacing ();
256         } else {
257                 w = b->nchannels() * grid_spacing();
258         }
259
260         double x_ = xoff;
261
262         uint32_t y = yoff;
263         y += _height;
264
265         double y_ = y;
266         cairo_move_to (cr, x_, y_);
267         x_ += w;
268         cairo_line_to (cr, x_, y_);
269         x_ += _height / tan (angle ());
270         y_ -= _height;
271         cairo_line_to (cr, x_, y_);
272         x_ -= w;
273         cairo_line_to (cr, x_, y_);
274         cairo_line_to (cr, xoff, y);
275         cairo_fill_preserve (cr);
276         set_source_rgb (cr, fg_colour);
277         cairo_set_line_width (cr, label_border_width());
278         cairo_stroke (cr);
279
280         set_source_rgb (cr, text_colour());
281
282         double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
283
284         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
285
286                 double rl = 0;
287                 if (_matrix->show_only_bundles()) {
288                         rl = name_pad();
289                 } else {
290                         rl = 3 * name_pad() + _longest_channel_name;
291                 }
292                 cairo_move_to (
293                         cr,
294                         xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle()),
295                         yoff + _height - q * cos (angle ()) - rl * sin (angle())
296                         );
297
298         } else {
299
300                 cairo_move_to (
301                         cr,
302                         xoff + grid_spacing() - q * sin (angle ()),
303                         yoff + _height - q * cos (angle ())
304                         );
305         }
306
307         cairo_save (cr);
308         cairo_rotate (cr, -angle());
309         cairo_show_text (cr, b->name().c_str());
310         cairo_restore (cr);
311 }
312
313 void
314 PortMatrixColumnLabels::render_channel_name (
315         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const &bc
316         )
317 {
318         vector<pair<double, double> > const shape = port_name_shape (xoff, yoff);
319
320         cairo_move_to (cr, shape[0].first, shape[0].second);
321         for (uint32_t i = 1; i < 4; ++i) {
322                 cairo_line_to (cr, shape[i].first, shape[i].second);
323         }
324         cairo_line_to (cr, shape[0].first, shape[0].second);
325
326         set_source_rgb (cr, bg_colour);
327         cairo_fill_preserve (cr);
328         set_source_rgb (cr, fg_colour);
329         cairo_set_line_width (cr, label_border_width());
330         cairo_stroke (cr);
331
332         set_source_rgb (cr, text_colour());
333
334         double const q = ((grid_spacing() * sin (angle())) - _text_height) / 2 + _descender_height;
335
336         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
337
338                 cairo_move_to (
339                         cr,
340                         xoff + grid_spacing() - q * sin (angle ()),
341                         yoff + _height - q * cos (angle ())
342                         );
343                                 
344
345         } else {
346
347                 double const rl = 3 * name_pad() + _longest_bundle_name;
348                 cairo_move_to (
349                         cr,
350                         xoff + grid_spacing() - q * sin (angle ()) + rl * cos (angle ()),
351                         yoff + _height - q * cos (angle ()) - rl * sin (angle())
352                         );
353         }
354
355         if (bc.bundle->nchannels() > 1) {
356
357                 /* only plot the name if the bundle has more than one channel;
358                    the name of a single channel is assumed to be redundant */
359                 
360                 cairo_save (cr);
361                 cairo_rotate (cr, -angle());
362                 
363                 cairo_show_text (
364                         cr,
365                         bc.bundle->channel_name(bc.channel).c_str()
366                         );
367                 
368                 cairo_restore (cr);
369         }
370 }
371
372 double
373 PortMatrixColumnLabels::channel_x (ARDOUR::BundleChannel const &bc) const
374 {
375         return channel_to_position (bc, _matrix->visible_columns()) * grid_spacing ();
376 }
377
378 double
379 PortMatrixColumnLabels::channel_y (ARDOUR::BundleChannel const &) const
380 {
381         return 0;
382 }
383
384 void
385 PortMatrixColumnLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
386 {
387         if (!bc.bundle) {
388                 return;
389         }
390
391         if (_matrix->show_only_bundles()) {
392
393                 _body->queue_draw_area (
394                         component_to_parent_x (channel_x (bc)) - 1,
395                         component_to_parent_y (0) - 1,
396                         grid_spacing() + _height * tan (angle()) + 2,
397                         _height + 2
398                         );
399
400         } else {
401
402                 double const x = channel_x (bc);
403                 double const lc = _longest_channel_name + name_pad();
404                 double const h = lc * sin (angle ()) + grid_spacing() * sin (angle()) * cos (angle());
405
406                 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
407
408                         _body->queue_draw_area (
409                                 component_to_parent_x (x) - 1,
410                                 component_to_parent_y (_height - h) - 1,
411                                 grid_spacing() + lc * cos (angle()) + 2,
412                                 h + 2
413                                 );
414
415                 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
416
417                         double const x_ = x + _height / tan (angle()) - lc * cos (angle());
418
419                         _body->queue_draw_area (
420                                 component_to_parent_x (x_) - 1,
421                                 component_to_parent_y (0) - 1,
422                                 grid_spacing() + lc * cos (angle()) + 2,
423                                 h + 2
424                                 );
425
426                 }
427
428         }
429 }
430
431 ARDOUR::BundleChannel
432 PortMatrixColumnLabels::position_to_channel (double p, double o, boost::shared_ptr<const PortGroup> group) const
433 {
434         uint32_t const cx = p - (_height - o) * tan (angle ());
435         return PortMatrixComponent::position_to_channel (cx, o, group);
436 }
437
438 void
439 PortMatrixColumnLabels::button_press (double x, double y, int b, uint32_t t, guint)
440 {
441         ARDOUR::BundleChannel w = position_to_channel (x, y, _matrix->visible_columns());
442
443         if (
444                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > (_height - _longest_bundle_name * sin (angle ()))) ||
445                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_longest_bundle_name * sin (angle ())))
446                 ) {
447
448                 w.channel = -1;
449         }
450
451         if (b == 3) {
452                 _matrix->popup_menu (
453                         w,
454                         ARDOUR::BundleChannel (),
455                         t
456                         );
457         }
458 }
459
460 void
461 PortMatrixColumnLabels::motion (double x, double y)
462 {
463         ARDOUR::BundleChannel const w = position_to_channel (x, y, _matrix->visible_columns());
464
465         if (w.bundle == 0) {
466                 _body->set_mouseover (PortMatrixNode ());
467                 return;
468         }
469
470         uint32_t const bh = _longest_channel_name * sin (angle ()) + _text_height / cos (angle ());
471
472         if (
473                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && y > bh) ||
474                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && y < (_height - bh))
475                 ) {
476
477                 /* if the mouse is over a bundle name, highlight all channels in the bundle */
478                 
479                 list<PortMatrixNode> n;
480
481                 for (uint32_t i = 0; i < w.bundle->nchannels(); ++i) {
482                         ARDOUR::BundleChannel const bc (w.bundle, i);
483                         n.push_back (PortMatrixNode (ARDOUR::BundleChannel (), bc));
484                 }
485
486                 _body->set_mouseover (n);
487
488         } else {
489         
490                 _body->set_mouseover (PortMatrixNode (ARDOUR::BundleChannel (), w));
491         }
492 }