Fix formatting samplecnt_t (aka int64_t aka long long int)
[ardour.git] / gtk2_ardour / port_matrix_body.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  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include <iostream>
22 #include "ardour/bundle.h"
23 #include "ardour/types.h"
24
25 #include "gui_thread.h"
26 #include "port_matrix_body.h"
27 #include "port_matrix.h"
28 #include "port_matrix_column_labels.h"
29 #include "port_matrix_row_labels.h"
30 #include "port_matrix_grid.h"
31
32 #include "pbd/i18n.h"
33
34 using namespace std;
35
36 PortMatrixBody::PortMatrixBody (PortMatrix* p)
37         : _matrix (p),
38           _alloc_width (0),
39           _alloc_height (0),
40           _xoffset (0),
41           _yoffset (0),
42           _column_labels_border_x (0),
43           _column_labels_height (0),
44           _ignore_component_size_changed (false)
45 {
46         _column_labels = new PortMatrixColumnLabels (p, this);
47         _row_labels = new PortMatrixRowLabels (p, this, *_column_labels);
48         _grid = new PortMatrixGrid (p, this);
49
50         _components.push_back (_column_labels);
51         _components.push_back (_row_labels);
52         _components.push_back (_grid);
53
54         add_events (Gdk::LEAVE_NOTIFY_MASK | Gdk::POINTER_MOTION_MASK);
55 }
56
57
58 PortMatrixBody::~PortMatrixBody ()
59 {
60         for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
61                 delete *i;
62         }
63 }
64
65 bool
66 PortMatrixBody::on_expose_event (GdkEventExpose* event)
67 {
68         if (
69                 _matrix->visible_columns() == 0 || _matrix->visible_rows() == 0 ||
70                 _matrix->visible_columns()->bundles().empty() || _matrix->visible_rows()->bundles().empty()
71                 ) {
72
73                 /* nothing to connect */
74
75                 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
76
77                 cairo_set_source_rgb (cr, 0, 0, 0);
78                 cairo_rectangle (cr, 0, 0, _alloc_width, _alloc_height);
79                 cairo_fill (cr);
80
81                 string t;
82                 if (_matrix->type() == ARDOUR::DataType::NIL) {
83                         t = _("There are no ports to connect.");
84                 } else {
85                         t = string_compose (_("There are no %1 ports to connect."), _matrix->type().to_i18n_string());
86                 }
87
88                 cairo_text_extents_t ext;
89                 cairo_text_extents (cr, t.c_str(), &ext);
90
91                 cairo_set_source_rgb (cr, 1, 1, 1);
92                 cairo_move_to (cr, (_alloc_width - ext.width) / 2, (_alloc_height + ext.height) / 2);
93                 cairo_show_text (cr, t.c_str ());
94
95                 cairo_destroy (cr);
96
97                 return true;
98         }
99
100         Gdk::Rectangle const exposure (
101                 event->area.x, event->area.y, event->area.width, event->area.height
102                 );
103
104         bool intersects;
105
106         for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
107
108                 Gdk::Rectangle r = exposure;
109
110                 /* the get_pixmap call may cause things to be rerendered and sizes to change,
111                    so fetch the pixmap before calculating where to put it */
112                 GdkPixmap* p = (*i)->get_pixmap (get_window()->gobj());
113                 r.intersect ((*i)->parent_rectangle(), intersects);
114
115                 if (intersects) {
116
117                         gdk_draw_drawable (
118                                 get_window()->gobj(),
119                                 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
120                                 p,
121                                 (*i)->parent_to_component_x (r.get_x()),
122                                 (*i)->parent_to_component_y (r.get_y()),
123                                 r.get_x(),
124                                 r.get_y(),
125                                 r.get_width(),
126                                 r.get_height()
127                                 );
128                 }
129
130         }
131
132         cairo_t* cr = gdk_cairo_create (get_window()->gobj());
133
134         for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
135                 cairo_save (cr);
136                 set_cairo_clip (cr, (*i)->parent_rectangle ());
137                 (*i)->draw_extra (cr);
138                 cairo_restore (cr);
139         }
140
141         cairo_destroy (cr);
142
143         return true;
144 }
145
146 void
147 PortMatrixBody::on_size_request (Gtk::Requisition *req)
148 {
149         pair<int, int> const col = _column_labels->dimensions ();
150         pair<int, int> const row = _row_labels->dimensions ();
151         pair<int, int> const grid = _grid->dimensions ();
152
153         if (grid.first == 0 && grid.second == 0) {
154                 /* nothing to display */
155                 req->width = 256;
156                 req->height = 64;
157                 return;
158         }
159
160         /* don't ask for the maximum size of our contents, otherwise GTK won't
161            let the containing window shrink below this size */
162
163         /* XXX these shouldn't be hard-coded */
164         int const min_width = 512;
165         int const min_height = 512;
166
167         req->width = min (min_width, max (col.first, grid.first + row.first));
168         req->height = min (min_height / _matrix->min_height_divisor(), col.second + grid.second);
169 }
170
171 void
172 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
173 {
174         Gtk::EventBox::on_size_allocate (alloc);
175
176         _alloc_width = alloc.get_width ();
177         _alloc_height = alloc.get_height ();
178
179         compute_rectangles ();
180         _matrix->setup_scrollbars ();
181 }
182
183 void
184 PortMatrixBody::compute_rectangles ()
185 {
186         /* full sizes of components */
187         pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
188         uint32_t const col_overhang = _column_labels->overhang ();
189         pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
190         pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
191
192         Gdk::Rectangle col_rect;
193         Gdk::Rectangle row_rect;
194         Gdk::Rectangle grid_rect;
195
196         if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
197
198                 col_rect.set_x (0);
199                 _column_labels_border_x = col_overhang;
200                 col_rect.set_y (0);
201                 grid_rect.set_x (0);
202
203                 col_rect.set_width (min (col.first, _alloc_width));
204
205                 uint32_t const y = min (_alloc_height, col.second);
206                 col_rect.set_height (y);
207                 row_rect.set_y (y);
208                 row_rect.set_height (_alloc_height - y);
209                 grid_rect.set_y (y);
210                 grid_rect.set_height (_alloc_height - y);
211
212                 uint32_t x = 0;
213                 if (_alloc_width > (grid.first + row.first)) {
214                         x = grid.first;
215                 } else if (_alloc_width > row.first) {
216                         x = _alloc_width - row.first;
217                 }
218
219                 grid_rect.set_width (x);
220                 row_rect.set_x (x);
221                 row_rect.set_width (_alloc_width - x);
222
223
224         } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
225
226                 col_rect.set_height (min (_alloc_height, col.second));
227                 row_rect.set_height (std::min (_alloc_height - col_rect.get_height(), row.second));
228
229                 row_rect.set_x (0);
230                 row_rect.set_y (_alloc_height - row_rect.get_height() - col_rect.get_height());
231                 row_rect.set_width (min (_alloc_width, row.first));
232
233                 grid_rect.set_x (row_rect.get_width());
234                 grid_rect.set_y (_alloc_height - row_rect.get_height() - col_rect.get_height());
235                 grid_rect.set_width (std::min (_alloc_width - row_rect.get_width(), grid.first));
236                 grid_rect.set_height (row_rect.get_height ());
237
238                 col_rect.set_width (grid_rect.get_width () + col_overhang);
239                 col_rect.set_x (row_rect.get_width() + grid_rect.get_width() - col_rect.get_width());
240                 _column_labels_border_x = col_rect.get_x () >= 0 ? col_rect.get_x () : 0;
241                 col_rect.set_y (_alloc_height - col_rect.get_height());
242         }
243
244         _column_labels_height = col_rect.get_height ();
245
246         _row_labels->set_parent_rectangle (row_rect);
247         _column_labels->set_parent_rectangle (col_rect);
248         _grid->set_parent_rectangle (grid_rect);
249
250         DimensionsChanged (); /* EMIT SIGNAL */
251 }
252
253 void
254 PortMatrixBody::setup ()
255 {
256         /* Discard any old connections to bundles */
257
258         _bundle_connections.drop_connections ();
259
260         /* Connect to bundles so that we find out when their names change */
261
262         if (_matrix->visible_rows()) {
263                 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
264                 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
265
266                         (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_row_labels, this), gui_context());
267
268                 }
269         }
270
271         if (_matrix->visible_columns()) {
272                 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
273                 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
274                         (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_column_labels, this), gui_context());
275                 }
276         }
277
278         for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
279                 (*i)->setup ();
280         }
281
282         set_mouseover (PortMatrixNode ());
283
284         _ignore_component_size_changed = true;
285         compute_rectangles ();
286         _ignore_component_size_changed = false;
287 }
288
289 uint32_t
290 PortMatrixBody::full_scroll_width ()
291 {
292         return _grid->dimensions().first;
293
294 }
295
296 uint32_t
297 PortMatrixBody::alloc_scroll_width ()
298 {
299         return _grid->parent_rectangle().get_width();
300 }
301
302 uint32_t
303 PortMatrixBody::full_scroll_height ()
304 {
305         return _grid->dimensions().second;
306 }
307
308 uint32_t
309 PortMatrixBody::alloc_scroll_height ()
310 {
311         return _grid->parent_rectangle().get_height();
312 }
313
314 /** Set x offset (for scrolling) */
315 void
316 PortMatrixBody::set_xoffset (uint32_t xo)
317 {
318         _xoffset = xo;
319         queue_draw ();
320 }
321
322 /** Set y offset (for scrolling) */
323 void
324 PortMatrixBody::set_yoffset (uint32_t yo)
325 {
326         _yoffset = yo;
327         queue_draw ();
328 }
329
330 bool
331 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
332 {
333         for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
334                 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
335                         (*i)->button_press (
336                                 (*i)->parent_to_component_x (ev->x),
337                                 (*i)->parent_to_component_y (ev->y),
338                                 ev
339                                 );
340                 }
341         }
342
343         return true;
344 }
345
346 bool
347 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
348 {
349         for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
350                 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
351                         (*i)->button_release (
352                                 (*i)->parent_to_component_x (ev->x),
353                                 (*i)->parent_to_component_y (ev->y),
354                                 ev
355                                 );
356                 } else {
357                         (*i)->button_release (
358                                 -1, -1,
359                                 ev
360                                 );
361                 }
362         }
363
364         return true;
365 }
366
367 void
368 PortMatrixBody::rebuild_and_draw_grid ()
369 {
370         _grid->require_rebuild ();
371         queue_draw ();
372 }
373
374 void
375 PortMatrixBody::rebuild_and_draw_column_labels ()
376 {
377         _column_labels->require_rebuild ();
378         queue_draw ();
379 }
380
381 void
382 PortMatrixBody::rebuild_and_draw_row_labels ()
383 {
384         _row_labels->require_rebuild ();
385         queue_draw ();
386 }
387
388 bool
389 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
390 {
391         if (ev->type == GDK_LEAVE_NOTIFY) {
392                 set_mouseover (PortMatrixNode ());
393         }
394
395         return true;
396 }
397
398 bool
399 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
400 {
401         bool done = false;
402
403         for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
404                 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
405                         (*i)->motion (
406                                 (*i)->parent_to_component_x (ev->x),
407                                 (*i)->parent_to_component_y (ev->y)
408                                 );
409
410                         done = true;
411                 }
412         }
413
414
415         if (!done) {
416                 set_mouseover (PortMatrixNode ());
417         }
418
419         return true;
420 }
421
422 void
423 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
424 {
425         list<PortMatrixNode> m;
426         m.push_back (n);
427         set_mouseover (m);
428 }
429
430 void
431 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
432 {
433         if (n == _mouseover) {
434                 return;
435         }
436
437         /* Channel highlights are set up only on mouseovers, so
438            it's reasonable to remove all channel highlights here.
439            We can't let individual components clear their own highlights
440            because of the case where, say, the row labels set up some column
441            highlights, and then we ask the column labels to set up their
442            own highlights and they clear them out before they start.
443         */
444
445         _row_labels->clear_channel_highlights ();
446         _column_labels->clear_channel_highlights ();
447
448         list<PortMatrixNode> old = _mouseover;
449         _mouseover = n;
450
451         for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
452                 (*i)->mouseover_changed (old);
453         }
454 }
455
456 void
457 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
458 {
459         ARDOUR::BundleChannel bc[2];
460         bc[dim] = h;
461
462         if (!PortMatrix::bundle_with_channels (bc[dim].bundle)) {
463                 return;
464         }
465
466         if (dim == _matrix->column_index()) {
467                 _column_labels->add_channel_highlight (bc[dim]);
468         } else {
469                 _row_labels->add_channel_highlight (bc[dim]);
470         }
471
472         PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
473
474         for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
475                 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
476
477                         if (!_matrix->should_show ((*i)->bundle->channel_type(j))) {
478                                 continue;
479                         }
480
481                         bc[1 - dim] = ARDOUR::BundleChannel ((*i)->bundle, j);
482
483                         PortMatrixNode n;
484                         n.row = bc[_matrix->row_index()];
485                         n.column = bc[_matrix->column_index()];
486
487                         if (_matrix->get_association(n) != PortMatrixNode::NOT_ASSOCIATED) {
488                                 if (dim == _matrix->column_index()) {
489                                         _row_labels->add_channel_highlight (bc[1 - dim]);
490                                 } else {
491                                         _column_labels->add_channel_highlight (bc[1 - dim]);
492                                 }
493                         }
494                 }
495         }
496 }
497
498 void
499 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
500 {
501         cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
502         cairo_clip (cr);
503 }
504
505 void
506 PortMatrixBody::component_size_changed ()
507 {
508         if (_ignore_component_size_changed) {
509                 return;
510         }
511
512         compute_rectangles ();
513         _matrix->setup_scrollbars ();
514 }
515
516 pair<uint32_t, uint32_t>
517 PortMatrixBody::max_size () const
518 {
519         pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
520         pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
521         pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
522
523         return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
524 }
525
526 /** @return x position at which the column labels meet the border of the matrix */
527 uint32_t
528 PortMatrixBody::column_labels_border_x () const
529 {
530         return _column_labels_border_x;
531 }
532
533 uint32_t
534 PortMatrixBody::column_labels_height () const
535 {
536         return _column_labels_height;
537 }