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