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