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());
110 boost::optional<Rect> draw = item.intersection (area);
114 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
115 cerr << _canvas->render_indent() << " render "
128 (*i)->render (area, context);
133 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
134 //cerr << string_compose ("%1skip render of %2 %3, no intersection\n", _canvas->render_indent(), (*i)->whatami(),
145 Group::compute_bounding_box () const
148 bool have_one = false;
150 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
152 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
158 Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
160 bbox = bbox.extend (group_bbox);
168 _bounding_box = boost::optional<Rect> ();
170 _bounding_box = bbox;
173 _bounding_box_dirty = false;
179 /* XXX should really notify canvas about this */
181 _items.push_back (i);
183 _bounding_box_dirty = true;
189 Group::remove (Item* i)
192 if (i->parent() != this) {
201 _bounding_box_dirty = true;
207 Group::clear (bool with_delete)
211 for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
222 _bounding_box_dirty = true;
228 Group::raise_child_to_top (Item* i)
231 _items.push_back (i);
236 Group::raise_child (Item* i, int levels)
238 list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
239 assert (j != _items.end ());
244 while (levels > 0 && j != _items.end ()) {
249 _items.insert (j, i);
254 Group::lower_child_to_bottom (Item* i)
257 _items.push_front (i);
262 Group::ensure_lut () const
265 _lut = new DumbLookupTable (*this);
270 Group::invalidate_lut () const
277 Group::child_changed ()
280 _bounding_box_dirty = true;
283 _parent->child_changed ();
288 Group::add_items_at_point (Duple const point, vector<Item const *>& items) const
290 /* Point is in canvas coordinate system */
292 boost::optional<Rect> const bbox = bounding_box ();
294 if (!bbox || !bbox.get().contains (point)) {
298 /* this adds this group itself to the list of items at point */
299 Item::add_items_at_point (point, items);
302 /* now recurse and add any items within our group that contain point */
305 vector<Item*> our_items = _lut->items_at_point (point);
307 for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
308 (*i)->add_items_at_point (point - (*i)->position(), items);
313 Group::dump (ostream& o) const
316 o << _canvas->indent();
317 o << "Group " << this << " [" << name << ']';
318 o << " @ " << position();
319 o << " Items: " << _items.size();
320 o << " Visible ? " << _visible;
322 boost::optional<Rect> bb = bounding_box();
325 o << endl << _canvas->indent() << " bbox: " << bb.get();
326 o << endl << _canvas->indent() << " CANVAS bbox: " << item_to_canvas (bb.get());
334 ArdourCanvas::dump_depth++;
336 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
340 ArdourCanvas::dump_depth--;