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