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 _ignore_component_size_changed (false)
39 _column_labels = new PortMatrixColumnLabels (p, this);
40 _row_labels = new PortMatrixRowLabels (p, this);
41 _grid = new PortMatrixGrid (p, this);
43 _components.push_back (_column_labels);
44 _components.push_back (_row_labels);
45 _components.push_back (_grid);
47 add_events (Gdk::LEAVE_NOTIFY_MASK | Gdk::POINTER_MOTION_MASK);
51 PortMatrixBody::~PortMatrixBody ()
53 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
59 PortMatrixBody::on_expose_event (GdkEventExpose* event)
61 if (_matrix->visible_columns()->bundles().empty() || _matrix->visible_rows()->bundles().empty()) {
63 /* nothing to connect */
65 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
67 cairo_set_source_rgb (cr, 0, 0, 0);
68 cairo_rectangle (cr, 0, 0, _alloc_width, _alloc_height);
72 t << _("There are no ") << (_matrix->type() == ARDOUR::DataType::AUDIO ? _("audio") : _("MIDI")) << _(" ports to connect.");
74 cairo_text_extents_t ext;
75 cairo_text_extents (cr, t.str().c_str(), &ext);
77 cairo_set_source_rgb (cr, 1, 1, 1);
78 cairo_move_to (cr, (_alloc_width - ext.width) / 2, (_alloc_height + ext.height) / 2);
79 cairo_show_text (cr, t.str().c_str ());
86 Gdk::Rectangle const exposure (
87 event->area.x, event->area.y, event->area.width, event->area.height
92 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
94 Gdk::Rectangle r = exposure;
96 /* the get_pixmap call may cause things to be rerendered and sizes to change,
97 so fetch the pixmap before calculating where to put it */
98 GdkPixmap* p = (*i)->get_pixmap (get_window()->gobj());
99 r.intersect ((*i)->parent_rectangle(), intersects);
104 get_window()->gobj(),
105 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
107 (*i)->parent_to_component_x (r.get_x()),
108 (*i)->parent_to_component_y (r.get_y()),
118 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
120 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
122 set_cairo_clip (cr, (*i)->parent_rectangle ());
123 (*i)->draw_extra (cr);
133 PortMatrixBody::on_size_request (Gtk::Requisition *req)
135 pair<int, int> const col = _column_labels->dimensions ();
136 pair<int, int> const row = _row_labels->dimensions ();
137 pair<int, int> const grid = _grid->dimensions ();
139 if (grid.first == 0 && grid.second == 0) {
140 /* nothing to display */
146 /* don't ask for the maximum size of our contents, otherwise GTK won't
147 let the containing window shrink below this size */
149 /* XXX these shouldn't be hard-coded */
150 int const min_width = 512;
151 int const min_height = 512;
153 req->width = min (min_width, max (col.first, grid.first + row.first));
154 req->height = min (min_height / _matrix->min_height_divisor(), col.second + grid.second);
158 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
160 Gtk::EventBox::on_size_allocate (alloc);
162 _alloc_width = alloc.get_width ();
163 _alloc_height = alloc.get_height ();
165 compute_rectangles ();
166 _matrix->setup_scrollbars ();
170 PortMatrixBody::compute_rectangles ()
172 /* full sizes of components */
173 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
174 uint32_t col_overhang = _column_labels->overhang ();
175 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
176 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
178 Gdk::Rectangle col_rect;
179 Gdk::Rectangle row_rect;
180 Gdk::Rectangle grid_rect;
182 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
188 col_rect.set_width (min (col.first, _alloc_width));
190 uint32_t const y = min (_alloc_height, col.second);
191 col_rect.set_height (y);
193 row_rect.set_height (_alloc_height - y);
195 grid_rect.set_height (_alloc_height - y);
198 if (_alloc_width > (grid.first + row.first)) {
200 } else if (_alloc_width > row.first) {
201 x = _alloc_width - row.first;
204 grid_rect.set_width (x);
206 row_rect.set_width (_alloc_width - x);
209 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
211 col_rect.set_height (min (_alloc_height, col.second));
215 row_rect.set_width (min (_alloc_width, row.first));
216 row_rect.set_height (std::min (_alloc_height - col_rect.get_height(), row.second));
218 grid_rect.set_x (row_rect.get_width());
220 grid_rect.set_width (std::min (_alloc_width - row_rect.get_width(), grid.first));
221 grid_rect.set_height (row_rect.get_height ());
223 col_rect.set_width (grid_rect.get_width () + col_overhang);
224 col_rect.set_x (row_rect.get_width() + grid_rect.get_width() - col_rect.get_width());
225 col_rect.set_y (row_rect.get_height());
229 _row_labels->set_parent_rectangle (row_rect);
230 _column_labels->set_parent_rectangle (col_rect);
231 _grid->set_parent_rectangle (grid_rect);
235 PortMatrixBody::setup ()
237 /* Discard any old connections to bundles */
239 for (list<sigc::connection>::iterator i = _bundle_connections.begin(); i != _bundle_connections.end(); ++i) {
242 _bundle_connections.clear ();
244 /* Connect to bundles so that we find out when their names change */
246 PortGroup::BundleList r = _matrix->rows()->bundles ();
247 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
249 _bundle_connections.push_back (
250 i->bundle->Changed.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrixBody::rebuild_and_draw_row_labels)))
255 PortGroup::BundleList c = _matrix->columns()->bundles ();
256 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
257 _bundle_connections.push_back (
258 i->bundle->Changed.connect (sigc::hide (sigc::mem_fun (*this, &PortMatrixBody::rebuild_and_draw_column_labels)))
262 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
266 set_mouseover (PortMatrixNode ());
268 _ignore_component_size_changed = true;
269 compute_rectangles ();
270 _ignore_component_size_changed = false;
274 PortMatrixBody::full_scroll_width ()
276 return _grid->dimensions().first;
281 PortMatrixBody::alloc_scroll_width ()
283 return _grid->parent_rectangle().get_width();
287 PortMatrixBody::full_scroll_height ()
289 return _grid->dimensions().second;
293 PortMatrixBody::alloc_scroll_height ()
295 return _grid->parent_rectangle().get_height();
299 PortMatrixBody::set_xoffset (uint32_t xo)
306 PortMatrixBody::set_yoffset (uint32_t yo)
313 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
315 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
316 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
318 (*i)->parent_to_component_x (ev->x),
319 (*i)->parent_to_component_y (ev->y),
320 ev->button, ev->time, ev->state
329 PortMatrixBody::on_button_release_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)) {
333 (*i)->button_release (
334 (*i)->parent_to_component_x (ev->x),
335 (*i)->parent_to_component_y (ev->y),
336 ev->button, ev->time, ev->state
339 (*i)->button_release (
341 ev->button, ev->time, ev->state
350 PortMatrixBody::rebuild_and_draw_grid ()
352 _grid->require_rebuild ();
357 PortMatrixBody::rebuild_and_draw_column_labels ()
359 _column_labels->require_rebuild ();
364 PortMatrixBody::rebuild_and_draw_row_labels ()
366 _row_labels->require_rebuild ();
371 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
373 if (ev->type == GDK_LEAVE_NOTIFY) {
374 set_mouseover (PortMatrixNode ());
381 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
385 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
386 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
388 (*i)->parent_to_component_x (ev->x),
389 (*i)->parent_to_component_y (ev->y)
398 set_mouseover (PortMatrixNode ());
405 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
407 list<PortMatrixNode> m;
413 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
415 if (n == _mouseover) {
419 /* Channel highlights are set up only on mouseovers, so
420 it's reasonable to remove all channel highlights here.
421 We can't let individual components clear their own highlights
422 because of the case where, say, the row labels set up some column
423 highlights, and then we ask the column labels to set up their
424 own highlights and they clear them out before they start.
427 _row_labels->clear_channel_highlights ();
428 _column_labels->clear_channel_highlights ();
430 list<PortMatrixNode> old = _mouseover;
433 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
434 (*i)->mouseover_changed (old);
439 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
441 ARDOUR::BundleChannel bc[2];
444 if (!bc[dim].bundle) {
448 if (dim == _matrix->column_index()) {
449 _column_labels->add_channel_highlight (bc[dim]);
451 _row_labels->add_channel_highlight (bc[dim]);
454 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
456 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
457 for (uint32_t j = 0; j < i->bundle->nchannels(); ++j) {
458 bc[1 - dim] = ARDOUR::BundleChannel (i->bundle, j);
459 if (_matrix->get_state (bc) == PortMatrixNode::ASSOCIATED) {
460 if (dim == _matrix->column_index()) {
461 _row_labels->add_channel_highlight (bc[1 - dim]);
463 _column_labels->add_channel_highlight (bc[1 - dim]);
471 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
473 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
478 PortMatrixBody::component_size_changed ()
480 if (_ignore_component_size_changed) {
484 compute_rectangles ();
485 _matrix->setup_scrollbars ();
488 pair<uint32_t, uint32_t>
489 PortMatrixBody::max_size () const
491 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
492 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
493 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
495 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);