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>
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.
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.
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.
22 #include "ardour/bundle.h"
23 #include "ardour/types.h"
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"
36 PortMatrixBody::PortMatrixBody (PortMatrix* p)
42 _column_labels_border_x (0),
43 _column_labels_height (0),
44 _ignore_component_size_changed (false)
46 _column_labels = new PortMatrixColumnLabels (p, this);
47 _row_labels = new PortMatrixRowLabels (p, this, *_column_labels);
48 _grid = new PortMatrixGrid (p, this);
50 _components.push_back (_column_labels);
51 _components.push_back (_row_labels);
52 _components.push_back (_grid);
54 add_events (Gdk::LEAVE_NOTIFY_MASK | Gdk::POINTER_MOTION_MASK);
58 PortMatrixBody::~PortMatrixBody ()
60 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
66 PortMatrixBody::on_expose_event (GdkEventExpose* event)
69 _matrix->visible_columns() == 0 || _matrix->visible_rows() == 0 ||
70 _matrix->visible_columns()->bundles().empty() || _matrix->visible_rows()->bundles().empty()
73 /* nothing to connect */
75 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
77 cairo_set_source_rgb (cr, 0, 0, 0);
78 cairo_rectangle (cr, 0, 0, _alloc_width, _alloc_height);
82 if (_matrix->type() == ARDOUR::DataType::NIL) {
83 t = _("There are no ports to connect.");
85 t = string_compose (_("There are no %1 ports to connect."), _matrix->type().to_i18n_string());
88 cairo_text_extents_t ext;
89 cairo_text_extents (cr, t.c_str(), &ext);
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 ());
100 Gdk::Rectangle const exposure (
101 event->area.x, event->area.y, event->area.width, event->area.height
106 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
108 Gdk::Rectangle r = exposure;
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);
118 get_window()->gobj(),
119 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
121 (*i)->parent_to_component_x (r.get_x()),
122 (*i)->parent_to_component_y (r.get_y()),
132 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
134 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
136 set_cairo_clip (cr, (*i)->parent_rectangle ());
137 (*i)->draw_extra (cr);
147 PortMatrixBody::on_size_request (Gtk::Requisition *req)
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 ();
153 if (grid.first == 0 && grid.second == 0) {
154 /* nothing to display */
160 /* don't ask for the maximum size of our contents, otherwise GTK won't
161 let the containing window shrink below this size */
163 /* XXX these shouldn't be hard-coded */
164 int const min_width = 512;
165 int const min_height = 512;
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);
172 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
174 Gtk::EventBox::on_size_allocate (alloc);
176 _alloc_width = alloc.get_width ();
177 _alloc_height = alloc.get_height ();
179 compute_rectangles ();
180 _matrix->setup_scrollbars ();
184 PortMatrixBody::compute_rectangles ()
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 ();
192 Gdk::Rectangle col_rect;
193 Gdk::Rectangle row_rect;
194 Gdk::Rectangle grid_rect;
196 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
199 _column_labels_border_x = col_overhang;
203 col_rect.set_width (min (col.first, _alloc_width));
205 uint32_t const y = min (_alloc_height, col.second);
206 col_rect.set_height (y);
208 row_rect.set_height (_alloc_height - y);
210 grid_rect.set_height (_alloc_height - y);
213 if (_alloc_width > (grid.first + row.first)) {
215 } else if (_alloc_width > row.first) {
216 x = _alloc_width - row.first;
219 grid_rect.set_width (x);
221 row_rect.set_width (_alloc_width - x);
224 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
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));
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));
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 ());
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());
244 _column_labels_height = col_rect.get_height ();
246 _row_labels->set_parent_rectangle (row_rect);
247 _column_labels->set_parent_rectangle (col_rect);
248 _grid->set_parent_rectangle (grid_rect);
250 DimensionsChanged (); /* EMIT SIGNAL */
254 PortMatrixBody::setup ()
256 /* Discard any old connections to bundles */
258 _bundle_connections.drop_connections ();
260 /* Connect to bundles so that we find out when their names change */
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) {
266 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_row_labels, this), gui_context());
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());
278 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
282 set_mouseover (PortMatrixNode ());
284 _ignore_component_size_changed = true;
285 compute_rectangles ();
286 _ignore_component_size_changed = false;
290 PortMatrixBody::full_scroll_width ()
292 return _grid->dimensions().first;
297 PortMatrixBody::alloc_scroll_width ()
299 return _grid->parent_rectangle().get_width();
303 PortMatrixBody::full_scroll_height ()
305 return _grid->dimensions().second;
309 PortMatrixBody::alloc_scroll_height ()
311 return _grid->parent_rectangle().get_height();
314 /** Set x offset (for scrolling) */
316 PortMatrixBody::set_xoffset (uint32_t xo)
322 /** Set y offset (for scrolling) */
324 PortMatrixBody::set_yoffset (uint32_t yo)
331 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
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)) {
336 (*i)->parent_to_component_x (ev->x),
337 (*i)->parent_to_component_y (ev->y),
347 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
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),
357 (*i)->button_release (
368 PortMatrixBody::rebuild_and_draw_grid ()
370 _grid->require_rebuild ();
375 PortMatrixBody::rebuild_and_draw_column_labels ()
377 _column_labels->require_rebuild ();
382 PortMatrixBody::rebuild_and_draw_row_labels ()
384 _row_labels->require_rebuild ();
389 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
391 if (ev->type == GDK_LEAVE_NOTIFY) {
392 set_mouseover (PortMatrixNode ());
399 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
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)) {
406 (*i)->parent_to_component_x (ev->x),
407 (*i)->parent_to_component_y (ev->y)
416 set_mouseover (PortMatrixNode ());
423 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
425 list<PortMatrixNode> m;
431 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
433 if (n == _mouseover) {
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.
445 _row_labels->clear_channel_highlights ();
446 _column_labels->clear_channel_highlights ();
448 list<PortMatrixNode> old = _mouseover;
451 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
452 (*i)->mouseover_changed (old);
457 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
459 ARDOUR::BundleChannel bc[2];
462 if (!PortMatrix::bundle_with_channels (bc[dim].bundle)) {
466 if (dim == _matrix->column_index()) {
467 _column_labels->add_channel_highlight (bc[dim]);
469 _row_labels->add_channel_highlight (bc[dim]);
472 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
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) {
477 if (!_matrix->should_show ((*i)->bundle->channel_type(j))) {
481 bc[1 - dim] = ARDOUR::BundleChannel ((*i)->bundle, j);
484 n.row = bc[_matrix->row_index()];
485 n.column = bc[_matrix->column_index()];
487 if (_matrix->get_association(n) != PortMatrixNode::NOT_ASSOCIATED) {
488 if (dim == _matrix->column_index()) {
489 _row_labels->add_channel_highlight (bc[1 - dim]);
491 _column_labels->add_channel_highlight (bc[1 - dim]);
499 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
501 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
506 PortMatrixBody::component_size_changed ()
508 if (_ignore_component_size_changed) {
512 compute_rectangles ();
513 _matrix->setup_scrollbars ();
516 pair<uint32_t, uint32_t>
517 PortMatrixBody::max_size () const
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 ();
523 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
526 /** @return x position at which the column labels meet the border of the matrix */
528 PortMatrixBody::column_labels_border_x () const
530 return _column_labels_border_x;
534 PortMatrixBody::column_labels_height () const
536 return _column_labels_height;