Pass whole GdkEventButton into port matrix button
[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 "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 #include "utils.h"
30
31 using namespace std;
32
33 PortMatrixRowLabels::PortMatrixRowLabels (PortMatrix* m, PortMatrixBody* b)
34         : PortMatrixLabels (m, b)
35 {
36
37 }
38
39 void
40 PortMatrixRowLabels::compute_dimensions ()
41 {
42         cairo_surface_t* surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 200, 200);
43         cairo_t* cr = cairo_create (surface);
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         cairo_surface_destroy (surface);
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                                 ARDOUR::BundleChannel bc (
122                                         (*i)->bundle,
123                                         (*i)->bundle->type_channel_to_overall (_matrix->type (), j)
124                                         );
125                                 
126                                 render_channel_name (cr, background_colour (), c, 0, y, bc);
127                                 y += grid_spacing();
128                                 ++M;
129                         }
130
131                         if (N == 0) {
132                                 y += grid_spacing ();
133                         }
134                         
135                 } else {
136                         y += grid_spacing();
137                 }
138
139                 ++N;
140         }
141 }
142
143 void
144 PortMatrixRowLabels::button_press (double x, double y, GdkEventButton* ev)
145 {
146         ARDOUR::BundleChannel w = position_to_channel (y, x, _matrix->visible_rows());
147
148         if (
149                 (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_longest_port_name + name_pad() * 2)) ||
150                 (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < (_longest_bundle_name + name_pad() * 2))
151
152                 ) {
153                         w.channel = -1;
154         }
155
156         if (Gtkmm2ext::Keyboard::is_delete_event (ev) && w.channel != -1) {
157                 _matrix->remove_channel (w);
158         } else if (ev->button == 3) {
159                 _matrix->popup_menu (
160                         ARDOUR::BundleChannel (),
161                         w,
162                         ev->time
163                         );
164         }
165 }
166
167 double
168 PortMatrixRowLabels::component_to_parent_x (double x) const
169 {
170         /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
171         return x + _parent_rectangle.get_x();
172 }
173
174 double
175 PortMatrixRowLabels::parent_to_component_x (double x) const
176 {
177         /* Row labels don't scroll horizontally, so x conversion does not depend on xoffset */
178         return x - _parent_rectangle.get_x();
179 }
180
181 double
182 PortMatrixRowLabels::component_to_parent_y (double y) const
183 {
184         return y - _body->yoffset() + _parent_rectangle.get_y();
185 }
186
187 double
188 PortMatrixRowLabels::parent_to_component_y (double y) const
189 {
190         return y + _body->yoffset() - _parent_rectangle.get_y();
191 }
192
193
194 double
195 PortMatrixRowLabels::bundle_name_x () const
196 {
197         double x = 0;
198
199         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && !_matrix->show_only_bundles ()) {
200                 x = _longest_port_name + name_pad() * 2;
201         }
202
203         return x;
204 }
205
206 double
207 PortMatrixRowLabels::port_name_x () const
208 {
209         if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
210                 return _longest_bundle_name + name_pad() * 2;
211         } else {
212                 return 0;
213         }
214
215         return 0;
216 }
217
218 void
219 PortMatrixRowLabels::render_bundle_name (
220         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, boost::shared_ptr<ARDOUR::Bundle> b
221         )
222 {
223         double const x = bundle_name_x ();
224
225         int const n = _matrix->show_only_bundles() ? 1 : _matrix->count_of_our_type_min_1 (b->nchannels());
226         set_source_rgb (cr, bg_colour);
227         cairo_rectangle (cr, xoff + x, yoff, _longest_bundle_name + name_pad() * 2, grid_spacing() * n);
228         cairo_fill_preserve (cr);
229         set_source_rgb (cr, fg_colour);
230         cairo_set_line_width (cr, label_border_width ());
231         cairo_stroke (cr);
232
233         cairo_text_extents_t ext;
234         cairo_text_extents (cr, b->name().c_str(), &ext);
235         double const off = (grid_spacing() - ext.height) / 2;
236
237         set_source_rgb (cr, text_colour());
238         cairo_move_to (cr, xoff + x + name_pad(), yoff + name_pad() + off);
239         cairo_show_text (cr, b->name().c_str());
240 }
241
242 void
243 PortMatrixRowLabels::render_channel_name (
244         cairo_t* cr, Gdk::Color fg_colour, Gdk::Color bg_colour, double xoff, double yoff, ARDOUR::BundleChannel const& bc
245         )
246 {
247         set_source_rgb (cr, bg_colour);
248         cairo_rectangle (cr, port_name_x() + xoff, yoff, _longest_port_name + name_pad() * 2, grid_spacing());
249         cairo_fill_preserve (cr);
250         set_source_rgb (cr, fg_colour);
251         cairo_set_line_width (cr, label_border_width ());
252         cairo_stroke (cr);
253
254         if (_matrix->count_of_our_type (bc.bundle->nchannels()) > 1) {
255
256                 /* only plot the name if the bundle has more than one channel;
257                    the name of a single channel is assumed to be redundant */
258
259                 cairo_text_extents_t ext;
260                 cairo_text_extents (cr, bc.bundle->channel_name(bc.channel).c_str(), &ext);
261                 double const off = (grid_spacing() - ext.height) / 2;
262
263                 set_source_rgb (cr, text_colour());
264                 cairo_move_to (cr, port_name_x() + xoff + name_pad(), yoff + name_pad() + off);
265                 cairo_show_text (cr, bc.bundle->channel_name(bc.channel).c_str());
266         }
267 }
268
269 double
270 PortMatrixRowLabels::channel_x (ARDOUR::BundleChannel const &) const
271 {
272         return 0;
273 }
274
275 double
276 PortMatrixRowLabels::channel_y (ARDOUR::BundleChannel const& bc) const
277 {
278         return channel_to_position (bc, _matrix->visible_rows()) * grid_spacing ();
279 }
280
281 void
282 PortMatrixRowLabels::queue_draw_for (ARDOUR::BundleChannel const & bc)
283 {
284         if (bc.bundle) {
285
286                 if (_matrix->show_only_bundles()) {
287                         _body->queue_draw_area (
288                                 component_to_parent_x (bundle_name_x()) - 1,
289                                 component_to_parent_y (channel_y (bc)) - 1,
290                                 _longest_bundle_name + name_pad() * 2 + 2,
291                                 grid_spacing() + 2
292                                 );
293                 } else {
294                         _body->queue_draw_area (
295                                 component_to_parent_x (port_name_x()) - 1,
296                                 component_to_parent_y (channel_y (bc)) - 1,
297                                 _longest_port_name + name_pad() * 2 + 2,
298                                 grid_spacing() + 2
299                                 );
300                 }
301         }
302
303 }
304
305 void
306 PortMatrixRowLabels::mouseover_changed (list<PortMatrixNode> const &)
307 {
308         list<PortMatrixNode> const m = _body->mouseover ();
309         for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
310
311                 ARDOUR::BundleChannel c = i->column;
312                 ARDOUR::BundleChannel r = i->row;
313
314                 if (PortMatrix::bundle_with_channels (c.bundle) && PortMatrix::bundle_with_channels (r.bundle)) {
315                         add_channel_highlight (r);
316                 } else if (r.bundle) {
317                         _body->highlight_associated_channels (_matrix->row_index(), r);
318                 }
319         }
320 }
321
322 void
323 PortMatrixRowLabels::motion (double x, double y)
324 {
325         ARDOUR::BundleChannel const w = position_to_channel (y, x, _matrix->visible_rows());
326
327         uint32_t const bw = _longest_bundle_name + 2 * name_pad();
328
329         bool done = false;
330
331         if (w.bundle) {
332
333                 if (
334                         (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM && x < bw) ||
335                         (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT && x > (_width - bw) && x < _width)
336
337                         ) {
338
339                         /* if the mouse is over a bundle name, highlight all channels in the bundle */
340
341                         list<PortMatrixNode> n;
342
343                         for (uint32_t i = 0; i < w.bundle->nchannels().n_total(); ++i) {
344                                 if (!_matrix->should_show (w.bundle->channel_type (i))) {
345                                         continue;
346                                 }
347
348                                 ARDOUR::BundleChannel const bc (w.bundle, i);
349                                 n.push_back (PortMatrixNode (bc, ARDOUR::BundleChannel ()));
350                         }
351
352                         _body->set_mouseover (n);
353                         done = true;
354
355                 } else if (x < _width) {
356
357                         _body->set_mouseover (PortMatrixNode (w, ARDOUR::BundleChannel ()));
358                         done = true;
359
360                 }
361
362         }
363
364         if (!done) {
365                 /* not over any bundle */
366                 _body->set_mouseover (PortMatrixNode ());
367                 return;
368         }
369 }