fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / port_matrix_body.cc
blob6da2ab87b5d97617e81c68d3133186c67ae72baf
1 /*
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.
20 #include <iostream>
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"
31 #include "i18n.h"
33 using namespace std;
35 PortMatrixBody::PortMatrixBody (PortMatrix* p)
36 : _matrix (p),
37 _alloc_width (0),
38 _alloc_height (0),
39 _xoffset (0),
40 _yoffset (0),
41 _column_labels_border_x (0),
42 _column_labels_height (0),
43 _ignore_component_size_changed (false)
45 _column_labels = new PortMatrixColumnLabels (p, this);
46 _row_labels = new PortMatrixRowLabels (p, this);
47 _grid = new PortMatrixGrid (p, this);
49 _components.push_back (_column_labels);
50 _components.push_back (_row_labels);
51 _components.push_back (_grid);
53 add_events (Gdk::LEAVE_NOTIFY_MASK | Gdk::POINTER_MOTION_MASK);
57 PortMatrixBody::~PortMatrixBody ()
59 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
60 delete *i;
64 bool
65 PortMatrixBody::on_expose_event (GdkEventExpose* event)
67 if (
68 _matrix->visible_columns() == 0 || _matrix->visible_rows() == 0 ||
69 _matrix->visible_columns()->bundles().empty() || _matrix->visible_rows()->bundles().empty()
70 ) {
72 /* nothing to connect */
74 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
76 cairo_set_source_rgb (cr, 0, 0, 0);
77 cairo_rectangle (cr, 0, 0, _alloc_width, _alloc_height);
78 cairo_fill (cr);
80 stringstream t;
81 t << _("There are no ") << (_matrix->type() == ARDOUR::DataType::AUDIO ? _("audio") : _("MIDI")) << _(" ports to connect.");
83 cairo_text_extents_t ext;
84 cairo_text_extents (cr, t.str().c_str(), &ext);
86 cairo_set_source_rgb (cr, 1, 1, 1);
87 cairo_move_to (cr, (_alloc_width - ext.width) / 2, (_alloc_height + ext.height) / 2);
88 cairo_show_text (cr, t.str().c_str ());
90 cairo_destroy (cr);
92 return true;
95 Gdk::Rectangle const exposure (
96 event->area.x, event->area.y, event->area.width, event->area.height
99 bool intersects;
101 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
103 Gdk::Rectangle r = exposure;
105 /* the get_pixmap call may cause things to be rerendered and sizes to change,
106 so fetch the pixmap before calculating where to put it */
107 GdkPixmap* p = (*i)->get_pixmap (get_window()->gobj());
108 r.intersect ((*i)->parent_rectangle(), intersects);
110 if (intersects) {
112 gdk_draw_drawable (
113 get_window()->gobj(),
114 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
116 (*i)->parent_to_component_x (r.get_x()),
117 (*i)->parent_to_component_y (r.get_y()),
118 r.get_x(),
119 r.get_y(),
120 r.get_width(),
121 r.get_height()
127 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
129 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
130 cairo_save (cr);
131 set_cairo_clip (cr, (*i)->parent_rectangle ());
132 (*i)->draw_extra (cr);
133 cairo_restore (cr);
136 cairo_destroy (cr);
138 return true;
141 void
142 PortMatrixBody::on_size_request (Gtk::Requisition *req)
144 pair<int, int> const col = _column_labels->dimensions ();
145 pair<int, int> const row = _row_labels->dimensions ();
146 pair<int, int> const grid = _grid->dimensions ();
148 if (grid.first == 0 && grid.second == 0) {
149 /* nothing to display */
150 req->width = 256;
151 req->height = 64;
152 return;
155 /* don't ask for the maximum size of our contents, otherwise GTK won't
156 let the containing window shrink below this size */
158 /* XXX these shouldn't be hard-coded */
159 int const min_width = 512;
160 int const min_height = 512;
162 req->width = min (min_width, max (col.first, grid.first + row.first));
163 req->height = min (min_height / _matrix->min_height_divisor(), col.second + grid.second);
166 void
167 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
169 Gtk::EventBox::on_size_allocate (alloc);
171 _alloc_width = alloc.get_width ();
172 _alloc_height = alloc.get_height ();
174 compute_rectangles ();
175 _matrix->setup_scrollbars ();
178 void
179 PortMatrixBody::compute_rectangles ()
181 /* full sizes of components */
182 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
183 uint32_t const col_overhang = _column_labels->overhang ();
184 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
185 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
187 Gdk::Rectangle col_rect;
188 Gdk::Rectangle row_rect;
189 Gdk::Rectangle grid_rect;
191 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
193 col_rect.set_x (0);
194 _column_labels_border_x = col_overhang;
195 col_rect.set_y (0);
196 grid_rect.set_x (0);
198 col_rect.set_width (min (col.first, _alloc_width));
200 uint32_t const y = min (_alloc_height, col.second);
201 col_rect.set_height (y);
202 row_rect.set_y (y);
203 row_rect.set_height (_alloc_height - y);
204 grid_rect.set_y (y);
205 grid_rect.set_height (_alloc_height - y);
207 uint32_t x = 0;
208 if (_alloc_width > (grid.first + row.first)) {
209 x = grid.first;
210 } else if (_alloc_width > row.first) {
211 x = _alloc_width - row.first;
214 grid_rect.set_width (x);
215 row_rect.set_x (x);
216 row_rect.set_width (_alloc_width - x);
219 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
221 col_rect.set_height (min (_alloc_height, col.second));
223 row_rect.set_x (0);
224 row_rect.set_y (0);
225 row_rect.set_width (min (_alloc_width, row.first));
226 row_rect.set_height (std::min (_alloc_height - col_rect.get_height(), row.second));
228 grid_rect.set_x (row_rect.get_width());
229 grid_rect.set_y (0);
230 grid_rect.set_width (std::min (_alloc_width - row_rect.get_width(), grid.first));
231 grid_rect.set_height (row_rect.get_height ());
233 col_rect.set_width (grid_rect.get_width () + col_overhang);
234 col_rect.set_x (row_rect.get_width() + grid_rect.get_width() - col_rect.get_width());
235 _column_labels_border_x = col_rect.get_x () >= 0 ? col_rect.get_x () : 0;
236 col_rect.set_y (row_rect.get_height());
239 _column_labels_height = col_rect.get_height ();
241 _row_labels->set_parent_rectangle (row_rect);
242 _column_labels->set_parent_rectangle (col_rect);
243 _grid->set_parent_rectangle (grid_rect);
245 DimensionsChanged (); /* EMIT SIGNAL */
248 void
249 PortMatrixBody::setup ()
251 /* Discard any old connections to bundles */
253 _bundle_connections.drop_connections ();
255 /* Connect to bundles so that we find out when their names change */
257 if (_matrix->visible_rows()) {
258 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
259 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
261 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_row_labels, this), gui_context());
266 if (_matrix->visible_columns()) {
267 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
268 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
269 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_column_labels, this), gui_context());
273 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
274 (*i)->setup ();
277 set_mouseover (PortMatrixNode ());
279 _ignore_component_size_changed = true;
280 compute_rectangles ();
281 _ignore_component_size_changed = false;
284 uint32_t
285 PortMatrixBody::full_scroll_width ()
287 return _grid->dimensions().first;
291 uint32_t
292 PortMatrixBody::alloc_scroll_width ()
294 return _grid->parent_rectangle().get_width();
297 uint32_t
298 PortMatrixBody::full_scroll_height ()
300 return _grid->dimensions().second;
303 uint32_t
304 PortMatrixBody::alloc_scroll_height ()
306 return _grid->parent_rectangle().get_height();
309 /** Set x offset (for scrolling) */
310 void
311 PortMatrixBody::set_xoffset (uint32_t xo)
313 _xoffset = xo;
314 queue_draw ();
317 /** Set y offset (for scrolling) */
318 void
319 PortMatrixBody::set_yoffset (uint32_t yo)
321 _yoffset = yo;
322 queue_draw ();
325 bool
326 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
328 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
329 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
330 (*i)->button_press (
331 (*i)->parent_to_component_x (ev->x),
332 (*i)->parent_to_component_y (ev->y),
333 ev->button, ev->time, ev->state
338 return true;
341 bool
342 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
344 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
345 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
346 (*i)->button_release (
347 (*i)->parent_to_component_x (ev->x),
348 (*i)->parent_to_component_y (ev->y),
349 ev->button, ev->time, ev->state
351 } else {
352 (*i)->button_release (
353 -1, -1,
354 ev->button, ev->time, ev->state
359 return true;
362 void
363 PortMatrixBody::rebuild_and_draw_grid ()
365 _grid->require_rebuild ();
366 queue_draw ();
369 void
370 PortMatrixBody::rebuild_and_draw_column_labels ()
372 _column_labels->require_rebuild ();
373 queue_draw ();
376 void
377 PortMatrixBody::rebuild_and_draw_row_labels ()
379 _row_labels->require_rebuild ();
380 queue_draw ();
383 bool
384 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
386 if (ev->type == GDK_LEAVE_NOTIFY) {
387 set_mouseover (PortMatrixNode ());
390 return true;
393 bool
394 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
396 bool done = false;
398 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
399 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
400 (*i)->motion (
401 (*i)->parent_to_component_x (ev->x),
402 (*i)->parent_to_component_y (ev->y)
405 done = true;
410 if (!done) {
411 set_mouseover (PortMatrixNode ());
414 return true;
417 void
418 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
420 list<PortMatrixNode> m;
421 m.push_back (n);
422 set_mouseover (m);
425 void
426 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
428 if (n == _mouseover) {
429 return;
432 /* Channel highlights are set up only on mouseovers, so
433 it's reasonable to remove all channel highlights here.
434 We can't let individual components clear their own highlights
435 because of the case where, say, the row labels set up some column
436 highlights, and then we ask the column labels to set up their
437 own highlights and they clear them out before they start.
440 _row_labels->clear_channel_highlights ();
441 _column_labels->clear_channel_highlights ();
443 list<PortMatrixNode> old = _mouseover;
444 _mouseover = n;
446 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
447 (*i)->mouseover_changed (old);
451 void
452 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
454 ARDOUR::BundleChannel bc[2];
455 bc[dim] = h;
457 if (!bc[dim].bundle) {
458 return;
461 if (dim == _matrix->column_index()) {
462 _column_labels->add_channel_highlight (bc[dim]);
463 } else {
464 _row_labels->add_channel_highlight (bc[dim]);
467 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
469 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
470 for (uint32_t j = 0; j < (*i)->bundle->nchannels(); ++j) {
471 bc[1 - dim] = ARDOUR::BundleChannel ((*i)->bundle, j);
472 if (_matrix->get_state (bc) == PortMatrixNode::ASSOCIATED) {
473 if (dim == _matrix->column_index()) {
474 _row_labels->add_channel_highlight (bc[1 - dim]);
475 } else {
476 _column_labels->add_channel_highlight (bc[1 - dim]);
483 void
484 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
486 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
487 cairo_clip (cr);
490 void
491 PortMatrixBody::component_size_changed ()
493 if (_ignore_component_size_changed) {
494 return;
497 compute_rectangles ();
498 _matrix->setup_scrollbars ();
501 pair<uint32_t, uint32_t>
502 PortMatrixBody::max_size () const
504 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
505 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
506 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
508 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
511 /** @return x position at which the column labels meet the border of the matrix */
512 uint32_t
513 PortMatrixBody::column_labels_border_x () const
515 return _column_labels_border_x;
518 uint32_t
519 PortMatrixBody::column_labels_height () const
521 return _column_labels_height;