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