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"
31 #include "ui_config.h"
37 PortMatrixBody::PortMatrixBody (PortMatrix* p)
43 _column_labels_border_x (0),
44 _column_labels_height (0),
45 _ignore_component_size_changed (false)
47 _column_labels = new PortMatrixColumnLabels (p, this);
48 _row_labels = new PortMatrixRowLabels (p, this, *_column_labels);
49 _grid = new PortMatrixGrid (p, this);
51 _components.push_back (_column_labels);
52 _components.push_back (_row_labels);
53 _components.push_back (_grid);
55 add_events (Gdk::LEAVE_NOTIFY_MASK | Gdk::POINTER_MOTION_MASK);
59 PortMatrixBody::~PortMatrixBody ()
61 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
67 PortMatrixBody::on_expose_event (GdkEventExpose* event)
70 _matrix->visible_columns() == 0 || _matrix->visible_rows() == 0 ||
71 _matrix->visible_columns()->bundles().empty() || _matrix->visible_rows()->bundles().empty()
74 /* nothing to connect */
76 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
77 cairo_set_font_size (cr, UIConfiguration::instance().get_ui_scale() * 10);
79 cairo_set_source_rgb (cr, 0, 0, 0);
80 cairo_rectangle (cr, 0, 0, _alloc_width, _alloc_height);
84 if (_matrix->type() == ARDOUR::DataType::NIL) {
85 t = _("There are no ports to connect.");
87 t = string_compose (_("There are no %1 ports to connect."), _matrix->type().to_i18n_string());
90 cairo_text_extents_t ext;
91 cairo_text_extents (cr, t.c_str(), &ext);
93 cairo_set_source_rgb (cr, 1, 1, 1);
94 cairo_move_to (cr, (_alloc_width - ext.width) / 2, (_alloc_height + ext.height) / 2);
95 cairo_show_text (cr, t.c_str ());
102 Gdk::Rectangle const exposure (
103 event->area.x, event->area.y, event->area.width, event->area.height
108 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
110 Gdk::Rectangle r = exposure;
112 /* the get_pixmap call may cause things to be rerendered and sizes to change,
113 so fetch the pixmap before calculating where to put it */
114 GdkPixmap* p = (*i)->get_pixmap (get_window()->gobj());
115 r.intersect ((*i)->parent_rectangle(), intersects);
120 get_window()->gobj(),
121 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
123 (*i)->parent_to_component_x (r.get_x()),
124 (*i)->parent_to_component_y (r.get_y()),
134 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
135 cairo_set_font_size (cr, UIConfiguration::instance().get_ui_scale() * 10);
137 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
139 set_cairo_clip (cr, (*i)->parent_rectangle ());
140 (*i)->draw_extra (cr);
150 PortMatrixBody::on_size_request (Gtk::Requisition *req)
152 pair<int, int> const col = _column_labels->dimensions ();
153 pair<int, int> const row = _row_labels->dimensions ();
154 pair<int, int> const grid = _grid->dimensions ();
156 if (grid.first == 0 && grid.second == 0) {
157 /* nothing to display */
163 /* don't ask for the maximum size of our contents, otherwise GTK won't
164 let the containing window shrink below this size */
166 /* XXX these shouldn't be hard-coded */
167 int const min_width = 512;
168 int const min_height = 512;
170 req->width = min (min_width, max (col.first, grid.first + row.first));
171 req->height = min (min_height / _matrix->min_height_divisor(), col.second + grid.second);
175 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
177 Gtk::EventBox::on_size_allocate (alloc);
179 _alloc_width = alloc.get_width ();
180 _alloc_height = alloc.get_height ();
182 compute_rectangles ();
183 _matrix->setup_scrollbars ();
187 PortMatrixBody::compute_rectangles ()
189 /* full sizes of components */
190 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
191 uint32_t const col_overhang = _column_labels->overhang ();
192 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
193 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
195 Gdk::Rectangle col_rect;
196 Gdk::Rectangle row_rect;
197 Gdk::Rectangle grid_rect;
199 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
202 _column_labels_border_x = col_overhang;
206 col_rect.set_width (min (col.first, _alloc_width));
208 uint32_t const y = min (_alloc_height, col.second);
209 col_rect.set_height (y);
211 row_rect.set_height (_alloc_height - y);
213 grid_rect.set_height (_alloc_height - y);
216 if (_alloc_width > (grid.first + row.first)) {
218 } else if (_alloc_width > row.first) {
219 x = _alloc_width - row.first;
222 grid_rect.set_width (x);
224 row_rect.set_width (_alloc_width - x);
227 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
229 col_rect.set_height (min (_alloc_height, col.second));
230 row_rect.set_height (std::min (_alloc_height - col_rect.get_height(), row.second));
233 row_rect.set_y (_alloc_height - row_rect.get_height() - col_rect.get_height());
234 row_rect.set_width (min (_alloc_width, row.first));
236 grid_rect.set_x (row_rect.get_width());
237 grid_rect.set_y (_alloc_height - row_rect.get_height() - col_rect.get_height());
238 grid_rect.set_width (std::min (_alloc_width - row_rect.get_width(), grid.first));
239 grid_rect.set_height (row_rect.get_height ());
241 col_rect.set_width (grid_rect.get_width () + col_overhang);
242 col_rect.set_x (row_rect.get_width() + grid_rect.get_width() - col_rect.get_width());
243 _column_labels_border_x = col_rect.get_x () >= 0 ? col_rect.get_x () : 0;
244 col_rect.set_y (_alloc_height - col_rect.get_height());
247 _column_labels_height = col_rect.get_height ();
249 _row_labels->set_parent_rectangle (row_rect);
250 _column_labels->set_parent_rectangle (col_rect);
251 _grid->set_parent_rectangle (grid_rect);
253 DimensionsChanged (); /* EMIT SIGNAL */
257 PortMatrixBody::setup ()
259 /* Discard any old connections to bundles */
261 _bundle_connections.drop_connections ();
263 /* Connect to bundles so that we find out when their names change */
265 if (_matrix->visible_rows()) {
266 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
267 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
269 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_row_labels, this), gui_context());
274 if (_matrix->visible_columns()) {
275 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
276 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
277 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_column_labels, this), gui_context());
281 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
285 set_mouseover (PortMatrixNode ());
287 _ignore_component_size_changed = true;
288 compute_rectangles ();
289 _ignore_component_size_changed = false;
293 PortMatrixBody::full_scroll_width ()
295 return _grid->dimensions().first;
300 PortMatrixBody::alloc_scroll_width ()
302 return _grid->parent_rectangle().get_width();
306 PortMatrixBody::full_scroll_height ()
308 return _grid->dimensions().second;
312 PortMatrixBody::alloc_scroll_height ()
314 return _grid->parent_rectangle().get_height();
317 /** Set x offset (for scrolling) */
319 PortMatrixBody::set_xoffset (uint32_t xo)
325 /** Set y offset (for scrolling) */
327 PortMatrixBody::set_yoffset (uint32_t yo)
334 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
336 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
337 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
339 (*i)->parent_to_component_x (ev->x),
340 (*i)->parent_to_component_y (ev->y),
350 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
352 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
353 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
354 (*i)->button_release (
355 (*i)->parent_to_component_x (ev->x),
356 (*i)->parent_to_component_y (ev->y),
360 (*i)->button_release (
371 PortMatrixBody::rebuild_and_draw_grid ()
373 _grid->require_rebuild ();
378 PortMatrixBody::rebuild_and_draw_column_labels ()
380 _column_labels->require_rebuild ();
385 PortMatrixBody::rebuild_and_draw_row_labels ()
387 _row_labels->require_rebuild ();
392 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
394 if (ev->type == GDK_LEAVE_NOTIFY) {
395 set_mouseover (PortMatrixNode ());
402 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
406 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
407 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
409 (*i)->parent_to_component_x (ev->x),
410 (*i)->parent_to_component_y (ev->y)
419 set_mouseover (PortMatrixNode ());
426 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
428 list<PortMatrixNode> m;
434 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
436 if (n == _mouseover) {
440 /* Channel highlights are set up only on mouseovers, so
441 it's reasonable to remove all channel highlights here.
442 We can't let individual components clear their own highlights
443 because of the case where, say, the row labels set up some column
444 highlights, and then we ask the column labels to set up their
445 own highlights and they clear them out before they start.
448 _row_labels->clear_channel_highlights ();
449 _column_labels->clear_channel_highlights ();
451 list<PortMatrixNode> old = _mouseover;
454 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
455 (*i)->mouseover_changed (old);
460 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
462 ARDOUR::BundleChannel bc[2];
465 if (!PortMatrix::bundle_with_channels (bc[dim].bundle)) {
469 if (dim == _matrix->column_index()) {
470 _column_labels->add_channel_highlight (bc[dim]);
472 _row_labels->add_channel_highlight (bc[dim]);
475 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
477 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
478 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
480 if (!_matrix->should_show ((*i)->bundle->channel_type(j))) {
484 bc[1 - dim] = ARDOUR::BundleChannel ((*i)->bundle, j);
487 n.row = bc[_matrix->row_index()];
488 n.column = bc[_matrix->column_index()];
490 if (_matrix->get_association(n) != PortMatrixNode::NOT_ASSOCIATED) {
491 if (dim == _matrix->column_index()) {
492 _row_labels->add_channel_highlight (bc[1 - dim]);
494 _column_labels->add_channel_highlight (bc[1 - dim]);
502 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
504 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
509 PortMatrixBody::component_size_changed ()
511 if (_ignore_component_size_changed) {
515 compute_rectangles ();
516 _matrix->setup_scrollbars ();
519 pair<uint32_t, uint32_t>
520 PortMatrixBody::max_size () const
522 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
523 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
524 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
526 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
529 /** @return x position at which the column labels meet the border of the matrix */
531 PortMatrixBody::column_labels_border_x () const
533 return _column_labels_border_x;
537 PortMatrixBody::column_labels_height () const
539 return _column_labels_height;