2 Copyright (C) 2011-2013 Paul Davis
3 Author: Carl Hetherington <cth@carlh.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <cairomm/context.h>
23 #include "pbd/stacktrace.h"
24 #include "pbd/compose.h"
26 #include "canvas/group.h"
27 #include "canvas/types.h"
28 #include "canvas/debug.h"
29 #include "canvas/item.h"
30 #include "canvas/canvas.h"
33 using namespace ArdourCanvas;
35 int Group::default_items_per_cell = 64;
38 Group::Group (Canvas* canvas)
45 Group::Group (Group* parent)
52 Group::Group (Group* parent, Duple position)
53 : Item (parent, position)
64 /** @param area Area to draw in this group's coordinates.
65 * @param context Context, set up with its origin at this group's position.
68 Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
71 vector<Item*> items = _lut->get (area);
74 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
75 cerr << string_compose ("%1GROUP %2 @ %7 render %5 @ %6 %3 items out of %4\n",
76 _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area, _position, this);
82 for (vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
84 if (!(*i)->visible ()) {
86 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
87 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
93 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
97 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
98 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
104 Rect item = (*i)->item_to_window (item_bbox.get());
105 boost::optional<Rect> d = item.intersection (area);
109 if (draw.width() && draw.height()) {
111 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
112 if (dynamic_cast<Group*>(*i) == 0) {
113 cerr << _canvas->render_indent() << "render "
131 (*i)->render (area, context);
138 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
139 cerr << string_compose ("%1skip render of %2 %3, no intersection\n", _canvas->render_indent(), (*i)->whatami(),
151 Group::compute_bounding_box () const
154 bool have_one = false;
156 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
158 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
164 Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
166 bbox = bbox.extend (group_bbox);
174 _bounding_box = boost::optional<Rect> ();
176 _bounding_box = bbox;
179 _bounding_box_dirty = false;
185 /* XXX should really notify canvas about this */
187 _items.push_back (i);
189 _bounding_box_dirty = true;
195 Group::remove (Item* i)
198 if (i->parent() != this) {
207 _bounding_box_dirty = true;
213 Group::clear (bool with_delete)
217 for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
229 _bounding_box_dirty = true;
235 Group::raise_child_to_top (Item* i)
237 if (!_items.empty()) {
238 if (_items.back() == i) {
244 _items.push_back (i);
249 Group::raise_child (Item* i, int levels)
251 list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
252 assert (j != _items.end ());
257 while (levels > 0 && j != _items.end ()) {
262 _items.insert (j, i);
267 Group::lower_child_to_bottom (Item* i)
269 if (!_items.empty()) {
270 if (_items.front() == i) {
275 _items.push_front (i);
280 Group::ensure_lut () const
283 _lut = new DumbLookupTable (*this);
288 Group::invalidate_lut () const
295 Group::child_changed ()
298 _bounding_box_dirty = true;
301 _parent->child_changed ();
306 Group::add_items_at_point (Duple const point, vector<Item const *>& items) const
308 boost::optional<Rect> const bbox = bounding_box ();
310 /* Point is in canvas coordinate system */
312 if (!bbox || !item_to_canvas (bbox.get()).contains (point)) {
316 /* now recurse and add any items within our group that contain point */
319 vector<Item*> our_items = _lut->items_at_point (point);
321 if (!our_items.empty()) {
322 /* this adds this group itself to the list of items at point */
323 Item::add_items_at_point (point, items);
326 for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
327 (*i)->add_items_at_point (point, items);
332 Group::dump (ostream& o) const
335 o << _canvas->indent();
336 o << "Group " << this << " [" << name << ']';
337 o << " @ " << position();
338 o << " Items: " << _items.size();
339 o << " Visible ? " << _visible;
341 boost::optional<Rect> bb = bounding_box();
344 o << endl << _canvas->indent() << " bbox: " << bb.get();
345 o << endl << _canvas->indent() << " CANVAS bbox: " << item_to_canvas (bb.get());
353 ArdourCanvas::dump_depth++;
355 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
359 ArdourCanvas::dump_depth--;