Attempt to fix some confusions caused by bundles containing
[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/cairo.h>
23 #include "ardour/bundle.h"
24 #include "port_matrix_row_labels.h"
25 #include "port_matrix.h"
26 #include "port_matrix_body.h"
27 #include "i18n.h"
28 #include "utils.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, int b, uint32_t t, guint)
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 (b == 3) {
156
157                 _matrix->popup_menu (
158                         ARDOUR::BundleChannel (),
159                         w,
160                         t
161                         );
162         }
163 }
164
165 double
166 PortMatrixRowLabels::component_to_parent_x (double x) const
167 {
168         /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
169         return x + _parent_rectangle.get_x();
170 }
171
172 double
173 PortMatrixRowLabels::parent_to_component_x (double x) const
174 {
175         /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
176         return x - _parent_rectangle.get_x();
177 }
178
179 double
180 PortMatrixRowLabels::component_to_parent_y (double y) const
181 {
182         return y - _body->yoffset() + _parent_rectangle.get_y();
183 }
184
185 double
186 PortMatrixRowLabels::parent_to_component_y (double y) const
187 {
188         return y + _body->yoffset() - _parent_rectangle.get_y();
189 }
190
191
192 double
193 PortMatrixRowLabels::bundle_name_x () const
194 {
195         double x = 0;
196
197         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && !_matrix->show_only_bundles ()) {
198                 x = _longest_port_name + name_pad() * 2;
199         }
200
201         return x;
202 }
203
204 double
205 PortMatrixRowLabels::port_name_x () const
206 {
207         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
208                 return _longest_bundle_name + name_pad() * 2;
209         } else {
210                 return 0;
211         }
212
213         return 0;
214 }
215
216 void
217 PortMatrixRowLabels::render_bundle_name (
218         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
219         )
220 {
221         double const x = bundle_name_x ();
222
223         int const n = _matrix->show_only_bundles() ? 1 : _matrix->count_of_our_type_min_1 (b->nchannels());
224         set_source_rgb (cr, bg_colour);
225         cairo_rectangle (cr, xoff + x, yoff, _longest_bundle_name + name_pad() * 2, grid_spacing() * n);
226         cairo_fill_preserve (cr);
227         set_source_rgb (cr, fg_colour);
228         cairo_set_line_width (cr, label_border_width ());
229         cairo_stroke (cr);
230
231         cairo_text_extents_t ext;
232         cairo_text_extents (cr, b->name().c_str(), &ext);
233         double const off = (grid_spacing() - ext.height) / 2;
234
235         set_source_rgb (cr, text_colour());
236         cairo_move_to (cr, xoff + x + name_pad(), yoff + name_pad() + off);
237         cairo_show_text (cr, b->name().c_str());
238 }
239
240 void
241 PortMatrixRowLabels::render_channel_name (
242         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
243         )
244 {
245         set_source_rgb (cr, bg_colour);
246         cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, grid_spacing());
247         cairo_fill_preserve (cr);
248         set_source_rgb (cr, fg_colour);
249         cairo_set_line_width (cr, label_border_width ());
250         cairo_stroke (cr);
251
252         if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
253
254                 /* only plot the name if the bundle has more than one channel;
255                    the name of a single channel is assumed to be redundant */
256
257                 cairo_text_extents_t ext;
258                 cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
259                 double const off = (grid_spacing() - ext.height) / 2;
260
261                 set_source_rgb (cr, text_colour());
262                 cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
263                 cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
264         }
265 }
266
267 double
268 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const &) const
269 {
270         return 0;
271 }
272
273 double
274 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
275 {
276         return channel_to_position (bc, _matrix->visible_rows()) * grid_spacing ();
277 }
278
279 void
280 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
281 {
282         if (bc.bundle) {
283
284                 if (_matrix->show_only_bundles()) {
285                         _body->queue_draw_area (
286                                 component_to_parent_x (bundle_name_x()) - 1,
287                                 component_to_parent_y (channel_y (bc)) - 1,
288                                 _longest_bundle_name + name_pad() * 2 + 2,
289                                 grid_spacing() + 2
290                                 );
291                 } else {
292                         _body->queue_draw_area (
293                                 component_to_parent_x (port_name_x()) - 1,
294                                 component_to_parent_y (channel_y (bc)) - 1,
295                                 _longest_port_name + name_pad() * 2 + 2,
296                                 grid_spacing() + 2
297                                 );
298                 }
299         }
300
301 }
302
303 void
304 PortMatrixRowLabels::mouseover_changed (list<PortMatrixNode> const &)
305 {
306         list<PortMatrixNode> const m = _body->mouseover ();
307         for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
308
309                 ARDOUR::BundleChannel c = i->column;
310                 ARDOUR::BundleChannel r = i->row;
311
312                 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
313                         add_channel_highlight (r);
314                 } else if (r.bundle) {
315                         _body->highlight_associated_channels (_matrix->row_index(), r);
316                 }
317         }
318 }
319
320 void
321 PortMatrixRowLabels::motion (double x, double y)
322 {
323         ARDOUR::BundleChannel const w = position_to_channel (y, x, _matrix->visible_rows());
324
325         uint32_t const bw = _longest_bundle_name + 2 * name_pad();
326
327         bool done = false;
328
329         if (w.bundle) {
330
331                 if (
332                         (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < bw) ||
333                         (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_width - bw) && x < _width)
334
335                         ) {
336
337                         /* if the mouse is over a bundle name, highlight all channels in the bundle */
338
339                         list<PortMatrixNode> n;
340
341                         for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
342                                 if (!_matrix->should_show (w.bundle->channel_type (i))) {
343                                         continue;
344                                 }
345
346                                 ARDOUR::BundleChannel const bc (w.bundle, i);
347                                 n.push_back (PortMatrixNode (bc, ARDOUR::BundleChannel ()));
348                         }
349
350                         _body->set_mouseover (n);
351                         done = true;
352
353                 } else if (x < _width) {
354
355                         _body->set_mouseover (PortMatrixNode (w, ARDOUR::BundleChannel ()));
356                         done = true;
357
358                 }
359
360         }
361
362         if (!done) {
363                 /* not over any bundle */
364                 _body->set_mouseover (PortMatrixNode ());
365                 return;
366         }
367 }