+ /* XXX should really notify canvas about this */
+
+ _items.push_back (i);
+ i->reparent (this);
+ invalidate_lut ();
+ _bounding_box_dirty = true;
+}
+
+void
+Item::remove (Item* i)
+{
+
+ if (i->parent() != this) {
+ return;
+ }
+
+ /* we cannot call bounding_box() here because that will iterate over
+ _items, one of which (the argument, i) may be in the middle of
+ deletion, making it impossible to call compute_bounding_box()
+ on it.
+ */
+
+ if (_bounding_box) {
+ _pre_change_bounding_box = _bounding_box;
+ } else {
+ _pre_change_bounding_box = Rect();
+ }
+
+ i->unparent ();
+ _items.remove (i);
+ invalidate_lut ();
+ _bounding_box_dirty = true;
+
+ end_change ();
+}
+
+void
+Item::clear (bool with_delete)
+{
+ begin_change ();
+
+ clear_items (with_delete);
+
+ invalidate_lut ();
+ _bounding_box_dirty = true;
+
+ end_change ();
+}
+
+void
+Item::clear_items (bool with_delete)
+{
+ for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
+
+ list<Item*>::iterator tmp = i;
+ Item *item = *i;
+
+ ++tmp;
+
+ /* remove from list before doing anything else, because we
+ * don't want to find the item in _items during any activity
+ * driven by unparent-ing or deletion.
+ */
+
+ _items.erase (i);
+ item->unparent ();
+
+ if (with_delete) {
+ delete item;
+ }
+
+ i = tmp;
+ }
+}
+
+void
+Item::raise_child_to_top (Item* i)
+{
+ if (!_items.empty()) {
+ if (_items.back() == i) {
+ return;
+ }
+ }
+
+ _items.remove (i);
+ _items.push_back (i);
+
+ invalidate_lut ();
+ redraw ();
+}
+
+void
+Item::raise_child (Item* i, int levels)
+{
+ list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
+ assert (j != _items.end ());
+
+ ++j;
+ _items.remove (i);
+
+ while (levels > 0 && j != _items.end ()) {
+ ++j;
+ --levels;
+ }
+
+ _items.insert (j, i);
+ invalidate_lut ();
+ redraw ();
+}
+
+void
+Item::lower_child_to_bottom (Item* i)
+{
+ if (!_items.empty()) {
+ if (_items.front() == i) {
+ return;
+ }
+ }
+ _items.remove (i);
+ _items.push_front (i);
+ invalidate_lut ();
+ redraw ();
+}
+
+void
+Item::ensure_lut () const
+{
+ if (!_lut) {
+ _lut = new DumbLookupTable (*this);
+ }
+}
+
+void
+Item::invalidate_lut () const
+{
+ delete _lut;
+ _lut = 0;
+}
+
+void
+Item::child_changed ()
+{
+ invalidate_lut ();
+ _bounding_box_dirty = true;
+
+ if (_parent) {
+ _parent->child_changed ();
+ }
+}
+
+void
+Item::add_items_at_point (Duple const point, vector<Item const *>& items) const
+{
+ boost::optional<Rect> const bbox = bounding_box ();
+
+ /* Point is in window coordinate system */
+
+ if (!bbox || !item_to_window (bbox.get()).contains (point)) {
+ return;
+ }
+
+ /* recurse and add any items within our group that contain point.
+ Our children are only considered visible if we are, and similarly
+ only if we do not ignore events.
+ */
+
+ vector<Item*> our_items;
+
+ if (!_items.empty() && visible() && !_ignore_events) {
+ ensure_lut ();
+ our_items = _lut->items_at_point (point);
+ }
+
+ if (!our_items.empty() || covers (point)) {
+ /* this adds this item itself to the list of items at point */
+ items.push_back (this);
+ }
+
+ for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
+ (*i)->add_items_at_point (point, items);
+ }
+}
+
+void
+Item::set_tooltip (const std::string& s)
+{
+ _tooltip = s;
+}
+
+void
+Item::start_tooltip_timeout ()
+{
+ if (!_tooltip.empty()) {
+ _canvas->start_tooltip_timeout (this);
+ }
+}
+
+void
+Item::stop_tooltip_timeout ()
+{
+ _canvas->stop_tooltip_timeout ();