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