ardour
port_matrix_grid.cc
Go to the documentation of this file.
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.h>
22 #include "ardour/bundle.h"
23 #include "port_matrix_grid.h"
24 #include "port_matrix.h"
25 #include "port_matrix_body.h"
26 #include "keyboard.h"
27 
28 using namespace std;
30 
32  : PortMatrixComponent (m, b),
33  _dragging (false),
34  _drag_valid (false),
35  _moved (false)
36 {
37 
38 }
39 
40 void
42 {
43  if (_matrix->visible_columns()) {
45  } else {
46  _width = 0;
47  }
48 
49  if (_matrix->visible_rows()) {
51  } else {
52  _height = 0;
53  }
54 }
55 
56 
57 void
59 {
61  cairo_rectangle (cr, 0, 0, _width, _height);
62  cairo_fill (cr);
63 
64  PortGroup::BundleList const & row_bundles = _matrix->visible_rows()->bundles();
65  PortGroup::BundleList const & column_bundles = _matrix->visible_columns()->bundles();
66 
67  uint32_t x = 0;
68 
69  /* VERTICAL GRID LINES */
70 
72  uint32_t N = 0;
73 
74  for (PortGroup::BundleList::const_iterator i = column_bundles.begin(); i != column_bundles.end(); ++i) {
75 
76  cairo_set_line_width (cr, thick_grid_line_width());
77  cairo_move_to (cr, x, 0);
78  cairo_line_to (cr, x, _height);
79  cairo_stroke (cr);
80 
81  if (!_matrix->show_only_bundles()) {
82  cairo_set_line_width (cr, thin_grid_line_width());
83  for (uint32_t j = 0; j < _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()); ++j) {
84  x += grid_spacing ();
85  cairo_move_to (cr, x, 0);
86  cairo_line_to (cr, x, _height);
87  cairo_stroke (cr);
88  }
89 
90  } else {
91 
92  x += grid_spacing ();
93 
94  }
95 
96  ++N;
97  }
98 
99  if (_matrix->show_only_bundles ()) {
100  cairo_move_to (cr, x, 0);
101  cairo_line_to (cr, x, _height);
102  cairo_stroke (cr);
103  }
104 
105  uint32_t y = 0;
106 
107  /* HORIZONTAL GRID LINES */
108 
109  N = 0;
110  for (PortGroup::BundleList::const_iterator i = row_bundles.begin(); i != row_bundles.end(); ++i) {
111 
112  cairo_set_line_width (cr, thick_grid_line_width());
113  cairo_move_to (cr, 0, y);
114  cairo_line_to (cr, _width, y);
115  cairo_stroke (cr);
116 
117  if (!_matrix->show_only_bundles()) {
118  cairo_set_line_width (cr, thin_grid_line_width());
119  for (uint32_t j = 0; j < _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()); ++j) {
120  y += grid_spacing ();
121  cairo_move_to (cr, 0, y);
122  cairo_line_to (cr, _width, y);
123  cairo_stroke (cr);
124  }
125 
126  } else {
127 
128  y += grid_spacing ();
129 
130  }
131 
132  ++N;
133  }
134 
135  if (_matrix->show_only_bundles ()) {
136  cairo_move_to (cr, 0, y);
137  cairo_line_to (cr, _width, y);
138  cairo_stroke (cr);
139  }
140 
141  /* ASSOCIATION INDICATORS and NON-CONNECTABLE INDICATORS */
142 
143  /* we draw a grey square in a matrix box if the two ports that intersect at that box
144  cannot be connected because they are of different types (MIDI vs. audio)
145  */
146 
147  uint32_t bx = 0;
148  uint32_t by = 0;
149 
150  if (_matrix->show_only_bundles()) {
151 
152  for (PortGroup::BundleList::const_iterator i = column_bundles.begin(); i != column_bundles.end(); ++i) {
153  by = 0;
154 
155  for (PortGroup::BundleList::const_iterator j = row_bundles.begin(); j != row_bundles.end(); ++j) {
156 
158  ARDOUR::BundleChannel ((*j)->bundle, 0),
159  ARDOUR::BundleChannel ((*i)->bundle, 0)
160  ));
161  switch (s) {
163  draw_association_indicator (cr, bx, by);
164  break;
166  draw_association_indicator (cr, bx, by, 0.5);
167  break;
168  default:
169  break;
170  }
171 
172  by += grid_spacing();
173  }
174 
175  bx += grid_spacing();
176 
177  }
178 
179  } else {
180 
181  for (PortGroup::BundleList::const_iterator i = column_bundles.begin(); i != column_bundles.end(); ++i) {
182  by = 0;
183 
184  for (PortGroup::BundleList::const_iterator j = row_bundles.begin(); j != row_bundles.end(); ++j) {
185 
186  x = bx;
187  for (uint32_t k = 0; k < _matrix->count_of_our_type ((*i)->bundle->nchannels()); ++k) {
188 
189  y = by;
190  for (uint32_t l = 0; l < _matrix->count_of_our_type ((*j)->bundle->nchannels()); ++l) {
191 
193 
195  (*i)->bundle,
196  (*i)->bundle->type_channel_to_overall (_matrix->type (), k)
197  );
198 
200  (*j)->bundle,
201  (*j)->bundle->type_channel_to_overall (_matrix->type (), l)
202  );
203 
204  if (c[0].bundle->channel_type (c[0].channel) != c[1].bundle->channel_type (c[1].channel)) {
205  /* these two channels are of different types */
207  } else {
208  /* these two channels might be associated */
209  PortMatrixNode::State const s = _matrix->get_state (c);
210 
211  switch (s) {
213  draw_association_indicator (cr, x, y);
214  break;
215 
217  break;
218 
219  default:
220  break;
221  }
222  }
223 
224  y += grid_spacing();
225  }
226 
227  if (_matrix->count_of_our_type ((*j)->bundle->nchannels()) == 0) {
228  /* the *j bundle has no channels of our type, so it will have a dummy
229  one which needs to be marked non-connectable.
230  */
232  }
233 
234  x += grid_spacing();
235  }
236 
237  if (_matrix->count_of_our_type ((*i)->bundle->nchannels()) == 0) {
238  /* draw non-connectable indicators for the case where the *i bundle
239  has no channels of our type (and hence has 1 dummy channel)
240  */
241  y = by;
242  for (uint32_t l = 0; l < _matrix->count_of_our_type_min_1 ((*j)->bundle->nchannels()); ++l) {
244  y += grid_spacing ();
245  }
246  }
247 
248  by += _matrix->count_of_our_type_min_1 ((*j)->bundle->nchannels()) * grid_spacing();
249  }
250 
251  bx += _matrix->count_of_our_type_min_1 ((*i)->bundle->nchannels()) * grid_spacing();
252  }
253  }
254 }
255 
256 void
257 PortMatrixGrid::draw_association_indicator (cairo_t* cr, uint32_t x, uint32_t y, double p)
258 {
259  set_source_rgba (cr, association_colour(), 0.5);
260 
261  cairo_arc (
262  cr,
263  x + grid_spacing() / 2,
264  y + grid_spacing() / 2,
265  (grid_spacing() - (2 * connection_indicator_pad())) / 2,
266  0,
267  p * 2 * M_PI
268  );
269 
270  cairo_fill (cr);
271 }
272 
273 void
274 PortMatrixGrid::draw_empty_square (cairo_t* cr, uint32_t x, uint32_t y)
275 {
277  cairo_rectangle (
278  cr,
279  x + thick_grid_line_width(),
280  y + thick_grid_line_width(),
283  );
284  cairo_fill (cr);
285 }
286 
290 void
291 PortMatrixGrid::draw_non_connectable_indicator (cairo_t* cr, uint32_t x, uint32_t y)
292 {
294  cairo_rectangle (
295  cr,
296  x + thick_grid_line_width(),
297  y + thick_grid_line_width(),
300  );
301  cairo_fill (cr);
302 }
303 
305 PortMatrixGrid::position_to_node (double x, double y) const
306 {
307  return PortMatrixNode (
310  );
311 }
312 
313 void
314 PortMatrixGrid::button_press (double x, double y, GdkEventButton* ev)
315 {
318 
319  if (ev->button == 1) {
320 
321  _dragging = true;
322  _drag_valid = (px.bundle && py.bundle);
323 
324  _moved = false;
325  _drag_start_x = x / grid_spacing ();
326  _drag_start_y = y / grid_spacing ();
327 
328  } else if (ev->button == 3) {
329 
330  _matrix->popup_menu (px, py, ev->time);
331 
332  }
333 }
334 
335 void
337 {
338  if (_matrix->show_only_bundles()) {
339 
340  for (uint32_t i = 0; i < node.column.bundle->nchannels().n_total(); ++i) {
341  for (uint32_t j = 0; j < node.row.bundle->nchannels().n_total(); ++j) {
342 
343  if (!_matrix->should_show (node.column.bundle->channel_type(i)) || !_matrix->should_show (node.row.bundle->channel_type(j))) {
344  continue;
345  }
346 
350  _matrix->set_state (c, s && (i == j));
351  }
352  }
353 
354  } else {
355 
356  if (node.row.bundle && node.column.bundle) {
357 
359  c[_matrix->row_index()] = node.row;
360  c[_matrix->column_index()] = node.column;
361 
362  _matrix->set_state (c, s);
363  }
364  }
365 }
366 
367 void
368 PortMatrixGrid::button_release (double x, double y, GdkEventButton* ev)
369 {
370  if (ev->button == 1) {
371 
372  if (x != -1) {
373 
374  if (_dragging && _moved) {
375 
376  if (_drag_valid) {
377  list<PortMatrixNode> const p = nodes_on_line (_drag_start_x, _drag_start_y, _drag_x, _drag_y);
378 
379  if (!p.empty()) {
380  PortMatrixNode::State const s = _matrix->get_association (p.front());
381  for (list<PortMatrixNode>::const_iterator i = p.begin(); i != p.end(); ++i) {
382  set_association (*i, toggle_state (s));
383  }
384  }
385  }
386 
387  } else {
388 
389  if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
390  /* associate/disassociate things diagonally down and right until we run out */
392  while (1) {
393  PortMatrixNode const n = position_to_node (x, y);
394  if (n.row.bundle && n.column.bundle) {
395  if (s == (PortMatrixNode::State) 0) {
396  s = _matrix->get_association (n);
397  }
398  set_association (n, toggle_state (s));
399  } else {
400  break;
401  }
402  x += grid_spacing ();
403  y += grid_spacing ();
404  }
405 
406  } else {
407 
408  PortMatrixNode const n = position_to_node (x, y);
409  if (n.row.bundle && n.column.bundle) {
411  set_association (n, toggle_state (s));
412  }
413  }
414  }
415 
416  require_render ();
417  }
418 
419  _body->queue_draw ();
420  }
421 
422  _dragging = false;
423 }
424 
425 
426 void
428 {
430  cairo_set_line_width (cr, mouseover_line_width());
431 
432  list<PortMatrixNode> const m = _body->mouseover ();
433 
434  for (list<PortMatrixNode>::const_iterator i = m.begin(); i != m.end(); ++i) {
435 
436  double const x = component_to_parent_x (channel_to_position (i->column, _matrix->visible_columns()) * grid_spacing()) + grid_spacing() / 2;
437  double const y = component_to_parent_y (channel_to_position (i->row, _matrix->visible_rows()) * grid_spacing()) + grid_spacing() / 2;
438 
439  if (PortMatrix::bundle_with_channels (i->row.bundle) && PortMatrix::bundle_with_channels (i->column.bundle)) {
440 
441  cairo_move_to (cr, x, y);
443  cairo_line_to (cr, component_to_parent_x (0), y);
444  } else if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
445  cairo_line_to (cr, _parent_rectangle.get_x() + _parent_rectangle.get_width(), y);
446  }
447  cairo_stroke (cr);
448 
449  cairo_move_to (cr, x, y);
451  cairo_line_to (cr, x, _parent_rectangle.get_y() + _parent_rectangle.get_height());
452  } else if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
453  cairo_line_to (cr, x, component_to_parent_y (0));
454  }
455  cairo_stroke (cr);
456  }
457  }
458 
459  if (_dragging && _drag_valid && _moved) {
460 
461  list<PortMatrixNode> const p = nodes_on_line (_drag_start_x, _drag_start_y, _drag_x, _drag_y);
462 
463  if (!p.empty()) {
464 
465  bool const s = toggle_state (_matrix->get_association (p.front()));
466 
467  for (list<PortMatrixNode>::const_iterator i = p.begin(); i != p.end(); ++i) {
468  if (s) {
470  cr,
473  );
474  } else {
476  cr,
479  );
480  }
481  }
482  }
483 
484  set_source_rgba (cr, association_colour (), 0.3);
485 
486  cairo_move_to (
487  cr,
490  );
491 
492  cairo_line_to (
493  cr,
496  );
497 
498  cairo_stroke (cr);
499 
500  }
501 }
502 
503 void
504 PortMatrixGrid::mouseover_changed (list<PortMatrixNode> const & old)
505 {
506  queue_draw_for (old);
508 }
509 
510 void
511 PortMatrixGrid::motion (double x, double y)
512 {
514 
515  int const px = x / grid_spacing ();
516  int const py = y / grid_spacing ();
517 
518  if (_dragging && !_moved && ( (px != _drag_start_x || py != _drag_start_x) )) {
519  _moved = true;
520  }
521 
522  if (_dragging && _drag_valid && _moved) {
523  _drag_x = px;
524  _drag_y = py;
525  _body->queue_draw ();
526  }
527 }
528 
529 void
530 PortMatrixGrid::queue_draw_for (list<PortMatrixNode> const &n)
531 {
532  for (list<PortMatrixNode>::const_iterator i = n.begin(); i != n.end(); ++i) {
533 
534  if (i->row.bundle) {
535 
536  double const y = channel_to_position (i->row, _matrix->visible_rows()) * grid_spacing ();
537  _body->queue_draw_area (
538  _parent_rectangle.get_x(),
540  _parent_rectangle.get_width(),
541  grid_spacing()
542  );
543  }
544 
545  if (i->column.bundle) {
546 
547  double const x = channel_to_position (i->column, _matrix->visible_columns()) * grid_spacing ();
548 
549  _body->queue_draw_area (
551  _parent_rectangle.get_y(),
552  grid_spacing(),
553  _parent_rectangle.get_height()
554  );
555  }
556  }
557 }
558 
559 double
561 {
562  return x - _body->xoffset() + _parent_rectangle.get_x();
563 }
564 
565 double
567 {
568  return x + _body->xoffset() - _parent_rectangle.get_x();
569 }
570 
571 double
573 {
574  return y - _body->yoffset() + _parent_rectangle.get_y();
575 }
576 
577 double
579 {
580  return y + _body->yoffset() - _parent_rectangle.get_y();
581 }
582 
583 list<PortMatrixNode>
584 PortMatrixGrid::nodes_on_line (int x0, int y0, int x1, int y1) const
585 {
586  list<PortMatrixNode> p;
587 
588  bool const steep = abs (y1 - y0) > abs (x1 - x0);
589  if (steep) {
590  int tmp = x0;
591  x0 = y0;
592  y0 = tmp;
593 
594  tmp = y1;
595  y1 = x1;
596  x1 = tmp;
597  }
598 
599  if (x0 > x1) {
600  int tmp = x0;
601  x0 = x1;
602  x1 = tmp;
603 
604  tmp = y0;
605  y0 = y1;
606  y1 = tmp;
607  }
608 
609  int dx = x1 - x0;
610  int dy = abs (y1 - y0);
611 
612  double err = 0;
613  double derr = double (dy) / dx;
614 
615  int y = y0;
616  int const ystep = y0 < y1 ? 1 : -1;
617 
618  for (int x = x0; x <= x1; ++x) {
619  if (steep) {
621  if (n.row.bundle && n.column.bundle) {
622  p.push_back (n);
623  }
624  } else {
626  if (n.row.bundle && n.column.bundle) {
627  p.push_back (n);
628  }
629  }
630 
631  err += derr;
632 
633  if (err >= 0.5) {
634  y += ystep;
635  err -= 1;
636  }
637  }
638 
639  return p;
640 }
641 
642 bool
644 {
646 }
PortMatrixNode position_to_node(double, double) const
virtual void set_state(ARDOUR::BundleChannel c[2], bool s)=0
void button_release(double, double, GdkEventButton *)
void popup_menu(ARDOUR::BundleChannel, ARDOUR::BundleChannel, uint32_t)
Definition: port_matrix.cc:418
void motion(double, double)
void set_source_rgb(cairo_t *, Gdk::Color const &)
the ports are not associated
void render(cairo_t *)
static Gdk::Color association_colour()
void set_association(PortMatrixNode, bool)
std::list< BundleRecord * > BundleList
Definition: port_group.h:86
uint32_t xoffset() const
static int N
Definition: signals_test.cc:27
bool toggle_state(PortMatrixNode::State) const
bool should_show(ARDOUR::DataType) const
Definition: Beats.hpp:239
static uint32_t grid_spacing()
std::list< PortMatrixNode > mouseover() const
virtual PortMatrixNode::State get_state(ARDOUR::BundleChannel c[2]) const =0
uint32_t count_of_our_type(ARDOUR::ChanCount) const
std::list< PortMatrixNode > nodes_on_line(int, int, int, int) const
static Gdk::Color grid_colour()
the ports are associated
ARDOUR::BundleChannel row
column labels on top, row labels to the right
Definition: port_matrix.h:84
uint32_t _height
full height of the contents
PortMatrixNode::State get_association(PortMatrixNode) const
LIBGTKMM2EXT_API uint64_t Keyboard
Definition: debug.cc:23
double parent_to_component_x(double x) const
static uint32_t connection_indicator_pad()
double component_to_parent_y(double y) const
uint32_t group_size(boost::shared_ptr< const PortGroup >) const
virtual ARDOUR::BundleChannel position_to_channel(double, double, boost::shared_ptr< const PortGroup >) const
uint32_t channel_to_position(ARDOUR::BundleChannel, boost::shared_ptr< const PortGroup >) const
void button_press(double, double, GdkEventButton *)
uint32_t yoffset() const
uint32_t count_of_our_type_min_1(ARDOUR::ChanCount) const
Gdk::Rectangle _parent_rectangle
double component_to_parent_x(double x) const
static Gdk::Color non_connectable_colour()
PortMatrixBody * _body
the PortMatrixBody that we're in
PortMatrixGrid(PortMatrix *, PortMatrixBody *)
row labels to the left, column labels on the bottom
Definition: port_matrix.h:85
void set_source_rgba(cairo_t *, Gdk::Color const &, double)
bool show_only_bundles() const
Definition: port_matrix.h:94
static Gdk::Color mouseover_line_colour()
static uint32_t mouseover_line_width()
ARDOUR::BundleChannel column
ARDOUR::DataType type() const
Definition: port_matrix.h:68
void draw_empty_square(cairo_t *, uint32_t, uint32_t)
void set_mouseover(PortMatrixNode const &)
uint32_t _width
full width of the contents
int row_index() const
Definition: port_matrix.h:110
void draw_non_connectable_indicator(cairo_t *, uint32_t, uint32_t)
void draw_extra(cairo_t *)
boost::shared_ptr< Bundle > bundle
Definition: bundle.h:168
double parent_to_component_y(double y) const
used when we are examining bundles; the bundles are partially associated
void mouseover_changed(std::list< PortMatrixNode > const &)
void draw_association_indicator(cairo_t *, uint32_t, uint32_t, double p=1)
boost::shared_ptr< const PortGroup > visible_columns() const
Definition: port_matrix.cc:396
int channel
channel index, or -1 for "all"
Definition: bundle.h:169
void queue_draw_for(std::list< PortMatrixNode > const &)
boost::shared_ptr< const PortGroup > visible_rows() const
Definition: port_matrix.cc:409
static double thick_grid_line_width()
static bool bundle_with_channels(boost::shared_ptr< ARDOUR::Bundle >)
Arrangement arrangement() const
Definition: port_matrix.h:90
int column_index() const
Definition: port_matrix.h:102
static double thin_grid_line_width()