2 Copyright (C) 2002-2009 Paul Davis
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.
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.
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.
21 #include "ardour/bundle.h"
22 #include "ardour/types.h"
24 #include "gui_thread.h"
25 #include "port_matrix_body.h"
26 #include "port_matrix.h"
27 #include "port_matrix_column_labels.h"
28 #include "port_matrix_row_labels.h"
29 #include "port_matrix_grid.h"
35 PortMatrixBody::PortMatrixBody (PortMatrix* p)
41 _column_labels_border_x (0),
42 _column_labels_height (0),
43 _ignore_component_size_changed (false)
45 _column_labels = new PortMatrixColumnLabels (p, this);
46 _row_labels = new PortMatrixRowLabels (p, this);
47 _grid = new PortMatrixGrid (p, this);
49 _components.push_back (_column_labels);
50 _components.push_back (_row_labels);
51 _components.push_back (_grid);
53 add_events (Gdk::LEAVE_NOTIFY_MASK | Gdk::POINTER_MOTION_MASK);
57 PortMatrixBody::~PortMatrixBody ()
59 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
65 PortMatrixBody::on_expose_event (GdkEventExpose* event)
68 _matrix->visible_columns() == 0 || _matrix->visible_rows() == 0 ||
69 _matrix->visible_columns()->bundles().empty() || _matrix->visible_rows()->bundles().empty()
72 /* nothing to connect */
74 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
76 cairo_set_source_rgb (cr, 0, 0, 0);
77 cairo_rectangle (cr, 0, 0, _alloc_width, _alloc_height);
81 t << _("There are no ") << (_matrix->type() == ARDOUR::DataType::AUDIO ? _("audio") : _("MIDI")) << _(" ports to connect.");
83 cairo_text_extents_t ext;
84 cairo_text_extents (cr, t.str().c_str(), &ext);
86 cairo_set_source_rgb (cr, 1, 1, 1);
87 cairo_move_to (cr, (_alloc_width - ext.width) / 2, (_alloc_height + ext.height) / 2);
88 cairo_show_text (cr, t.str().c_str ());
95 Gdk::Rectangle const exposure (
96 event->area.x, event->area.y, event->area.width, event->area.height
101 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
103 Gdk::Rectangle r = exposure;
105 /* the get_pixmap call may cause things to be rerendered and sizes to change,
106 so fetch the pixmap before calculating where to put it */
107 GdkPixmap* p = (*i)->get_pixmap (get_window()->gobj());
108 r.intersect ((*i)->parent_rectangle(), intersects);
113 get_window()->gobj(),
114 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
116 (*i)->parent_to_component_x (r.get_x()),
117 (*i)->parent_to_component_y (r.get_y()),
127 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
129 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
131 set_cairo_clip (cr, (*i)->parent_rectangle ());
132 (*i)->draw_extra (cr);
142 PortMatrixBody::on_size_request (Gtk::Requisition *req)
144 pair<int, int> const col = _column_labels->dimensions ();
145 pair<int, int> const row = _row_labels->dimensions ();
146 pair<int, int> const grid = _grid->dimensions ();
148 if (grid.first == 0 && grid.second == 0) {
149 /* nothing to display */
155 /* don't ask for the maximum size of our contents, otherwise GTK won't
156 let the containing window shrink below this size */
158 /* XXX these shouldn't be hard-coded */
159 int const min_width = 512;
160 int const min_height = 512;
162 req->width = min (min_width, max (col.first, grid.first + row.first));
163 req->height = min (min_height / _matrix->min_height_divisor(), col.second + grid.second);
167 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
169 Gtk::EventBox::on_size_allocate (alloc);
171 _alloc_width = alloc.get_width ();
172 _alloc_height = alloc.get_height ();
174 compute_rectangles ();
175 _matrix->setup_scrollbars ();
179 PortMatrixBody::compute_rectangles ()
181 /* full sizes of components */
182 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
183 uint32_t const col_overhang = _column_labels->overhang ();
184 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
185 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
187 Gdk::Rectangle col_rect;
188 Gdk::Rectangle row_rect;
189 Gdk::Rectangle grid_rect;
191 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
194 _column_labels_border_x = col_overhang;
198 col_rect.set_width (min (col.first, _alloc_width));
200 uint32_t const y = min (_alloc_height, col.second);
201 col_rect.set_height (y);
203 row_rect.set_height (_alloc_height - y);
205 grid_rect.set_height (_alloc_height - y);
208 if (_alloc_width > (grid.first + row.first)) {
210 } else if (_alloc_width > row.first) {
211 x = _alloc_width - row.first;
214 grid_rect.set_width (x);
216 row_rect.set_width (_alloc_width - x);
219 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
221 col_rect.set_height (min (_alloc_height, col.second));
225 row_rect.set_width (min (_alloc_width, row.first));
226 row_rect.set_height (std::min (_alloc_height - col_rect.get_height(), row.second));
228 grid_rect.set_x (row_rect.get_width());
230 grid_rect.set_width (std::min (_alloc_width - row_rect.get_width(), grid.first));
231 grid_rect.set_height (row_rect.get_height ());
233 col_rect.set_width (grid_rect.get_width () + col_overhang);
234 col_rect.set_x (row_rect.get_width() + grid_rect.get_width() - col_rect.get_width());
235 _column_labels_border_x = col_rect.get_x () >= 0 ? col_rect.get_x () : 0;
236 col_rect.set_y (row_rect.get_height());
239 _column_labels_height = col_rect.get_height ();
241 _row_labels->set_parent_rectangle (row_rect);
242 _column_labels->set_parent_rectangle (col_rect);
243 _grid->set_parent_rectangle (grid_rect);
245 DimensionsChanged (); /* EMIT SIGNAL */
249 PortMatrixBody::setup ()
251 /* Discard any old connections to bundles */
253 _bundle_connections.drop_connections ();
255 /* Connect to bundles so that we find out when their names change */
257 if (_matrix->visible_rows()) {
258 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
259 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
261 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_row_labels, this), gui_context());
266 if (_matrix->visible_columns()) {
267 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
268 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
269 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_column_labels, this), gui_context());
273 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
277 set_mouseover (PortMatrixNode ());
279 _ignore_component_size_changed = true;
280 compute_rectangles ();
281 _ignore_component_size_changed = false;
285 PortMatrixBody::full_scroll_width ()
287 return _grid->dimensions().first;
292 PortMatrixBody::alloc_scroll_width ()
294 return _grid->parent_rectangle().get_width();
298 PortMatrixBody::full_scroll_height ()
300 return _grid->dimensions().second;
304 PortMatrixBody::alloc_scroll_height ()
306 return _grid->parent_rectangle().get_height();
309 /** Set x offset (for scrolling) */
311 PortMatrixBody::set_xoffset (uint32_t xo)
317 /** Set y offset (for scrolling) */
319 PortMatrixBody::set_yoffset (uint32_t yo)
326 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
328 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
329 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
331 (*i)->parent_to_component_x (ev->x),
332 (*i)->parent_to_component_y (ev->y),
333 ev->button, ev->time, ev->state
342 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
344 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
345 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
346 (*i)->button_release (
347 (*i)->parent_to_component_x (ev->x),
348 (*i)->parent_to_component_y (ev->y),
349 ev->button, ev->time, ev->state
352 (*i)->button_release (
354 ev->button, ev->time, ev->state
363 PortMatrixBody::rebuild_and_draw_grid ()
365 _grid->require_rebuild ();
370 PortMatrixBody::rebuild_and_draw_column_labels ()
372 _column_labels->require_rebuild ();
377 PortMatrixBody::rebuild_and_draw_row_labels ()
379 _row_labels->require_rebuild ();
384 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
386 if (ev->type == GDK_LEAVE_NOTIFY) {
387 set_mouseover (PortMatrixNode ());
394 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
398 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
399 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
401 (*i)->parent_to_component_x (ev->x),
402 (*i)->parent_to_component_y (ev->y)
411 set_mouseover (PortMatrixNode ());
418 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
420 list<PortMatrixNode> m;
426 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
428 if (n == _mouseover) {
432 /* Channel highlights are set up only on mouseovers, so
433 it's reasonable to remove all channel highlights here.
434 We can't let individual components clear their own highlights
435 because of the case where, say, the row labels set up some column
436 highlights, and then we ask the column labels to set up their
437 own highlights and they clear them out before they start.
440 _row_labels->clear_channel_highlights ();
441 _column_labels->clear_channel_highlights ();
443 list<PortMatrixNode> old = _mouseover;
446 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
447 (*i)->mouseover_changed (old);
452 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
454 ARDOUR::BundleChannel bc[2];
457 if (!bc[dim].bundle) {
461 if (dim == _matrix->column_index()) {
462 _column_labels->add_channel_highlight (bc[dim]);
464 _row_labels->add_channel_highlight (bc[dim]);
467 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
469 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
470 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
472 if ((*i)->bundle->channel_type(j) != _matrix->type()) {
476 bc[1 - dim] = ARDOUR::BundleChannel ((*i)->bundle, j);
477 if (_matrix->get_state (bc) == PortMatrixNode::ASSOCIATED) {
478 if (dim == _matrix->column_index()) {
479 _row_labels->add_channel_highlight (bc[1 - dim]);
481 _column_labels->add_channel_highlight (bc[1 - dim]);
489 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
491 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
496 PortMatrixBody::component_size_changed ()
498 if (_ignore_component_size_changed) {
502 compute_rectangles ();
503 _matrix->setup_scrollbars ();
506 pair<uint32_t, uint32_t>
507 PortMatrixBody::max_size () const
509 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
510 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
511 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
513 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
516 /** @return x position at which the column labels meet the border of the matrix */
518 PortMatrixBody::column_labels_border_x () const
520 return _column_labels_border_x;
524 PortMatrixBody::column_labels_height () const
526 return _column_labels_height;