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"
33 PortMatrixBody::PortMatrixBody (PortMatrix* p)
39 _column_labels_border_x (0),
40 _column_labels_height (0),
41 _ignore_component_size_changed (false)
43 _column_labels = new PortMatrixColumnLabels (p, this);
44 _row_labels = new PortMatrixRowLabels (p, this);
45 _grid = new PortMatrixGrid (p, this);
47 _components.push_back (_column_labels);
48 _components.push_back (_row_labels);
49 _components.push_back (_grid);
51 add_events (Gdk::LEAVE_NOTIFY_MASK | Gdk::POINTER_MOTION_MASK);
55 PortMatrixBody::~PortMatrixBody ()
57 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
63 PortMatrixBody::on_expose_event (GdkEventExpose* event)
66 _matrix->visible_columns() == 0 || _matrix->visible_rows() == 0 ||
67 _matrix->visible_columns()->bundles().empty() || _matrix->visible_rows()->bundles().empty()
70 /* nothing to connect */
72 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
74 cairo_set_source_rgb (cr, 0, 0, 0);
75 cairo_rectangle (cr, 0, 0, _alloc_width, _alloc_height);
79 t << _("There are no ") << (_matrix->type() == ARDOUR::DataType::AUDIO ? _("audio") : _("MIDI")) << _(" ports to connect.");
81 cairo_text_extents_t ext;
82 cairo_text_extents (cr, t.str().c_str(), &ext);
84 cairo_set_source_rgb (cr, 1, 1, 1);
85 cairo_move_to (cr, (_alloc_width - ext.width) / 2, (_alloc_height + ext.height) / 2);
86 cairo_show_text (cr, t.str().c_str ());
93 Gdk::Rectangle const exposure (
94 event->area.x, event->area.y, event->area.width, event->area.height
99 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
101 Gdk::Rectangle r = exposure;
103 /* the get_pixmap call may cause things to be rerendered and sizes to change,
104 so fetch the pixmap before calculating where to put it */
105 GdkPixmap* p = (*i)->get_pixmap (get_window()->gobj());
106 r.intersect ((*i)->parent_rectangle(), intersects);
111 get_window()->gobj(),
112 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
114 (*i)->parent_to_component_x (r.get_x()),
115 (*i)->parent_to_component_y (r.get_y()),
125 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
127 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
129 set_cairo_clip (cr, (*i)->parent_rectangle ());
130 (*i)->draw_extra (cr);
140 PortMatrixBody::on_size_request (Gtk::Requisition *req)
142 pair<int, int> const col = _column_labels->dimensions ();
143 pair<int, int> const row = _row_labels->dimensions ();
144 pair<int, int> const grid = _grid->dimensions ();
146 if (grid.first == 0 && grid.second == 0) {
147 /* nothing to display */
153 /* don't ask for the maximum size of our contents, otherwise GTK won't
154 let the containing window shrink below this size */
156 /* XXX these shouldn't be hard-coded */
157 int const min_width = 512;
158 int const min_height = 512;
160 req->width = min (min_width, max (col.first, grid.first + row.first));
161 req->height = min (min_height / _matrix->min_height_divisor(), col.second + grid.second);
165 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
167 Gtk::EventBox::on_size_allocate (alloc);
169 _alloc_width = alloc.get_width ();
170 _alloc_height = alloc.get_height ();
172 compute_rectangles ();
173 _matrix->setup_scrollbars ();
177 PortMatrixBody::compute_rectangles ()
179 /* full sizes of components */
180 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
181 uint32_t const col_overhang = _column_labels->overhang ();
182 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
183 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
185 Gdk::Rectangle col_rect;
186 Gdk::Rectangle row_rect;
187 Gdk::Rectangle grid_rect;
189 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
192 _column_labels_border_x = col_overhang;
196 col_rect.set_width (min (col.first, _alloc_width));
198 uint32_t const y = min (_alloc_height, col.second);
199 col_rect.set_height (y);
201 row_rect.set_height (_alloc_height - y);
203 grid_rect.set_height (_alloc_height - y);
206 if (_alloc_width > (grid.first + row.first)) {
208 } else if (_alloc_width > row.first) {
209 x = _alloc_width - row.first;
212 grid_rect.set_width (x);
214 row_rect.set_width (_alloc_width - x);
217 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
219 col_rect.set_height (min (_alloc_height, col.second));
223 row_rect.set_width (min (_alloc_width, row.first));
224 row_rect.set_height (std::min (_alloc_height - col_rect.get_height(), row.second));
226 grid_rect.set_x (row_rect.get_width());
228 grid_rect.set_width (std::min (_alloc_width - row_rect.get_width(), grid.first));
229 grid_rect.set_height (row_rect.get_height ());
231 col_rect.set_width (grid_rect.get_width () + col_overhang);
232 col_rect.set_x (row_rect.get_width() + grid_rect.get_width() - col_rect.get_width());
233 _column_labels_border_x = col_rect.get_x () >= 0 ? col_rect.get_x () : 0;
234 col_rect.set_y (row_rect.get_height());
237 _column_labels_height = col_rect.get_height ();
239 _row_labels->set_parent_rectangle (row_rect);
240 _column_labels->set_parent_rectangle (col_rect);
241 _grid->set_parent_rectangle (grid_rect);
243 DimensionsChanged (); /* EMIT SIGNAL */
247 PortMatrixBody::setup ()
249 /* Discard any old connections to bundles */
251 _bundle_connections.drop_connections ();
253 /* Connect to bundles so that we find out when their names change */
255 if (_matrix->visible_rows()) {
256 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
257 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
259 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_row_labels, this), gui_context());
264 if (_matrix->visible_columns()) {
265 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
266 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
267 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_column_labels, this), gui_context());
271 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
275 set_mouseover (PortMatrixNode ());
277 _ignore_component_size_changed = true;
278 compute_rectangles ();
279 _ignore_component_size_changed = false;
283 PortMatrixBody::full_scroll_width ()
285 return _grid->dimensions().first;
290 PortMatrixBody::alloc_scroll_width ()
292 return _grid->parent_rectangle().get_width();
296 PortMatrixBody::full_scroll_height ()
298 return _grid->dimensions().second;
302 PortMatrixBody::alloc_scroll_height ()
304 return _grid->parent_rectangle().get_height();
307 /** Set x offset (for scrolling) */
309 PortMatrixBody::set_xoffset (uint32_t xo)
315 /** Set y offset (for scrolling) */
317 PortMatrixBody::set_yoffset (uint32_t yo)
324 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
326 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
327 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
329 (*i)->parent_to_component_x (ev->x),
330 (*i)->parent_to_component_y (ev->y),
331 ev->button, ev->time, ev->state
340 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
342 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
343 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
344 (*i)->button_release (
345 (*i)->parent_to_component_x (ev->x),
346 (*i)->parent_to_component_y (ev->y),
347 ev->button, ev->time, ev->state
350 (*i)->button_release (
352 ev->button, ev->time, ev->state
361 PortMatrixBody::rebuild_and_draw_grid ()
363 _grid->require_rebuild ();
368 PortMatrixBody::rebuild_and_draw_column_labels ()
370 _column_labels->require_rebuild ();
375 PortMatrixBody::rebuild_and_draw_row_labels ()
377 _row_labels->require_rebuild ();
382 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
384 if (ev->type == GDK_LEAVE_NOTIFY) {
385 set_mouseover (PortMatrixNode ());
392 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
396 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
397 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
399 (*i)->parent_to_component_x (ev->x),
400 (*i)->parent_to_component_y (ev->y)
409 set_mouseover (PortMatrixNode ());
416 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
418 list<PortMatrixNode> m;
424 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
426 if (n == _mouseover) {
430 /* Channel highlights are set up only on mouseovers, so
431 it's reasonable to remove all channel highlights here.
432 We can't let individual components clear their own highlights
433 because of the case where, say, the row labels set up some column
434 highlights, and then we ask the column labels to set up their
435 own highlights and they clear them out before they start.
438 _row_labels->clear_channel_highlights ();
439 _column_labels->clear_channel_highlights ();
441 list<PortMatrixNode> old = _mouseover;
444 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
445 (*i)->mouseover_changed (old);
450 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
452 ARDOUR::BundleChannel bc[2];
455 if (!bc[dim].bundle) {
459 if (dim == _matrix->column_index()) {
460 _column_labels->add_channel_highlight (bc[dim]);
462 _row_labels->add_channel_highlight (bc[dim]);
465 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
467 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
468 for (uint32_t j = 0; j < (*i)->bundle->nchannels(); ++j) {
469 bc[1 - dim] = ARDOUR::BundleChannel ((*i)->bundle, j);
470 if (_matrix->get_state (bc) == PortMatrixNode::ASSOCIATED) {
471 if (dim == _matrix->column_index()) {
472 _row_labels->add_channel_highlight (bc[1 - dim]);
474 _column_labels->add_channel_highlight (bc[1 - dim]);
482 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
484 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
489 PortMatrixBody::component_size_changed ()
491 if (_ignore_component_size_changed) {
495 compute_rectangles ();
496 _matrix->setup_scrollbars ();
499 pair<uint32_t, uint32_t>
500 PortMatrixBody::max_size () const
502 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
503 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
504 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
506 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
509 /** @return x position at which the column labels meet the border of the matrix */
511 PortMatrixBody::column_labels_border_x () const
513 return _column_labels_border_x;
517 PortMatrixBody::column_labels_height () const
519 return _column_labels_height;