Fix some unused parameter warnings.
[ardour.git] / gtk2_ardour / port_matrix_grid.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 <cairo/cairo.h>
22 #include "ardour/bundle.h"
23 #include "ardour/types.h"
24 #include "port_matrix_grid.h"
25 #include "port_matrix.h"
26 #include "port_matrix_body.h"
27
28 using namespace std;
29
30 PortMatrixGrid::PortMatrixGrid (PortMatrix* m, PortMatrixBody* b)
31         : PortMatrixComponent (m, b),
32           _dragging (false),
33           _drag_valid (false),
34           _moved (false)
35 {
36         
37 }
38
39 void
40 PortMatrixGrid::compute_dimensions ()
41 {
42         _width = 0;
43
44         for (PortGroupList::List::const_iterator i = _matrix->columns()->begin(); i != _matrix->columns()->end(); ++i) {
45                 _width += group_size (*i) * grid_spacing ();
46         }
47
48         _height = 0;
49         for (PortGroupList::List::const_iterator i = _matrix->rows()->begin(); i != _matrix->rows()->end(); ++i) {
50                 _height += group_size (*i) * grid_spacing ();
51         }
52 }
53
54
55 void
56 PortMatrixGrid::render (cairo_t* cr)
57 {
58         set_source_rgb (cr, background_colour());
59         cairo_rectangle (cr, 0, 0, _width, _height);
60         cairo_fill (cr);
61
62         uint32_t x = 0;
63         for (PortGroupList::List::const_iterator c = _matrix->columns()->begin(); c != _matrix->columns()->end(); ++c) {
64
65                 uint32_t y = 0;
66                 for (PortGroupList::List::const_iterator r = _matrix->rows()->begin(); r != _matrix->rows()->end(); ++r) {
67
68                         if ((*c)->visible() && (*r)->visible()) {
69                                 render_group_pair (cr, *r, *c, x, y);
70                         }
71
72                         y += group_size (*r) * grid_spacing ();
73                 }
74
75                 x += group_size (*c) * grid_spacing ();
76         }
77 }
78
79 void
80 PortMatrixGrid::render_group_pair (cairo_t* cr, boost::shared_ptr<const PortGroup> row, boost::shared_ptr<const PortGroup> column, uint32_t const x, uint32_t const y)
81 {
82         PortGroup::BundleList const & row_bundles = row->bundles();
83         PortGroup::BundleList const & column_bundles = column->bundles();
84
85         /* unfortunately we need to compute the height of the row group here */
86         uint32_t height = group_size (row) * grid_spacing ();
87         
88         uint32_t tx = x;
89
90         /* VERTICAL GRID LINES */
91         
92         set_source_rgb (cr, grid_colour());
93         uint32_t N = 0;
94         
95         for (PortGroup::BundleList::const_iterator i = column_bundles.begin(); i != column_bundles.end(); ++i) {
96
97                 cairo_set_line_width (cr, thick_grid_line_width());
98                 cairo_move_to (cr, tx, y);
99                 cairo_line_to (cr, tx, y + height);
100                 cairo_stroke (cr);
101                 
102                 if (!_matrix->show_only_bundles()) {
103                         cairo_set_line_width (cr, thin_grid_line_width());
104                         for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
105                                 tx += grid_spacing ();
106                                 cairo_move_to (cr, tx, y);
107                                 cairo_line_to (cr, tx, y + height);
108                                 cairo_stroke (cr);
109                         }
110                         
111                 } else {
112                         
113                         tx += grid_spacing ();
114                         
115                 }
116                 
117                 ++N;
118         }
119
120         uint32_t const width = tx - x;
121
122         uint32_t ty = y;
123         
124         /* HORIZONTAL GRID LINES */
125         
126         N = 0;
127         for (PortGroup::BundleList::const_iterator i = row_bundles.begin(); i != row_bundles.end(); ++i) {
128                 
129                 cairo_set_line_width (cr, thick_grid_line_width());
130                 cairo_move_to (cr, x, ty);
131                 cairo_line_to (cr, x + width, ty);
132                 cairo_stroke (cr);
133                 
134                 if (!_matrix->show_only_bundles()) {
135                         cairo_set_line_width (cr, thin_grid_line_width());
136                         for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
137                                 ty += grid_spacing ();
138                                 cairo_move_to (cr, x, ty);
139                                 cairo_line_to (cr, x + width, ty);
140                                 cairo_stroke (cr);
141                         }
142
143                 } else {
144
145                         ty += grid_spacing ();
146
147                 }
148                 
149                 ++N;
150         }
151         
152         /* ASSOCIATION INDICATORS */
153
154         uint32_t bx = x;
155         uint32_t by = y;
156
157         if (_matrix->show_only_bundles()) {
158
159                 for (PortGroup::BundleList::const_iterator i = column_bundles.begin(); i != column_bundles.end(); ++i) {
160                         by = y;
161                         
162                         for (PortGroup::BundleList::const_iterator j = row_bundles.begin(); j != row_bundles.end(); ++j) {
163                                 
164                                 PortMatrixNode::State s = get_association (PortMatrixNode (
165                                                                                    ARDOUR::BundleChannel (i->bundle, 0),
166                                                                                    ARDOUR::BundleChannel (j->bundle, 0)
167                                                                                    ));
168                                 switch (s) {
169                                 case PortMatrixNode::UNKNOWN:
170                                         draw_unknown_indicator (cr, bx, by);
171                                         break;
172                                 case PortMatrixNode::ASSOCIATED:
173                                         draw_association_indicator (cr, bx, by);
174                                         break;
175                                 case PortMatrixNode::PARTIAL:
176                                         draw_association_indicator (cr, bx, by, 0.5);
177                                         break;
178                                 default:
179                                         break;
180                                 }
181                                 
182                                 by += grid_spacing();
183                         }
184                         
185                         bx += grid_spacing();
186                         
187                 }
188
189         } else {
190
191                 for (PortGroup::BundleList::const_iterator i = column_bundles.begin(); i != column_bundles.end(); ++i) {
192                         by = y;
193                         
194                         for (PortGroup::BundleList::const_iterator j = row_bundles.begin(); j != row_bundles.end(); ++j) {
195                                 
196                                 tx = bx;
197                                 for (uint32_t k = 0; k < i->bundle->nchannels (); ++k) {
198                                         
199                                         ty = by;
200                                         for (uint32_t l = 0; l < j->bundle->nchannels (); ++l) {
201                                                 
202                                                 ARDOUR::BundleChannel c[2];
203                                                 c[_matrix->column_index()] = ARDOUR::BundleChannel (i->bundle, k);
204                                                 c[_matrix->row_index()] = ARDOUR::BundleChannel (j->bundle, l);
205                                                 
206                                                 PortMatrixNode::State const s = _matrix->get_state (c);
207                                                 
208                                                 switch (s) {
209                                                 case PortMatrixNode::ASSOCIATED:
210                                                         draw_association_indicator (cr, tx, ty);
211                                                         break;
212                                                         
213                                                 case PortMatrixNode::UNKNOWN:
214                                                         draw_unknown_indicator (cr, tx, ty);
215                                                         break;
216                                                         
217                                                 case PortMatrixNode::NOT_ASSOCIATED:
218                                                         break;
219                                                         
220                                                 default:
221                                                         break;
222                                                 }
223                                                 
224                                                 ty += grid_spacing();
225                                         }
226                                         
227                                         tx += grid_spacing();
228                                 }
229                                 
230                                 by += j->bundle->nchannels () * grid_spacing();
231                         }
232                         
233                         bx += i->bundle->nchannels () * grid_spacing();
234                 }
235         }
236 }
237
238 void
239 PortMatrixGrid::draw_association_indicator (cairo_t* cr, uint32_t x, uint32_t y, double p)
240 {
241         set_source_rgba (cr, association_colour(), 0.5);
242
243         cairo_arc (
244                 cr,
245                 x + grid_spacing() / 2,
246                 y + grid_spacing() / 2,
247                 (grid_spacing() - (2 * connection_indicator_pad())) / 2,
248                 0,
249                 p * 2 * M_PI
250                 );
251         
252         cairo_fill (cr);
253 }
254
255 void
256 PortMatrixGrid::draw_empty_square (cairo_t* cr, uint32_t x, uint32_t y)
257 {
258         set_source_rgb (cr, background_colour());
259         cairo_rectangle (
260                 cr,
261                 x + thick_grid_line_width(),
262                 y + thick_grid_line_width(),
263                 grid_spacing() - 2 * thick_grid_line_width(),
264                 grid_spacing() - 2 * thick_grid_line_width()
265                 );
266         cairo_fill (cr);
267 }
268
269 void
270 PortMatrixGrid::draw_unknown_indicator (cairo_t* cr, uint32_t x, uint32_t y)
271 {
272         set_source_rgba (cr, unknown_colour(), 0.5);
273         cairo_rectangle (
274                 cr,
275                 x + thick_grid_line_width(),
276                 y + thick_grid_line_width(),
277                 grid_spacing() - 2 * thick_grid_line_width(),
278                 grid_spacing() - 2 * thick_grid_line_width()
279                 );
280         cairo_fill (cr);
281 }
282
283 PortMatrixNode
284 PortMatrixGrid::position_to_node (uint32_t x, uint32_t y) const
285 {
286         return PortMatrixNode (
287                 position_to_group_and_channel (y, _matrix->rows()).second,
288                 position_to_group_and_channel (x, _matrix->columns()).second
289                 );
290 }
291
292 void
293 PortMatrixGrid::button_press (double x, double y, int b, uint32_t t)
294 {
295         pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> px = position_to_group_and_channel (x / grid_spacing(), _matrix->columns());
296         pair<boost::shared_ptr<PortGroup>, ARDOUR::BundleChannel> py = position_to_group_and_channel (y / grid_spacing(), _matrix->rows());
297         
298         if (b == 1) {
299
300                 _dragging = true;
301                 _drag_valid = (px.second.bundle && py.second.bundle);
302                 
303                 _moved = false;
304                 _drag_start_x = x / grid_spacing ();
305                 _drag_start_y = y / grid_spacing ();
306
307         } else if (b == 3) {
308
309                 _matrix->popup_menu (px, py, t);
310                 
311         }
312 }
313
314 PortMatrixNode::State
315 PortMatrixGrid::get_association (PortMatrixNode node) const
316 {
317         if (_matrix->show_only_bundles()) {
318
319                 bool have_unknown = false;
320                 bool have_off_diagonal_association = false;
321                 bool have_diagonal_association = false;
322                 bool have_diagonal_not_association = false;
323                 
324                 for (uint32_t i = 0; i < node.row.bundle->nchannels (); ++i) {
325                         
326                         for (uint32_t j = 0; j < node.column.bundle->nchannels (); ++j) {
327                                 
328                                 ARDOUR::BundleChannel c[2];
329                                 c[_matrix->column_index()] = ARDOUR::BundleChannel (node.row.bundle, i);
330                                 c[_matrix->row_index()] = ARDOUR::BundleChannel (node.column.bundle, j);
331                                 
332                                 PortMatrixNode::State const s = _matrix->get_state (c);
333                                 
334                                 switch (s) {
335                                 case PortMatrixNode::ASSOCIATED:
336                                         if (i == j) {
337                                                 have_diagonal_association = true;
338                                         } else {
339                                                 have_off_diagonal_association = true;
340                                         }
341                                         break;
342                                         
343                                 case PortMatrixNode::UNKNOWN:
344                                         have_unknown = true;
345                                         break;
346                                         
347                                 case PortMatrixNode::NOT_ASSOCIATED:
348                                         if (i == j) {
349                                                 have_diagonal_not_association = true;
350                                         }
351                                         break;
352                                         
353                                 default:
354                                         break;
355                                 }
356                         }
357                 }
358                 
359                 if (have_unknown) {
360                         return PortMatrixNode::UNKNOWN;
361                 } else if (have_diagonal_association && !have_off_diagonal_association && !have_diagonal_not_association) {
362                         return PortMatrixNode::ASSOCIATED;
363                 } else if (!have_diagonal_association && !have_off_diagonal_association) {
364                         return PortMatrixNode::NOT_ASSOCIATED;
365                 }
366                 
367                 return PortMatrixNode::PARTIAL;
368
369         } else {
370
371                 ARDOUR::BundleChannel c[2];
372                 c[_matrix->column_index()] = node.column;
373                 c[_matrix->row_index()] = node.row;
374                 return _matrix->get_state (c);
375
376         }
377
378         return PortMatrixNode::UNKNOWN;
379 }
380
381 void
382 PortMatrixGrid::set_association (PortMatrixNode node, bool s)
383 {
384         if (_matrix->show_only_bundles()) {
385                                 
386                 for (uint32_t i = 0; i < node.column.bundle->nchannels(); ++i) {
387                         for (uint32_t j = 0; j < node.row.bundle->nchannels(); ++j) {
388                                 
389                                 ARDOUR::BundleChannel c[2];
390                                 c[_matrix->column_index()] = ARDOUR::BundleChannel (node.column.bundle, i);
391                                 c[_matrix->row_index()] = ARDOUR::BundleChannel (node.row.bundle, j);
392                                 _matrix->set_state (c, s && (i == j));
393                         }
394                 }
395                 
396         } else {
397                 
398                 if (node.row.bundle && node.column.bundle) {
399                         
400                         ARDOUR::BundleChannel c[2];
401                         c[_matrix->row_index()] = node.row;
402                         c[_matrix->column_index()] = node.column;
403                         _matrix->set_state (c, s);
404                 }
405         }
406 }
407
408 void
409 PortMatrixGrid::button_release (double x, double y, int b, uint32_t /*t*/)
410 {
411         if (b == 1) {
412
413                 if (_dragging && _moved) {
414
415                         if (_drag_valid) {
416                                 list<PortMatrixNode> const p = nodes_on_line (_drag_start_x, _drag_start_y, _drag_x, _drag_y);
417                                 
418                                 if (!p.empty()) {
419                                         PortMatrixNode::State const s = get_association (p.front());
420                                         for (list<PortMatrixNode>::const_iterator i = p.begin(); i != p.end(); ++i) {
421                                                 set_association (*i, toggle_state (s));
422                                         }
423                                 }
424                         }
425
426                 } else {
427
428                         PortMatrixNode const n = position_to_node (x / grid_spacing(), y / grid_spacing());
429                         if (n.row.bundle && n.column.bundle) {
430                                 PortMatrixNode::State const s = get_association (n);
431                                 set_association (n, toggle_state (s));
432                         }
433                 }
434
435                 require_render ();
436                 _body->queue_draw ();
437         }
438
439         _dragging = false;
440 }
441
442
443 void
444 PortMatrixGrid::draw_extra (cairo_t* cr)
445 {
446         set_source_rgba (cr, mouseover_line_colour(), 0.3);
447         cairo_set_line_width (cr, mouseover_line_width());
448
449         double const x = component_to_parent_x (channel_to_position (_body->mouseover().column, _matrix->columns()) * grid_spacing()) + grid_spacing() / 2;
450         double const y = component_to_parent_y (channel_to_position (_body->mouseover().row, _matrix->rows()) * grid_spacing()) + grid_spacing() / 2;
451
452         if (_body->mouseover().row.bundle && _body->mouseover().column.bundle) {
453
454                 cairo_move_to (cr, x, y);
455                 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
456                         cairo_line_to (cr, component_to_parent_x (0), y);
457                 } else if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
458                         cairo_line_to (cr, _parent_rectangle.get_x() + _parent_rectangle.get_width(), y);
459                 }
460                 cairo_stroke (cr);
461
462                 cairo_move_to (cr, x, y);
463                 if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
464                         cairo_line_to (cr, x, _parent_rectangle.get_y() + _parent_rectangle.get_height());
465                 } else if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
466                         cairo_line_to (cr, x, component_to_parent_y (0));
467                 }
468                 cairo_stroke (cr);
469         }
470
471         if (_dragging && _drag_valid && _moved) {
472
473                 list<PortMatrixNode> const p = nodes_on_line (_drag_start_x, _drag_start_y, _drag_x, _drag_y);
474
475                 if (!p.empty()) {
476
477                         bool const s = toggle_state (get_association (p.front()));
478
479                         for (list<PortMatrixNode>::const_iterator i = p.begin(); i != p.end(); ++i) {
480                                 if (s) {
481                                         draw_association_indicator (
482                                                 cr,
483                                                 component_to_parent_x (channel_to_position (i->column, _matrix->columns()) * grid_spacing ()),
484                                                 component_to_parent_y (channel_to_position (i->row, _matrix->rows()) * grid_spacing ())
485                                                 );
486                                 } else {
487                                         draw_empty_square (
488                                                 cr,
489                                                 component_to_parent_x (channel_to_position (i->column, _matrix->columns()) * grid_spacing ()),
490                                                 component_to_parent_y (channel_to_position (i->row, _matrix->rows()) * grid_spacing ())
491                                                 );
492                                 }
493                         }
494                 }               
495                 
496                 set_source_rgba (cr, association_colour (), 0.3);
497
498                 cairo_move_to (
499                         cr,
500                         component_to_parent_x (_drag_start_x * grid_spacing() + grid_spacing() / 2),
501                         component_to_parent_y (_drag_start_y * grid_spacing() + grid_spacing() / 2)
502                         );
503                 
504                 cairo_line_to (
505                         cr,
506                         component_to_parent_x (_drag_x * grid_spacing() + grid_spacing() / 2),
507                         component_to_parent_y (_drag_y * grid_spacing() + grid_spacing() / 2)
508                         );
509                 
510                 cairo_stroke (cr);
511
512         }
513 }
514
515 void
516 PortMatrixGrid::mouseover_changed (PortMatrixNode const& old)
517 {
518         queue_draw_for (old);
519         queue_draw_for (_body->mouseover());
520 }
521
522 void
523 PortMatrixGrid::motion (double x, double y)
524 {
525         _body->set_mouseover (position_to_node (x / grid_spacing(), y / grid_spacing()));
526
527         int const px = x / grid_spacing ();
528         int const py = y / grid_spacing ();
529
530         if (_dragging && !_moved && ( (px != _drag_start_x || py != _drag_start_x) )) {
531                 _moved = true;
532         }
533
534         if (_dragging && _drag_valid && _moved) {
535                 _drag_x = px;
536                 _drag_y = py;
537                 _body->queue_draw ();
538         }
539 }
540
541 void
542 PortMatrixGrid::queue_draw_for (PortMatrixNode const &n)
543 {
544         if (n.row.bundle) {
545
546                 double const y = channel_to_position (n.row, _matrix->rows()) * grid_spacing ();
547                 _body->queue_draw_area (
548                         _parent_rectangle.get_x(),
549                         component_to_parent_y (y),
550                         _parent_rectangle.get_width(),
551                         grid_spacing()
552                         );
553         }
554
555         if (n.column.bundle) {
556
557                 double const x = channel_to_position (n.column, _matrix->columns()) * grid_spacing ();
558                 
559                 _body->queue_draw_area (
560                         component_to_parent_x (x),
561                         _parent_rectangle.get_y(),
562                         grid_spacing(),
563                         _parent_rectangle.get_height()
564                         );
565         }
566 }
567
568 double
569 PortMatrixGrid::component_to_parent_x (double x) const
570 {
571         return x - _body->xoffset() + _parent_rectangle.get_x();
572 }
573
574 double
575 PortMatrixGrid::parent_to_component_x (double x) const
576 {
577         return x + _body->xoffset() - _parent_rectangle.get_x();
578 }
579
580 double
581 PortMatrixGrid::component_to_parent_y (double y) const
582 {
583         return y - _body->yoffset() + _parent_rectangle.get_y();
584 }
585
586 double
587 PortMatrixGrid::parent_to_component_y (double y) const
588 {
589         return y + _body->yoffset() - _parent_rectangle.get_y();
590 }
591
592 list<PortMatrixNode>
593 PortMatrixGrid::nodes_on_line (int x0, int y0, int x1, int y1) const
594 {
595         list<PortMatrixNode> p;
596
597         bool const steep = abs (y1 - y0) > abs (x1 - x0);
598         if (steep) {
599                 int tmp = x0;
600                 x0 = y0;
601                 y0 = tmp;
602
603                 tmp = y1;
604                 y1 = x1;
605                 x1 = tmp;
606         }
607
608         if (x0 > x1) {
609                 int tmp = x0;
610                 x0 = x1;
611                 x1 = tmp;
612
613                 tmp = y0;
614                 y0 = y1;
615                 y1 = tmp;
616         }
617
618         int dx = x1 - x0;
619         int dy = abs (y1 - y0);
620         
621         double err = 0;
622         double derr = double (dy) / dx;
623
624         int y = y0;
625         int const ystep = y0 < y1 ? 1 : -1;
626
627         for (int x = x0; x <= x1; ++x) {
628                 if (steep) {
629                         PortMatrixNode n = position_to_node (y, x);
630                         if (n.row.bundle && n.column.bundle) {
631                                 p.push_back (n);
632                         }
633                 } else {
634                         PortMatrixNode n = position_to_node (x, y);
635                         if (n.row.bundle && n.column.bundle) {
636                                 p.push_back (n);
637                         }
638                 }
639
640                 err += derr;
641
642                 if (err >= 0.5) {
643                         y += ystep;
644                         err -= 1;
645                 }
646         }
647
648         return p;
649 }
650
651 bool
652 PortMatrixGrid::toggle_state (PortMatrixNode::State s) const
653 {
654         return (s == PortMatrixNode::NOT_ASSOCIATED || s == PortMatrixNode::PARTIAL);
655 }