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"
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"
35 PortMatrixBody::PortMatrixBody (PortMatrix
* p
)
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
) {
65 PortMatrixBody::on_expose_event (GdkEventExpose
* event
)
68 _matrix
->visible_columns() == 0 || _matrix
->visible_rows() == 0 ||
69 _matrix
->visible_columns()->bundles().empty() || _matrix
->visible_rows()->bundles().empty()
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
);
81 if (_matrix
->type() == ARDOUR::DataType::NIL
) {
82 t
= _("There are no ports to connect.");
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 ());
99 Gdk::Rectangle
const exposure (
100 event
->area
.x
, event
->area
.y
, event
->area
.width
, event
->area
.height
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
);
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()),
131 cairo_t
* cr
= gdk_cairo_create (get_window()->gobj());
133 for (list
<PortMatrixComponent
*>::iterator i
= _components
.begin(); i
!= _components
.end(); ++i
) {
135 set_cairo_clip (cr
, (*i
)->parent_rectangle ());
136 (*i
)->draw_extra (cr
);
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 */
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
);
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 ();
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
) {
198 _column_labels_border_x
= col_overhang
;
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
);
207 row_rect
.set_height (_alloc_height
- y
);
209 grid_rect
.set_height (_alloc_height
- y
);
212 if (_alloc_width
> (grid
.first
+ row
.first
)) {
214 } else if (_alloc_width
> row
.first
) {
215 x
= _alloc_width
- row
.first
;
218 grid_rect
.set_width (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
));
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 */
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
) {
281 set_mouseover (PortMatrixNode ());
283 _ignore_component_size_changed
= true;
284 compute_rectangles ();
285 _ignore_component_size_changed
= false;
289 PortMatrixBody::full_scroll_width ()
291 return _grid
->dimensions().first
;
296 PortMatrixBody::alloc_scroll_width ()
298 return _grid
->parent_rectangle().get_width();
302 PortMatrixBody::full_scroll_height ()
304 return _grid
->dimensions().second
;
308 PortMatrixBody::alloc_scroll_height ()
310 return _grid
->parent_rectangle().get_height();
313 /** Set x offset (for scrolling) */
315 PortMatrixBody::set_xoffset (uint32_t xo
)
321 /** Set y offset (for scrolling) */
323 PortMatrixBody::set_yoffset (uint32_t yo
)
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
)) {
335 (*i
)->parent_to_component_x (ev
->x
),
336 (*i
)->parent_to_component_y (ev
->y
),
337 ev
->button
, ev
->time
, ev
->state
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
356 (*i
)->button_release (
358 ev
->button
, ev
->time
, ev
->state
367 PortMatrixBody::rebuild_and_draw_grid ()
369 _grid
->require_rebuild ();
374 PortMatrixBody::rebuild_and_draw_column_labels ()
376 _column_labels
->require_rebuild ();
381 PortMatrixBody::rebuild_and_draw_row_labels ()
383 _row_labels
->require_rebuild ();
388 PortMatrixBody::on_leave_notify_event (GdkEventCrossing
* ev
)
390 if (ev
->type
== GDK_LEAVE_NOTIFY
) {
391 set_mouseover (PortMatrixNode ());
398 PortMatrixBody::on_motion_notify_event (GdkEventMotion
* ev
)
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
)) {
405 (*i
)->parent_to_component_x (ev
->x
),
406 (*i
)->parent_to_component_y (ev
->y
)
415 set_mouseover (PortMatrixNode ());
422 PortMatrixBody::set_mouseover (PortMatrixNode
const & n
)
424 list
<PortMatrixNode
> m
;
430 PortMatrixBody::set_mouseover (list
<PortMatrixNode
> const & n
)
432 if (n
== _mouseover
) {
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
;
450 for (list
<PortMatrixComponent
*>::iterator i
= _components
.begin(); i
!= _components
.end(); ++i
) {
451 (*i
)->mouseover_changed (old
);
456 PortMatrixBody::highlight_associated_channels (int dim
, ARDOUR::BundleChannel h
)
458 ARDOUR::BundleChannel bc
[2];
461 if (!bc
[dim
].bundle
) {
465 if (dim
== _matrix
->column_index()) {
466 _column_labels
->add_channel_highlight (bc
[dim
]);
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
))) {
480 bc
[1 - dim
] = ARDOUR::BundleChannel ((*i
)->bundle
, j
);
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
]);
490 _column_labels
->add_channel_highlight (bc
[1 - dim
]);
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());
505 PortMatrixBody::component_size_changed ()
507 if (_ignore_component_size_changed
) {
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 */
527 PortMatrixBody::column_labels_border_x () const
529 return _column_labels_border_x
;
533 PortMatrixBody::column_labels_height () const
535 return _column_labels_height
;