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 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
257 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
259 _bundle_connections.push_back (
260 i->bundle->Changed.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrixBody::rebuild_and_draw_row_labels)))
265 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
266 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
267 _bundle_connections.push_back (
268 i->bundle->Changed.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrixBody::rebuild_and_draw_column_labels)))
272 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
276 set_mouseover (PortMatrixNode ());
278 _ignore_component_size_changed = true;
279 compute_rectangles ();
280 _ignore_component_size_changed = false;
284 PortMatrixBody::full_scroll_width ()
286 return _grid->dimensions().first;
291 PortMatrixBody::alloc_scroll_width ()
293 return _grid->parent_rectangle().get_width();
297 PortMatrixBody::full_scroll_height ()
299 return _grid->dimensions().second;
303 PortMatrixBody::alloc_scroll_height ()
305 return _grid->parent_rectangle().get_height();
308 /** Set x offset (for scrolling) */
310 PortMatrixBody::set_xoffset (uint32_t xo)
316 /** Set y offset (for scrolling) */
318 PortMatrixBody::set_yoffset (uint32_t yo)
325 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
327 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
328 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
330 (*i)->parent_to_component_x (ev->x),
331 (*i)->parent_to_component_y (ev->y),
332 ev->button, ev->time, ev->state
341 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
343 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
344 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
345 (*i)->button_release (
346 (*i)->parent_to_component_x (ev->x),
347 (*i)->parent_to_component_y (ev->y),
348 ev->button, ev->time, ev->state
351 (*i)->button_release (
353 ev->button, ev->time, ev->state
362 PortMatrixBody::rebuild_and_draw_grid ()
364 _grid->require_rebuild ();
369 PortMatrixBody::rebuild_and_draw_column_labels ()
371 _column_labels->require_rebuild ();
376 PortMatrixBody::rebuild_and_draw_row_labels ()
378 _row_labels->require_rebuild ();
383 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
385 if (ev->type == GDK_LEAVE_NOTIFY) {
386 set_mouseover (PortMatrixNode ());
393 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
397 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
398 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
400 (*i)->parent_to_component_x (ev->x),
401 (*i)->parent_to_component_y (ev->y)
410 set_mouseover (PortMatrixNode ());
417 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
419 list<PortMatrixNode> m;
425 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
427 if (n == _mouseover) {
431 /* Channel highlights are set up only on mouseovers, so
432 it's reasonable to remove all channel highlights here.
433 We can't let individual components clear their own highlights
434 because of the case where, say, the row labels set up some column
435 highlights, and then we ask the column labels to set up their
436 own highlights and they clear them out before they start.
439 _row_labels->clear_channel_highlights ();
440 _column_labels->clear_channel_highlights ();
442 list<PortMatrixNode> old = _mouseover;
445 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
446 (*i)->mouseover_changed (old);
451 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
453 ARDOUR::BundleChannel bc[2];
456 if (!bc[dim].bundle) {
460 if (dim == _matrix->column_index()) {
461 _column_labels->add_channel_highlight (bc[dim]);
463 _row_labels->add_channel_highlight (bc[dim]);
466 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
468 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
469 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
470 bc[1 - dim] = ARDOUR::BundleChannel (i->bundle, j);
471 if (_matrix->get_state (bc) == PortMatrixNode::ASSOCIATED) {
472 if (dim == _matrix->column_index()) {
473 _row_labels->add_channel_highlight (bc[1 - dim]);
475 _column_labels->add_channel_highlight (bc[1 - dim]);
483 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
485 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
490 PortMatrixBody::component_size_changed ()
492 if (_ignore_component_size_changed) {
496 compute_rectangles ();
497 _matrix->setup_scrollbars ();
500 pair<uint32_t, uint32_t>
501 PortMatrixBody::max_size () const
503 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
504 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
505 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
507 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
510 /** @return x position at which the column labels meet the border of the matrix */
512 PortMatrixBody::column_labels_border_x () const
514 return _column_labels_border_x;
518 PortMatrixBody::column_labels_height () const
520 return _column_labels_height;