move utility functions into a dedicated namespace
[ardour.git] / gtk2_ardour / port_matrix_row_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 <boost/weak_ptr.hpp>
22 #include <cairo.h>
23 #include "gtkmm2ext/keyboard.h"
24 #include "ardour/bundle.h"
25 #include "port_matrix_row_labels.h"
26 #include "port_matrix.h"
27 #include "port_matrix_body.h"
28 #include "i18n.h"
29
30 using namespace std;
31
32 PortMatrixRowLabels::PortMatrixRowLabels (PortMatrix* m, PortMatrixBody* b)
33         : PortMatrixLabels (m, b)
34 {
35
36 }
37
38 void
39 PortMatrixRowLabels::compute_dimensions ()
40 {
41         cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
42         cairo_t* cr = cairo_create (surface);
43
44         _longest_port_name = 0;
45         _longest_bundle_name = 0;
46
47         /* Compute maximum dimensions using all port groups, so that we allow for the largest and hence
48            we can change between visible groups without the size of the labels jumping around.
49         */
50
51         for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
52
53                 PortGroup::BundleList const r = (*i)->bundles ();
54                 for (PortGroup::BundleList::const_iterator j = r.begin(); j != r.end(); ++j) {
55
56                         for (uint32_t k = 0; k < (*j)->bundle->nchannels().n_total(); ++k) {
57
58                                 if (!_matrix->should_show ((*j)->bundle->channel_type(k))) {
59                                         continue;
60                                 }
61
62                                 cairo_text_extents_t ext;
63                                 cairo_text_extents (cr, (*j)->bundle->channel_name(k).c_str(), &ext);
64                                 if (ext.width > _longest_port_name) {
65                                         _longest_port_name = ext.width;
66                                 }
67                         }
68
69                         cairo_text_extents_t ext;
70                         cairo_text_extents (cr, (*j)->bundle->name().c_str(), &ext);
71                         if (ext.width > _longest_bundle_name) {
72                                 _longest_bundle_name = ext.width;
73                         }
74                 }
75         }
76
77
78         if (_matrix->visible_rows()) {
79                 _height = group_size (_matrix->visible_rows()) * grid_spacing ();
80         } else {
81                 _height = 0;
82         }
83
84         cairo_destroy (cr);
85         cairo_surface_destroy (surface);
86
87         _width = _longest_bundle_name +
88                 name_pad() * 2;
89
90         if (!_matrix->show_only_bundles()) {
91                 _width += _longest_port_name;
92                 _width += name_pad() * 2;
93         }
94 }
95
96
97 void
98 PortMatrixRowLabels::render (cairo_t* cr)
99 {
100         /* BACKGROUND */
101
102         set_source_rgb (cr, background_colour());
103         cairo_rectangle (cr, 0, 0, _width, _height);
104         cairo_fill (cr);
105
106         /* BUNDLE AND PORT NAMES */
107
108         double y = 0;
109         int N = 0;
110         int M = 0;
111
112         PortGroup::BundleList const & bundles = _matrix->visible_rows()->bundles ();
113         for (PortGroup::BundleList::const_iterator i = bundles.begin(); i != bundles.end(); ++i) {
114                 render_bundle_name (cr, background_colour (), (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (N), 0, y, (*i)->bundle);
115
116                 if (!_matrix->show_only_bundles()) {
117                         uint32_t const N = _matrix->count_of_our_type ((*i)->bundle->nchannels());
118                         for (uint32_t j = 0; j < N; ++j) {
119                                 Gdk::Color c = (*i)->has_colour ? (*i)->colour : get_a_bundle_colour (M);
120                                 ARDOUR::BundleChannel bc (
121                                         (*i)->bundle,
122                                         (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
123                                         );
124                                 
125                                 render_channel_name (cr, background_colour (), c, 0, y, bc);
126                                 y += grid_spacing();
127                                 ++M;
128                         }
129
130                         if (N == 0) {
131                                 y += grid_spacing ();
132                         }
133                         
134                 } else {
135                         y += grid_spacing();
136                 }
137
138                 ++N;
139         }
140 }
141
142 void
143 PortMatrixRowLabels::button_press (double x, double y, GdkEventButton* ev)
144 {
145         ARDOUR::BundleChannel w = position_to_channel (y, x, _matrix->visible_rows());
146
147         if (
148                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_longest_port_name + name_pad() * 2)) ||
149                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < (_longest_bundle_name + name_pad() * 2))
150
151                 ) {
152                         w.channel = -1;
153         }
154
155         if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
156                 _matrix->remove_channel (w);
157         } else if (ev->button == 3) {
158                 _matrix->popup_menu (
159                         ARDOUR::BundleChannel (),
160                         w,
161                         ev->time
162                         );
163         }
164 }
165
166 double
167 PortMatrixRowLabels::component_to_parent_x (double x) const
168 {
169         /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
170         return x + _parent_rectangle.get_x();
171 }
172
173 double
174 PortMatrixRowLabels::parent_to_component_x (double x) const
175 {
176         /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
177         return x - _parent_rectangle.get_x();
178 }
179
180 double
181 PortMatrixRowLabels::component_to_parent_y (double y) const
182 {
183         return y - _body->yoffset() + _parent_rectangle.get_y();
184 }
185
186 double
187 PortMatrixRowLabels::parent_to_component_y (double y) const
188 {
189         return y + _body->yoffset() - _parent_rectangle.get_y();
190 }
191
192
193 double
194 PortMatrixRowLabels::bundle_name_x () const
195 {
196         double x = 0;
197
198         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && !_matrix->show_only_bundles ()) {
199                 x = _longest_port_name + name_pad() * 2;
200         }
201
202         return x;
203 }
204
205 double
206 PortMatrixRowLabels::port_name_x () const
207 {
208         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
209                 return _longest_bundle_name + name_pad() * 2;
210         } else {
211                 return 0;
212         }
213
214         return 0;
215 }
216
217 void
218 PortMatrixRowLabels::render_bundle_name (
219         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
220         )
221 {
222         double const x = bundle_name_x ();
223
224         int const n = _matrix->show_only_bundles() ? 1 : _matrix->count_of_our_type_min_1 (b->nchannels());
225         set_source_rgb (cr, bg_colour);
226         cairo_rectangle (cr, xoff + x, yoff, _longest_bundle_name + name_pad() * 2, grid_spacing() * n);
227         cairo_fill_preserve (cr);
228         set_source_rgb (cr, fg_colour);
229         cairo_set_line_width (cr, label_border_width ());
230         cairo_stroke (cr);
231
232         cairo_text_extents_t ext;
233         cairo_text_extents (cr, b->name().c_str(), &ext);
234         double const off = (grid_spacing() - ext.height) / 2;
235
236         set_source_rgb (cr, text_colour());
237         cairo_move_to (cr, xoff + x + name_pad(), yoff + name_pad() + off);
238         cairo_show_text (cr, b->name().c_str());
239 }
240
241 void
242 PortMatrixRowLabels::render_channel_name (
243         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
244         )
245 {
246         set_source_rgb (cr, bg_colour);
247         cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, grid_spacing());
248         cairo_fill_preserve (cr);
249         set_source_rgb (cr, fg_colour);
250         cairo_set_line_width (cr, label_border_width ());
251         cairo_stroke (cr);
252
253         if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
254
255                 /* only plot the name if the bundle has more than one channel;
256                    the name of a single channel is assumed to be redundant */
257
258                 cairo_text_extents_t ext;
259                 cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
260                 double const off = (grid_spacing() - ext.height) / 2;
261
262                 set_source_rgb (cr, text_colour());
263                 cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
264                 cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
265         }
266 }
267
268 double
269 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const &) const
270 {
271         return 0;
272 }
273
274 double
275 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
276 {
277         return channel_to_position (bc, _matrix->visible_rows()) * grid_spacing ();
278 }
279
280 void
281 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
282 {
283         if (bc.bundle) {
284
285                 if (_matrix->show_only_bundles()) {
286                         _body->queue_draw_area (
287                                 component_to_parent_x (bundle_name_x()) - 1,
288                                 component_to_parent_y (channel_y (bc)) - 1,
289                                 _longest_bundle_name + name_pad() * 2 + 2,
290                                 grid_spacing() + 2
291                                 );
292                 } else {
293                         _body->queue_draw_area (
294                                 component_to_parent_x (port_name_x()) - 1,
295                                 component_to_parent_y (channel_y (bc)) - 1,
296                                 _longest_port_name + name_pad() * 2 + 2,
297                                 grid_spacing() + 2
298                                 );
299                 }
300         }
301
302 }
303
304 void
305 PortMatrixRowLabels::mouseover_changed (list<PortMatrixNode> const &)
306 {
307         list<PortMatrixNode> const m = _body->mouseover ();
308         for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
309
310                 ARDOUR::BundleChannel c = i->column;
311                 ARDOUR::BundleChannel r = i->row;
312
313                 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
314                         add_channel_highlight (r);
315                 } else if (r.bundle) {
316                         _body->highlight_associated_channels (_matrix->row_index(), r);
317                 }
318         }
319 }
320
321 void
322 PortMatrixRowLabels::motion (double x, double y)
323 {
324         ARDOUR::BundleChannel const w = position_to_channel (y, x, _matrix->visible_rows());
325
326         uint32_t const bw = _longest_bundle_name + 2 * name_pad();
327
328         bool done = false;
329
330         if (w.bundle) {
331
332                 if (
333                         (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < bw) ||
334                         (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_width - bw) && x < _width)
335
336                         ) {
337
338                         /* if the mouse is over a bundle name, highlight all channels in the bundle */
339
340                         list<PortMatrixNode> n;
341
342                         for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
343                                 if (!_matrix->should_show (w.bundle->channel_type (i))) {
344                                         continue;
345                                 }
346
347                                 ARDOUR::BundleChannel const bc (w.bundle, i);
348                                 n.push_back (PortMatrixNode (bc, ARDOUR::BundleChannel ()));
349                         }
350
351                         _body->set_mouseover (n);
352                         done = true;
353
354                 } else if (x < _width) {
355
356                         _body->set_mouseover (PortMatrixNode (w, ARDOUR::BundleChannel ()));
357                         done = true;
358
359                 }
360
361         }
362
363         if (!done) {
364                 /* not over any bundle */
365                 _body->set_mouseover (PortMatrixNode ());
366                 return;
367         }
368 }