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 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 ());
95 Gdk::Rectangle
const exposure (
96 event
->area
.x
, event
->area
.y
, event
->area
.width
, event
->area
.height
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
);
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()),
127 cairo_t
* cr
= gdk_cairo_create (get_window()->gobj());
129 for (list
<PortMatrixComponent
*>::iterator i
= _components
.begin(); i
!= _components
.end(); ++i
) {
131 set_cairo_clip (cr
, (*i
)->parent_rectangle ());
132 (*i
)->draw_extra (cr
);
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 */
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
);
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 ();
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
) {
194 _column_labels_border_x
= col_overhang
;
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
);
203 row_rect
.set_height (_alloc_height
- y
);
205 grid_rect
.set_height (_alloc_height
- y
);
208 if (_alloc_width
> (grid
.first
+ row
.first
)) {
210 } else if (_alloc_width
> row
.first
) {
211 x
= _alloc_width
- row
.first
;
214 grid_rect
.set_width (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
));
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());
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 */
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
) {
277 set_mouseover (PortMatrixNode ());
279 _ignore_component_size_changed
= true;
280 compute_rectangles ();
281 _ignore_component_size_changed
= false;
285 PortMatrixBody::full_scroll_width ()
287 return _grid
->dimensions().first
;
292 PortMatrixBody::alloc_scroll_width ()
294 return _grid
->parent_rectangle().get_width();
298 PortMatrixBody::full_scroll_height ()
300 return _grid
->dimensions().second
;
304 PortMatrixBody::alloc_scroll_height ()
306 return _grid
->parent_rectangle().get_height();
309 /** Set x offset (for scrolling) */
311 PortMatrixBody::set_xoffset (uint32_t xo
)
317 /** Set y offset (for scrolling) */
319 PortMatrixBody::set_yoffset (uint32_t yo
)
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
)) {
331 (*i
)->parent_to_component_x (ev
->x
),
332 (*i
)->parent_to_component_y (ev
->y
),
333 ev
->button
, ev
->time
, ev
->state
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
352 (*i
)->button_release (
354 ev
->button
, ev
->time
, ev
->state
363 PortMatrixBody::rebuild_and_draw_grid ()
365 _grid
->require_rebuild ();
370 PortMatrixBody::rebuild_and_draw_column_labels ()
372 _column_labels
->require_rebuild ();
377 PortMatrixBody::rebuild_and_draw_row_labels ()
379 _row_labels
->require_rebuild ();
384 PortMatrixBody::on_leave_notify_event (GdkEventCrossing
* ev
)
386 if (ev
->type
== GDK_LEAVE_NOTIFY
) {
387 set_mouseover (PortMatrixNode ());
394 PortMatrixBody::on_motion_notify_event (GdkEventMotion
* ev
)
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
)) {
401 (*i
)->parent_to_component_x (ev
->x
),
402 (*i
)->parent_to_component_y (ev
->y
)
411 set_mouseover (PortMatrixNode ());
418 PortMatrixBody::set_mouseover (PortMatrixNode
const & n
)
420 list
<PortMatrixNode
> m
;
426 PortMatrixBody::set_mouseover (list
<PortMatrixNode
> const & n
)
428 if (n
== _mouseover
) {
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
;
446 for (list
<PortMatrixComponent
*>::iterator i
= _components
.begin(); i
!= _components
.end(); ++i
) {
447 (*i
)->mouseover_changed (old
);
452 PortMatrixBody::highlight_associated_channels (int dim
, ARDOUR::BundleChannel h
)
454 ARDOUR::BundleChannel bc
[2];
457 if (!bc
[dim
].bundle
) {
461 if (dim
== _matrix
->column_index()) {
462 _column_labels
->add_channel_highlight (bc
[dim
]);
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
]);
476 _column_labels
->add_channel_highlight (bc
[1 - dim
]);
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());
491 PortMatrixBody::component_size_changed ()
493 if (_ignore_component_size_changed
) {
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 */
513 PortMatrixBody::column_labels_border_x () const
515 return _column_labels_border_x
;
519 PortMatrixBody::column_labels_height () const
521 return _column_labels_height
;