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"
23 #include "port_matrix_body.h"
24 #include "port_matrix.h"
25 #include "port_matrix_column_labels.h"
26 #include "port_matrix_row_labels.h"
27 #include "port_matrix_grid.h"
31 PortMatrixBody::PortMatrixBody (PortMatrix* p)
37 _column_labels_border_x (0),
38 _column_labels_height (0),
39 _ignore_component_size_changed (false)
41 _column_labels = new PortMatrixColumnLabels (p, this);
42 _row_labels = new PortMatrixRowLabels (p, this);
43 _grid = new PortMatrixGrid (p, this);
45 _components.push_back (_column_labels);
46 _components.push_back (_row_labels);
47 _components.push_back (_grid);
49 add_events (Gdk::LEAVE_NOTIFY_MASK | Gdk::POINTER_MOTION_MASK);
53 PortMatrixBody::~PortMatrixBody ()
55 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
61 PortMatrixBody::on_expose_event (GdkEventExpose* event)
64 _matrix->visible_columns() == 0 || _matrix->visible_rows() == 0 ||
65 _matrix->visible_columns()->bundles().empty() || _matrix->visible_rows()->bundles().empty()
68 /* nothing to connect */
70 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
72 cairo_set_source_rgb (cr, 0, 0, 0);
73 cairo_rectangle (cr, 0, 0, _alloc_width, _alloc_height);
77 t << _("There are no ") << (_matrix->type() == ARDOUR::DataType::AUDIO ? _("audio") : _("MIDI")) << _(" ports to connect.");
79 cairo_text_extents_t ext;
80 cairo_text_extents (cr, t.str().c_str(), &ext);
82 cairo_set_source_rgb (cr, 1, 1, 1);
83 cairo_move_to (cr, (_alloc_width - ext.width) / 2, (_alloc_height + ext.height) / 2);
84 cairo_show_text (cr, t.str().c_str ());
91 Gdk::Rectangle const exposure (
92 event->area.x, event->area.y, event->area.width, event->area.height
97 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
99 Gdk::Rectangle r = exposure;
101 /* the get_pixmap call may cause things to be rerendered and sizes to change,
102 so fetch the pixmap before calculating where to put it */
103 GdkPixmap* p = (*i)->get_pixmap (get_window()->gobj());
104 r.intersect ((*i)->parent_rectangle(), intersects);
109 get_window()->gobj(),
110 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
112 (*i)->parent_to_component_x (r.get_x()),
113 (*i)->parent_to_component_y (r.get_y()),
123 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
125 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
127 set_cairo_clip (cr, (*i)->parent_rectangle ());
128 (*i)->draw_extra (cr);
138 PortMatrixBody::on_size_request (Gtk::Requisition *req)
140 pair<int, int> const col = _column_labels->dimensions ();
141 pair<int, int> const row = _row_labels->dimensions ();
142 pair<int, int> const grid = _grid->dimensions ();
144 if (grid.first == 0 && grid.second == 0) {
145 /* nothing to display */
151 /* don't ask for the maximum size of our contents, otherwise GTK won't
152 let the containing window shrink below this size */
154 /* XXX these shouldn't be hard-coded */
155 int const min_width = 512;
156 int const min_height = 512;
158 req->width = min (min_width, max (col.first, grid.first + row.first));
159 req->height = min (min_height / _matrix->min_height_divisor(), col.second + grid.second);
163 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
165 Gtk::EventBox::on_size_allocate (alloc);
167 _alloc_width = alloc.get_width ();
168 _alloc_height = alloc.get_height ();
170 compute_rectangles ();
171 _matrix->setup_scrollbars ();
175 PortMatrixBody::compute_rectangles ()
177 /* full sizes of components */
178 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
179 uint32_t const col_overhang = _column_labels->overhang ();
180 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
181 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
183 Gdk::Rectangle col_rect;
184 Gdk::Rectangle row_rect;
185 Gdk::Rectangle grid_rect;
187 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
190 _column_labels_border_x = col_overhang;
194 col_rect.set_width (min (col.first, _alloc_width));
196 uint32_t const y = min (_alloc_height, col.second);
197 col_rect.set_height (y);
199 row_rect.set_height (_alloc_height - y);
201 grid_rect.set_height (_alloc_height - y);
204 if (_alloc_width > (grid.first + row.first)) {
206 } else if (_alloc_width > row.first) {
207 x = _alloc_width - row.first;
210 grid_rect.set_width (x);
212 row_rect.set_width (_alloc_width - x);
215 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
217 col_rect.set_height (min (_alloc_height, col.second));
221 row_rect.set_width (min (_alloc_width, row.first));
222 row_rect.set_height (std::min (_alloc_height - col_rect.get_height(), row.second));
224 grid_rect.set_x (row_rect.get_width());
226 grid_rect.set_width (std::min (_alloc_width - row_rect.get_width(), grid.first));
227 grid_rect.set_height (row_rect.get_height ());
229 col_rect.set_width (grid_rect.get_width () + col_overhang);
230 col_rect.set_x (row_rect.get_width() + grid_rect.get_width() - col_rect.get_width());
231 _column_labels_border_x = col_rect.get_x () >= 0 ? col_rect.get_x () : 0;
232 col_rect.set_y (row_rect.get_height());
235 _column_labels_height = col_rect.get_height ();
237 _row_labels->set_parent_rectangle (row_rect);
238 _column_labels->set_parent_rectangle (col_rect);
239 _grid->set_parent_rectangle (grid_rect);
241 DimensionsChanged (); /* EMIT SIGNAL */
245 PortMatrixBody::setup ()
247 /* Discard any old connections to bundles */
249 _bundle_connections.drop_connections ();
251 /* Connect to bundles so that we find out when their names change */
253 if (_matrix->visible_rows()) {
254 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
255 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
257 (*i)->bundle->Changed.connect (_bundle_connections, boost::bind (&PortMatrixBody::rebuild_and_draw_row_labels, this));
262 if (_matrix->visible_columns()) {
263 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
264 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
265 (*i)->bundle->Changed.connect (_bundle_connections, boost::bind (&PortMatrixBody::rebuild_and_draw_column_labels, this));
269 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
273 set_mouseover (PortMatrixNode ());
275 _ignore_component_size_changed = true;
276 compute_rectangles ();
277 _ignore_component_size_changed = false;
281 PortMatrixBody::full_scroll_width ()
283 return _grid->dimensions().first;
288 PortMatrixBody::alloc_scroll_width ()
290 return _grid->parent_rectangle().get_width();
294 PortMatrixBody::full_scroll_height ()
296 return _grid->dimensions().second;
300 PortMatrixBody::alloc_scroll_height ()
302 return _grid->parent_rectangle().get_height();
305 /** Set x offset (for scrolling) */
307 PortMatrixBody::set_xoffset (uint32_t xo)
313 /** Set y offset (for scrolling) */
315 PortMatrixBody::set_yoffset (uint32_t yo)
322 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
324 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
325 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
327 (*i)->parent_to_component_x (ev->x),
328 (*i)->parent_to_component_y (ev->y),
329 ev->button, ev->time, ev->state
338 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
340 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
341 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
342 (*i)->button_release (
343 (*i)->parent_to_component_x (ev->x),
344 (*i)->parent_to_component_y (ev->y),
345 ev->button, ev->time, ev->state
348 (*i)->button_release (
350 ev->button, ev->time, ev->state
359 PortMatrixBody::rebuild_and_draw_grid ()
361 _grid->require_rebuild ();
366 PortMatrixBody::rebuild_and_draw_column_labels ()
368 _column_labels->require_rebuild ();
373 PortMatrixBody::rebuild_and_draw_row_labels ()
375 _row_labels->require_rebuild ();
380 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
382 if (ev->type == GDK_LEAVE_NOTIFY) {
383 set_mouseover (PortMatrixNode ());
390 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
394 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
395 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
397 (*i)->parent_to_component_x (ev->x),
398 (*i)->parent_to_component_y (ev->y)
407 set_mouseover (PortMatrixNode ());
414 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
416 list<PortMatrixNode> m;
422 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
424 if (n == _mouseover) {
428 /* Channel highlights are set up only on mouseovers, so
429 it's reasonable to remove all channel highlights here.
430 We can't let individual components clear their own highlights
431 because of the case where, say, the row labels set up some column
432 highlights, and then we ask the column labels to set up their
433 own highlights and they clear them out before they start.
436 _row_labels->clear_channel_highlights ();
437 _column_labels->clear_channel_highlights ();
439 list<PortMatrixNode> old = _mouseover;
442 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
443 (*i)->mouseover_changed (old);
448 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
450 ARDOUR::BundleChannel bc[2];
453 if (!bc[dim].bundle) {
457 if (dim == _matrix->column_index()) {
458 _column_labels->add_channel_highlight (bc[dim]);
460 _row_labels->add_channel_highlight (bc[dim]);
463 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
465 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
466 for (uint32_t j = 0; j < (*i)->bundle->nchannels(); ++j) {
467 bc[1 - dim] = ARDOUR::BundleChannel ((*i)->bundle, j);
468 if (_matrix->get_state (bc) == PortMatrixNode::ASSOCIATED) {
469 if (dim == _matrix->column_index()) {
470 _row_labels->add_channel_highlight (bc[1 - dim]);
472 _column_labels->add_channel_highlight (bc[1 - dim]);
480 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
482 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
487 PortMatrixBody::component_size_changed ()
489 if (_ignore_component_size_changed) {
493 compute_rectangles ();
494 _matrix->setup_scrollbars ();
497 pair<uint32_t, uint32_t>
498 PortMatrixBody::max_size () const
500 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
501 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
502 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
504 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
507 /** @return x position at which the column labels meet the border of the matrix */
509 PortMatrixBody::column_labels_border_x () const
511 return _column_labels_border_x;
515 PortMatrixBody::column_labels_height () const
517 return _column_labels_height;