fixup auto-connection of new MIDI tracks and MIDI tracks with newly-added audio ports
[ardour2.git] / gtk2_ardour / port_matrix_body.cc
blob3d564319f841fe8b497966a7d3ac2dce96f21c4f
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 string t;
81 if (_matrix->type() == ARDOUR::DataType::NIL) {
82 t = _("There are no ports to connect.");
83 } else {
84 t = string_compose (_("There are no %1 ports to connect."), _matrix->type().to_i18n_string());
87 cairo_text_extents_t ext;
88 cairo_text_extents (cr, t.c_str(), &ext);
90 cairo_set_source_rgb (cr, 1, 1, 1);
91 cairo_move_to (cr, (_alloc_width - ext.width) / 2, (_alloc_height + ext.height) / 2);
92 cairo_show_text (cr, t.c_str ());
94 cairo_destroy (cr);
96 return true;
99 Gdk::Rectangle const exposure (
100 event->area.x, event->area.y, event->area.width, event->area.height
103 bool intersects;
105 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
107 Gdk::Rectangle r = exposure;
109 /* the get_pixmap call may cause things to be rerendered and sizes to change,
110 so fetch the pixmap before calculating where to put it */
111 GdkPixmap* p = (*i)->get_pixmap (get_window()->gobj());
112 r.intersect ((*i)->parent_rectangle(), intersects);
114 if (intersects) {
116 gdk_draw_drawable (
117 get_window()->gobj(),
118 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
120 (*i)->parent_to_component_x (r.get_x()),
121 (*i)->parent_to_component_y (r.get_y()),
122 r.get_x(),
123 r.get_y(),
124 r.get_width(),
125 r.get_height()
131 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
133 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
134 cairo_save (cr);
135 set_cairo_clip (cr, (*i)->parent_rectangle ());
136 (*i)->draw_extra (cr);
137 cairo_restore (cr);
140 cairo_destroy (cr);
142 return true;
145 void
146 PortMatrixBody::on_size_request (Gtk::Requisition *req)
148 pair<int, int> const col = _column_labels->dimensions ();
149 pair<int, int> const row = _row_labels->dimensions ();
150 pair<int, int> const grid = _grid->dimensions ();
152 if (grid.first == 0 && grid.second == 0) {
153 /* nothing to display */
154 req->width = 256;
155 req->height = 64;
156 return;
159 /* don't ask for the maximum size of our contents, otherwise GTK won't
160 let the containing window shrink below this size */
162 /* XXX these shouldn't be hard-coded */
163 int const min_width = 512;
164 int const min_height = 512;
166 req->width = min (min_width, max (col.first, grid.first + row.first));
167 req->height = min (min_height / _matrix->min_height_divisor(), col.second + grid.second);
170 void
171 PortMatrixBody::on_size_allocate (Gtk::Allocation& alloc)
173 Gtk::EventBox::on_size_allocate (alloc);
175 _alloc_width = alloc.get_width ();
176 _alloc_height = alloc.get_height ();
178 compute_rectangles ();
179 _matrix->setup_scrollbars ();
182 void
183 PortMatrixBody::compute_rectangles ()
185 /* full sizes of components */
186 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
187 uint32_t const col_overhang = _column_labels->overhang ();
188 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
189 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
191 Gdk::Rectangle col_rect;
192 Gdk::Rectangle row_rect;
193 Gdk::Rectangle grid_rect;
195 if (_matrix->arrangement() == PortMatrix::TOP_TO_RIGHT) {
197 col_rect.set_x (0);
198 _column_labels_border_x = col_overhang;
199 col_rect.set_y (0);
200 grid_rect.set_x (0);
202 col_rect.set_width (min (col.first, _alloc_width));
204 uint32_t const y = min (_alloc_height, col.second);
205 col_rect.set_height (y);
206 row_rect.set_y (y);
207 row_rect.set_height (_alloc_height - y);
208 grid_rect.set_y (y);
209 grid_rect.set_height (_alloc_height - y);
211 uint32_t x = 0;
212 if (_alloc_width > (grid.first + row.first)) {
213 x = grid.first;
214 } else if (_alloc_width > row.first) {
215 x = _alloc_width - row.first;
218 grid_rect.set_width (x);
219 row_rect.set_x (x);
220 row_rect.set_width (_alloc_width - x);
223 } else if (_matrix->arrangement() == PortMatrix::LEFT_TO_BOTTOM) {
225 col_rect.set_height (min (_alloc_height, col.second));
226 row_rect.set_height (std::min (_alloc_height - col_rect.get_height(), row.second));
228 row_rect.set_x (0);
229 row_rect.set_y (_alloc_height - row_rect.get_height() - col_rect.get_height());
230 row_rect.set_width (min (_alloc_width, row.first));
232 grid_rect.set_x (row_rect.get_width());
233 grid_rect.set_y (_alloc_height - row_rect.get_height() - col_rect.get_height());
234 grid_rect.set_width (std::min (_alloc_width - row_rect.get_width(), grid.first));
235 grid_rect.set_height (row_rect.get_height ());
237 col_rect.set_width (grid_rect.get_width () + col_overhang);
238 col_rect.set_x (row_rect.get_width() + grid_rect.get_width() - col_rect.get_width());
239 _column_labels_border_x = col_rect.get_x () >= 0 ? col_rect.get_x () : 0;
240 col_rect.set_y (_alloc_height - col_rect.get_height());
243 _column_labels_height = col_rect.get_height ();
245 _row_labels->set_parent_rectangle (row_rect);
246 _column_labels->set_parent_rectangle (col_rect);
247 _grid->set_parent_rectangle (grid_rect);
249 DimensionsChanged (); /* EMIT SIGNAL */
252 void
253 PortMatrixBody::setup ()
255 /* Discard any old connections to bundles */
257 _bundle_connections.drop_connections ();
259 /* Connect to bundles so that we find out when their names change */
261 if (_matrix->visible_rows()) {
262 PortGroup::BundleList r = _matrix->visible_rows()->bundles ();
263 for (PortGroup::BundleList::iterator i = r.begin(); i != r.end(); ++i) {
265 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_row_labels, this), gui_context());
270 if (_matrix->visible_columns()) {
271 PortGroup::BundleList c = _matrix->visible_columns()->bundles ();
272 for (PortGroup::BundleList::iterator i = c.begin(); i != c.end(); ++i) {
273 (*i)->bundle->Changed.connect (_bundle_connections, invalidator (*this), boost::bind (&PortMatrixBody::rebuild_and_draw_column_labels, this), gui_context());
277 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
278 (*i)->setup ();
281 set_mouseover (PortMatrixNode ());
283 _ignore_component_size_changed = true;
284 compute_rectangles ();
285 _ignore_component_size_changed = false;
288 uint32_t
289 PortMatrixBody::full_scroll_width ()
291 return _grid->dimensions().first;
295 uint32_t
296 PortMatrixBody::alloc_scroll_width ()
298 return _grid->parent_rectangle().get_width();
301 uint32_t
302 PortMatrixBody::full_scroll_height ()
304 return _grid->dimensions().second;
307 uint32_t
308 PortMatrixBody::alloc_scroll_height ()
310 return _grid->parent_rectangle().get_height();
313 /** Set x offset (for scrolling) */
314 void
315 PortMatrixBody::set_xoffset (uint32_t xo)
317 _xoffset = xo;
318 queue_draw ();
321 /** Set y offset (for scrolling) */
322 void
323 PortMatrixBody::set_yoffset (uint32_t yo)
325 _yoffset = yo;
326 queue_draw ();
329 bool
330 PortMatrixBody::on_button_press_event (GdkEventButton* ev)
332 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
333 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
334 (*i)->button_press (
335 (*i)->parent_to_component_x (ev->x),
336 (*i)->parent_to_component_y (ev->y),
337 ev->button, ev->time, ev->state
342 return true;
345 bool
346 PortMatrixBody::on_button_release_event (GdkEventButton* ev)
348 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
349 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
350 (*i)->button_release (
351 (*i)->parent_to_component_x (ev->x),
352 (*i)->parent_to_component_y (ev->y),
353 ev->button, ev->time, ev->state
355 } else {
356 (*i)->button_release (
357 -1, -1,
358 ev->button, ev->time, ev->state
363 return true;
366 void
367 PortMatrixBody::rebuild_and_draw_grid ()
369 _grid->require_rebuild ();
370 queue_draw ();
373 void
374 PortMatrixBody::rebuild_and_draw_column_labels ()
376 _column_labels->require_rebuild ();
377 queue_draw ();
380 void
381 PortMatrixBody::rebuild_and_draw_row_labels ()
383 _row_labels->require_rebuild ();
384 queue_draw ();
387 bool
388 PortMatrixBody::on_leave_notify_event (GdkEventCrossing* ev)
390 if (ev->type == GDK_LEAVE_NOTIFY) {
391 set_mouseover (PortMatrixNode ());
394 return true;
397 bool
398 PortMatrixBody::on_motion_notify_event (GdkEventMotion* ev)
400 bool done = false;
402 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
403 if (Gdk::Region ((*i)->parent_rectangle()).point_in (ev->x, ev->y)) {
404 (*i)->motion (
405 (*i)->parent_to_component_x (ev->x),
406 (*i)->parent_to_component_y (ev->y)
409 done = true;
414 if (!done) {
415 set_mouseover (PortMatrixNode ());
418 return true;
421 void
422 PortMatrixBody::set_mouseover (PortMatrixNode const & n)
424 list<PortMatrixNode> m;
425 m.push_back (n);
426 set_mouseover (m);
429 void
430 PortMatrixBody::set_mouseover (list<PortMatrixNode> const & n)
432 if (n == _mouseover) {
433 return;
436 /* Channel highlights are set up only on mouseovers, so
437 it's reasonable to remove all channel highlights here.
438 We can't let individual components clear their own highlights
439 because of the case where, say, the row labels set up some column
440 highlights, and then we ask the column labels to set up their
441 own highlights and they clear them out before they start.
444 _row_labels->clear_channel_highlights ();
445 _column_labels->clear_channel_highlights ();
447 list<PortMatrixNode> old = _mouseover;
448 _mouseover = n;
450 for (list<PortMatrixComponent*>::iterator i = _components.begin(); i != _components.end(); ++i) {
451 (*i)->mouseover_changed (old);
455 void
456 PortMatrixBody::highlight_associated_channels (int dim, ARDOUR::BundleChannel h)
458 ARDOUR::BundleChannel bc[2];
459 bc[dim] = h;
461 if (!bc[dim].bundle) {
462 return;
465 if (dim == _matrix->column_index()) {
466 _column_labels->add_channel_highlight (bc[dim]);
467 } else {
468 _row_labels->add_channel_highlight (bc[dim]);
471 PortGroup::BundleList const b = _matrix->visible_ports(1 - dim)->bundles ();
473 for (PortGroup::BundleList::const_iterator i = b.begin(); i != b.end(); ++i) {
474 for (uint32_t j = 0; j < (*i)->bundle->nchannels().n_total(); ++j) {
476 if (!_matrix->should_show ((*i)->bundle->channel_type(j))) {
477 continue;
480 bc[1 - dim] = ARDOUR::BundleChannel ((*i)->bundle, j);
482 PortMatrixNode n;
483 n.row = bc[_matrix->row_index()];
484 n.column = bc[_matrix->column_index()];
486 if (_matrix->get_association(n) != PortMatrixNode::NOT_ASSOCIATED) {
487 if (dim == _matrix->column_index()) {
488 _row_labels->add_channel_highlight (bc[1 - dim]);
489 } else {
490 _column_labels->add_channel_highlight (bc[1 - dim]);
497 void
498 PortMatrixBody::set_cairo_clip (cairo_t* cr, Gdk::Rectangle const & r) const
500 cairo_rectangle (cr, r.get_x(), r.get_y(), r.get_width(), r.get_height());
501 cairo_clip (cr);
504 void
505 PortMatrixBody::component_size_changed ()
507 if (_ignore_component_size_changed) {
508 return;
511 compute_rectangles ();
512 _matrix->setup_scrollbars ();
515 pair<uint32_t, uint32_t>
516 PortMatrixBody::max_size () const
518 pair<uint32_t, uint32_t> const col = _column_labels->dimensions ();
519 pair<uint32_t, uint32_t> const row = _row_labels->dimensions ();
520 pair<uint32_t, uint32_t> const grid = _grid->dimensions ();
522 return make_pair (std::max (row.first, _column_labels->overhang()) + grid.first, col.second + grid.second);
525 /** @return x position at which the column labels meet the border of the matrix */
526 uint32_t
527 PortMatrixBody::column_labels_border_x () const
529 return _column_labels_border_x;
532 uint32_t
533 PortMatrixBody::column_labels_height () const
535 return _column_labels_height;