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)
61 for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
68 /** @param area Area to draw in this group's coordinates.
69 * @param context Context, set up with its origin at this group's position.
72 Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
75 vector<Item*> items = _lut->get (area);
80 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
81 cerr << string_compose ("%1GROUP %2 render %5 %3 items out of %4\n",
82 _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area);
86 for (vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
88 if (!(*i)->visible ()) {
90 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
91 // cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
97 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
101 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
102 // cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
108 Rect item = (*i)->item_to_window (item_bbox.get());
109 boost::optional<Rect> draw = item.intersection (area);
113 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
114 cerr << string_compose ("%1render %2 %3\n", _canvas->render_indent(), (*i)->whatami(),
119 (*i)->render (area, context);
124 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
125 //cerr << string_compose ("%1skip render of %2 %3, no intersection\n", _canvas->render_indent(), (*i)->whatami(),
136 Group::compute_bounding_box () const
139 bool have_one = false;
141 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
142 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
147 Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
149 bbox = bbox.extend (group_bbox);
157 _bounding_box = boost::optional<Rect> ();
159 _bounding_box = bbox;
162 _bounding_box_dirty = false;
168 /* XXX should really notify canvas about this */
170 _items.push_back (i);
172 _bounding_box_dirty = true;
178 Group::remove (Item* i)
181 if (i->parent() != this) {
190 _bounding_box_dirty = true;
196 Group::clear (bool with_delete)
200 for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
211 _bounding_box_dirty = true;
217 Group::raise_child_to_top (Item* i)
220 _items.push_back (i);
225 Group::raise_child (Item* i, int levels)
227 list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
228 assert (j != _items.end ());
233 while (levels > 0 && j != _items.end ()) {
238 _items.insert (j, i);
243 Group::lower_child_to_bottom (Item* i)
246 _items.push_front (i);
251 Group::ensure_lut () const
254 _lut = new DumbLookupTable (*this);
259 Group::invalidate_lut () const
266 Group::child_changed ()
269 _bounding_box_dirty = true;
272 _parent->child_changed ();
277 Group::add_items_at_point (Duple const point, vector<Item const *>& items) const
279 boost::optional<Rect> const bbox = bounding_box ();
281 if (!bbox || !bbox.get().contains (point)) {
285 Item::add_items_at_point (point, items);
289 vector<Item*> our_items = _lut->items_at_point (point);
290 for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
291 (*i)->add_items_at_point (point - (*i)->position(), items);
296 Group::dump (ostream& o) const
299 o << _canvas->indent();
300 o << "Group " << this << " [" << name << ']';
301 o << " @ " << position();
302 o << " Items: " << _items.size();
303 o << " Visible ? " << _visible;
305 boost::optional<Rect> bb = bounding_box();
308 o << endl << _canvas->indent() << " bbox: " << bb.get();
309 o << endl << _canvas->indent() << " CANVAS bbox: " << item_to_canvas (bb.get());
317 ArdourCanvas::dump_depth++;
319 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
323 ArdourCanvas::dump_depth--;