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 for (list<sigc::connection>::iterator i = _bundle_connections.begin(); i != _bundle_connections.end(); ++i) {
252 _bundle_connections.clear ();
254 /* Connect to bundles so that we find out when their names change */
256 if (_matrix->visible_rows()) {
257 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
258 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
260 _bundle_connections.push_back (
261 i->bundle->Changed.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrixBody::rebuild_and_draw_row_labels)))
267 if (_matrix->visible_columns()) {
268 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
269 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
270 _bundle_connections.push_back (
271 i->bundle->Changed.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrixBody::rebuild_and_draw_column_labels)))
276 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
280 set_mouseover (PortMatrixNode ());
282 _ignore_component_size_changed = true;
283 compute_rectangles ();
284 _ignore_component_size_changed = false;
288 PortMatrixBody::full_scroll_width ()
290 return _grid->dimensions().first;
295 PortMatrixBody::alloc_scroll_width ()
297 return _grid->parent_rectangle().get_width();
301 PortMatrixBody::full_scroll_height ()
303 return _grid->dimensions().second;
307 PortMatrixBody::alloc_scroll_height ()
309 return _grid->parent_rectangle().get_height();
312 /** Set x offset (for scrolling) */
314 PortMatrixBody::set_xoffset (uint32_t xo)
320 /** Set y offset (for scrolling) */
322 PortMatrixBody::set_yoffset (uint32_t yo)
329 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
331 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
332 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
334 (*i)->parent_to_component_x (ev->x),
335 (*i)->parent_to_component_y (ev->y),
336 ev->button, ev->time, ev->state
345 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
347 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
348 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
349 (*i)->button_release (
350 (*i)->parent_to_component_x (ev->x),
351 (*i)->parent_to_component_y (ev->y),
352 ev->button, ev->time, ev->state
355 (*i)->button_release (
357 ev->button, ev->time, ev->state
366 PortMatrixBody::rebuild_and_draw_grid ()
368 _grid->require_rebuild ();
373 PortMatrixBody::rebuild_and_draw_column_labels ()
375 _column_labels->require_rebuild ();
380 PortMatrixBody::rebuild_and_draw_row_labels ()
382 _row_labels->require_rebuild ();
387 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
389 if (ev->type == GDK_LEAVE_NOTIFY) {
390 set_mouseover (PortMatrixNode ());
397 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
401 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
402 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
404 (*i)->parent_to_component_x (ev->x),
405 (*i)->parent_to_component_y (ev->y)
414 set_mouseover (PortMatrixNode ());
421 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
423 list<PortMatrixNode> m;
429 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
431 if (n == _mouseover) {
435 /* Channel highlights are set up only on mouseovers, so
436 it's reasonable to remove all channel highlights here.
437 We can't let individual components clear their own highlights
438 because of the case where, say, the row labels set up some column
439 highlights, and then we ask the column labels to set up their
440 own highlights and they clear them out before they start.
443 _row_labels->clear_channel_highlights ();
444 _column_labels->clear_channel_highlights ();
446 list<PortMatrixNode> old = _mouseover;
449 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
450 (*i)->mouseover_changed (old);
455 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
457 ARDOUR::BundleChannel bc[2];
460 if (!bc[dim].bundle) {
464 if (dim == _matrix->column_index()) {
465 _column_labels->add_channel_highlight (bc[dim]);
467 _row_labels->add_channel_highlight (bc[dim]);
470 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
472 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
473 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
474 bc[1 - dim] = ARDOUR::BundleChannel (i->bundle, j);
475 if (_matrix->get_state (bc) == PortMatrixNode::ASSOCIATED) {
476 if (dim == _matrix->column_index()) {
477 _row_labels->add_channel_highlight (bc[1 - dim]);
479 _column_labels->add_channel_highlight (bc[1 - dim]);
487 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
489 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
494 PortMatrixBody::component_size_changed ()
496 if (_ignore_component_size_changed) {
500 compute_rectangles ();
501 _matrix->setup_scrollbars ();
504 pair<uint32_t, uint32_t>
505 PortMatrixBody::max_size () const
507 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
508 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
509 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
511 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
514 /** @return x position at which the column labels meet the border of the matrix */
516 PortMatrixBody::column_labels_border_x () const
518 return _column_labels_border_x;
522 PortMatrixBody::column_labels_height () const
524 return _column_labels_height;