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