fix crash when copy'ing latent plugins
[ardour.git] / libs / canvas / item.cc
1 /*
2     Copyright (C) 2011-2013 Paul Davis
3     Author: Carl Hetherington <cth@carlh.net>
4
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.
9
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.
14
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.
18 */
19
20 #include "pbd/compose.h"
21 #include "pbd/demangle.h"
22 #include "pbd/convert.h"
23
24 #include "ardour/utils.h"
25
26 #include "canvas/canvas.h"
27 #include "canvas/debug.h"
28 #include "canvas/item.h"
29 #include "canvas/scroll_group.h"
30
31 using namespace std;
32 using namespace PBD;
33 using namespace ArdourCanvas;
34
35 int Item::default_items_per_cell = 64;
36
37 Item::Item (Canvas* canvas)
38         : Fill (*this)
39         , Outline (*this)
40         ,  _canvas (canvas)
41         , _parent (0)
42         , _scroll_parent (0)
43         , _visible (true)
44         , _bounding_box_dirty (true)
45         , _lut (0)
46         , _ignore_events (false)
47 {
48         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
49 }
50
51 Item::Item (Item* parent)
52         : Fill (*this)
53         , Outline (*this)
54         ,  _canvas (parent->canvas())
55         , _parent (parent)
56         , _scroll_parent (0)
57         , _visible (true)
58         , _bounding_box_dirty (true)
59         , _lut (0)
60         , _ignore_events (false)
61 {
62         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
63
64         if (parent) {
65                 _parent->add (this);
66         }
67
68         find_scroll_parent ();
69 }
70
71 Item::Item (Item* parent, Duple const& p)
72         : Fill (*this)
73         , Outline (*this)
74         ,  _canvas (parent->canvas())
75         , _parent (parent)
76         , _scroll_parent (0)
77         , _position (p)
78         , _visible (true)
79         , _bounding_box_dirty (true)
80         , _lut (0)
81         , _ignore_events (false)
82 {
83         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
84
85         if (parent) {
86                 _parent->add (this);
87         }
88
89         find_scroll_parent ();
90
91 }
92
93 Item::~Item ()
94 {
95         if (_parent) {
96                 _parent->remove (this);
97         }
98
99         if (_canvas) {
100                 _canvas->item_going_away (this, _bounding_box);
101         }
102
103         clear_items (true);
104         delete _lut;
105 }
106
107 bool
108 Item::visible() const
109 {
110         Item const * i = this;
111
112         while (i) {
113                 if (!i->self_visible()) {
114                         return false;
115                 }
116                 i = i->parent();
117         }
118
119         return true;
120 }
121
122 Duple
123 Item::canvas_origin () const
124 {
125         return item_to_canvas (Duple (0,0));
126 }
127
128 Duple
129 Item::window_origin () const
130 {
131         /* This is slightly subtle. Our _position is in the coordinate space of
132            our parent. So to find out where that is in window coordinates, we
133            have to ask our parent.
134         */
135         if (_parent) {
136                 return _parent->item_to_window (_position);
137         } else {
138                 return _position;
139         }
140 }
141
142 ArdourCanvas::Rect
143 Item::item_to_parent (ArdourCanvas::Rect const & r) const
144 {
145         return r.translate (_position);
146 }
147
148 Duple
149 Item::scroll_offset () const
150 {
151         if (_scroll_parent) {
152                 return _scroll_parent->scroll_offset();
153         }
154         return Duple (0,0);
155 }
156
157 Duple
158 Item::position_offset() const
159 {
160         Item const * i = this;
161         Duple offset;
162
163         while (i) {
164                 offset = offset.translate (i->position());
165                 i = i->parent();
166         }
167
168         return offset;
169 }
170
171 ArdourCanvas::Rect
172 Item::item_to_canvas (ArdourCanvas::Rect const & r) const
173 {
174         return r.translate (position_offset());
175 }
176
177 ArdourCanvas::Duple
178 Item::item_to_canvas (ArdourCanvas::Duple const & d) const
179 {
180         return d.translate (position_offset());
181 }
182
183 ArdourCanvas::Duple
184 Item::canvas_to_item (ArdourCanvas::Duple const & r) const
185 {
186         return r.translate (-position_offset());
187 }
188
189 ArdourCanvas::Rect
190 Item::canvas_to_item (ArdourCanvas::Rect const & r) const
191 {
192         return r.translate (-position_offset());
193 }
194
195 void
196 Item::item_to_canvas (Coord& x, Coord& y) const
197 {
198         Duple d = item_to_canvas (Duple (x, y));
199
200         x = d.x;
201         y = d.y;
202 }
203
204 void
205 Item::canvas_to_item (Coord& x, Coord& y) const
206 {
207         Duple d = canvas_to_item (Duple (x, y));
208
209         x = d.x;
210         y = d.y;
211 }
212
213
214 Duple
215 Item::item_to_window (ArdourCanvas::Duple const & d, bool rounded) const
216 {
217         Duple ret = item_to_canvas (d).translate (-scroll_offset());
218
219         if (rounded) {
220                 ret.x = round (ret.x);
221                 ret.y = round (ret.y);
222         }
223
224         return ret;
225 }
226
227 Duple
228 Item::window_to_item (ArdourCanvas::Duple const & d) const
229 {
230         return canvas_to_item (d.translate (scroll_offset()));
231 }
232
233 ArdourCanvas::Rect
234 Item::item_to_window (ArdourCanvas::Rect const & r, bool rounded) const
235 {
236         Rect ret = item_to_canvas (r).translate (-scroll_offset());
237
238         if (rounded) {
239                 ret.x0 = round (ret.x0);
240                 ret.x1 = round (ret.x1);
241                 ret.y0 = round (ret.y0);
242                 ret.y1 = round (ret.y1);
243         }
244
245         return ret;
246 }
247
248 ArdourCanvas::Rect
249 Item::window_to_item (ArdourCanvas::Rect const & r) const
250 {
251         return canvas_to_item (r.translate (scroll_offset()));
252 }
253
254 /** Set the position of this item in the parent's coordinates */
255 void
256 Item::set_position (Duple p)
257 {
258         if (p == _position) {
259                 return;
260         }
261
262         boost::optional<ArdourCanvas::Rect> bbox = bounding_box ();
263         boost::optional<ArdourCanvas::Rect> pre_change_parent_bounding_box;
264
265         if (bbox) {
266                 /* see the comment in Canvas::item_moved() to understand
267                  * why we use the parent's bounding box here.
268                  */
269                 pre_change_parent_bounding_box = item_to_parent (bbox.get());
270         }
271
272         _position = p;
273
274         /* only update canvas and parent if visible. Otherwise, this
275            will be done when ::show() is called.
276         */
277
278         if (visible()) {
279                 _canvas->item_moved (this, pre_change_parent_bounding_box);
280
281
282                 if (_parent) {
283                         _parent->child_changed ();
284                 }
285         }
286 }
287
288 void
289 Item::set_x_position (Coord x)
290 {
291         set_position (Duple (x, _position.y));
292 }
293
294 void
295 Item::set_y_position (Coord y)
296 {
297         set_position (Duple (_position.x, y));
298 }
299
300 void
301 Item::raise_to_top ()
302 {
303         if (_parent) {
304                 _parent->raise_child_to_top (this);
305         }
306 }
307
308 void
309 Item::raise (int levels)
310 {
311         if (_parent) {
312                 _parent->raise_child (this, levels);
313         }
314 }
315
316 void
317 Item::lower_to_bottom ()
318 {
319         if (_parent) {
320                 _parent->lower_child_to_bottom (this);
321         }
322 }
323
324 void
325 Item::hide ()
326 {
327         if (_visible) {
328                 _visible = false;
329
330                 /* children are all hidden because we are hidden, no need
331                    to propagate change because our bounding box necessarily
332                    includes them all already. thus our being hidden results
333                    in (a) a redraw of the entire bounding box (b) no children
334                    will be drawn.
335
336                    BUT ... current item in canvas might be one of our children,
337                    which is now hidden. So propagate away.
338                 */
339
340                 for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
341
342                         if ((*i)->self_visible()) {
343                                 /* item was visible but is now hidden because
344                                    we (its parent) are hidden
345                                 */
346                                 (*i)->propagate_show_hide ();
347                         }
348                 }
349
350
351                 propagate_show_hide ();
352         }
353 }
354
355 void
356 Item::show ()
357 {
358         if (!_visible) {
359
360                 _visible = true;
361
362                 for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
363                         if ((*i)->self_visible()) {
364                                 /* item used to be hidden by us (its parent),
365                                    but is now visible
366                                 */
367                                 (*i)->propagate_show_hide ();
368                         }
369                 }
370
371                 propagate_show_hide ();
372         }
373 }
374
375 void
376 Item::propagate_show_hide ()
377 {
378         /* bounding box may have changed while we were hidden */
379
380         if (_parent) {
381                 _parent->child_changed ();
382         }
383
384         _canvas->item_shown_or_hidden (this);
385 }
386
387 Duple
388 Item::item_to_parent (Duple const & d) const
389 {
390         return d.translate (_position);
391 }
392
393 Duple
394 Item::parent_to_item (Duple const & d) const
395 {
396         return d.translate (- _position);
397 }
398
399 ArdourCanvas::Rect
400 Item::parent_to_item (ArdourCanvas::Rect const & d) const
401 {
402         return d.translate (- _position);
403 }
404
405 void
406 Item::unparent ()
407 {
408         _parent = 0;
409         _scroll_parent = 0;
410 }
411
412 void
413 Item::reparent (Item* new_parent)
414 {
415         if (new_parent == _parent) {
416                 return;
417         }
418
419         assert (_canvas == new_parent->canvas());
420
421         if (_parent) {
422                 _parent->remove (this);
423         }
424
425         assert (new_parent);
426
427         _parent = new_parent;
428         _canvas = _parent->canvas ();
429
430         find_scroll_parent ();
431
432         _parent->add (this);
433 }
434
435 void
436 Item::find_scroll_parent ()
437 {
438         Item const * i = this;
439         ScrollGroup const * last_scroll_group = 0;
440
441         /* Don't allow a scroll group to find itself as its own scroll parent
442          */
443
444         i = i->parent ();
445
446         while (i) {
447                 ScrollGroup const * sg = dynamic_cast<ScrollGroup const *> (i);
448                 if (sg) {
449                         last_scroll_group = sg;
450                 }
451                 i = i->parent();
452         }
453
454         _scroll_parent = const_cast<ScrollGroup*> (last_scroll_group);
455 }
456
457 bool
458 Item::common_ancestor_within (uint32_t limit, const Item& other) const
459 {
460         uint32_t d1 = depth();
461         uint32_t d2 = other.depth();
462         const Item* i1 = this;
463         const Item* i2 = &other;
464
465         /* move towards root until we are at the same level
466            for both items
467         */
468
469         while (d1 != d2) {
470                 if (d1 > d2) {
471                         if (!i1) {
472                                 return false;
473                         }
474                         i1 = i1->parent();
475                         d1--;
476                         limit--;
477                 } else {
478                         if (!i2) {
479                                 return false;
480                         }
481                         i2 = i2->parent();
482                         d2--;
483                         limit--;
484                 }
485                 if (limit == 0) {
486                         return false;
487                 }
488         }
489
490         /* now see if there is a common parent */
491
492         while (i1 != i2) {
493                 if (i1) {
494                         i1 = i1->parent();
495                 }
496                 if (i2) {
497                         i2 = i2->parent ();
498                 }
499
500                 limit--;
501                 if (limit == 0) {
502                         return false;
503                 }
504         }
505
506         return true;
507 }
508
509 const Item*
510 Item::closest_ancestor_with (const Item& other) const
511 {
512         uint32_t d1 = depth();
513         uint32_t d2 = other.depth();
514         const Item* i1 = this;
515         const Item* i2 = &other;
516
517         /* move towards root until we are at the same level
518            for both items
519         */
520
521         while (d1 != d2) {
522                 if (d1 > d2) {
523                         if (!i1) {
524                                 return 0;
525                         }
526                         i1 = i1->parent();
527                         d1--;
528                 } else {
529                         if (!i2) {
530                                 return 0;
531                         }
532                         i2 = i2->parent();
533                         d2--;
534                 }
535         }
536
537         /* now see if there is a common parent */
538
539         while (i1 != i2) {
540                 if (i1) {
541                         i1 = i1->parent();
542                 }
543                 if (i2) {
544                         i2 = i2->parent ();
545                 }
546         }
547
548         return i1;
549 }
550
551 bool
552 Item::is_descendant_of (const Item& candidate) const
553 {
554         Item const * i = _parent;
555
556         while (i) {
557                 if (i == &candidate) {
558                         return true;
559                 }
560                 i = i->parent();
561         }
562
563         return false;
564 }
565
566 void
567 Item::grab_focus ()
568 {
569         /* XXX */
570 }
571
572 /** @return Bounding box in this item's coordinates */
573 boost::optional<ArdourCanvas::Rect>
574 Item::bounding_box () const
575 {
576         if (_bounding_box_dirty) {
577                 compute_bounding_box ();
578                 assert (!_bounding_box_dirty);
579                 add_child_bounding_boxes ();
580         }
581
582         return _bounding_box;
583 }
584
585 Coord
586 Item::height () const
587 {
588         boost::optional<ArdourCanvas::Rect> bb  = bounding_box();
589
590         if (bb) {
591                 return bb->height ();
592         }
593         return 0;
594 }
595
596 Coord
597 Item::width () const
598 {
599         boost::optional<ArdourCanvas::Rect> bb = bounding_box();
600
601         if (bb) {
602                 return bb->width ();
603         }
604
605         return 0;
606 }
607
608 void
609 Item::redraw () const
610 {
611         if (visible() && _bounding_box && _canvas) {
612                 _canvas->request_redraw (item_to_window (_bounding_box.get()));
613         }
614 }
615
616 void
617 Item::begin_change ()
618 {
619         _pre_change_bounding_box = bounding_box ();
620 }
621
622 void
623 Item::end_change ()
624 {
625         if (visible()) {
626                 _canvas->item_changed (this, _pre_change_bounding_box);
627
628                 if (_parent) {
629                         _parent->child_changed ();
630                 }
631         }
632 }
633
634 void
635 Item::begin_visual_change ()
636 {
637 }
638
639 void
640 Item::end_visual_change ()
641 {
642         if (visible()) {
643                 _canvas->item_visual_property_changed (this);
644         }
645 }
646
647 void
648 Item::move (Duple movement)
649 {
650         set_position (position() + movement);
651 }
652
653 void
654 Item::grab ()
655 {
656         assert (_canvas);
657         _canvas->grab (this);
658 }
659
660 void
661 Item::ungrab ()
662 {
663         assert (_canvas);
664         _canvas->ungrab ();
665 }
666
667 void
668 Item::set_data (string const & key, void* data)
669 {
670         _data[key] = data;
671 }
672
673 void *
674 Item::get_data (string const & key) const
675 {
676         map<string, void*>::const_iterator i = _data.find (key);
677         if (i == _data.end ()) {
678                 return 0;
679         }
680
681         return i->second;
682 }
683
684 void
685 Item::set_ignore_events (bool ignore)
686 {
687         _ignore_events = ignore;
688 }
689
690 std::string
691 Item::whatami () const
692 {
693         std::string type = demangle (typeid (*this).name());
694         return type.substr (type.find_last_of (':') + 1);
695 }
696
697 uint32_t
698 Item::depth () const
699 {
700         Item* i = _parent;
701         int d = 0;
702         while (i) {
703                 ++d;
704                 i = i->parent();
705         }
706         return d;
707 }
708
709 bool
710 Item::covers (Duple const & point) const
711 {
712         Duple p = window_to_item (point);
713
714         if (_bounding_box_dirty) {
715                 compute_bounding_box ();
716         }
717
718         boost::optional<Rect> r = bounding_box();
719
720         if (!r) {
721                 return false;
722         }
723
724         return r.get().contains (p);
725 }
726
727 /* nesting/grouping API */
728
729 void
730 Item::render_children (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
731 {
732         if (_items.empty()) {
733                 return;
734         }
735
736         ensure_lut ();
737         std::vector<Item*> items = _lut->get (area);
738
739 #ifdef CANVAS_DEBUG
740         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
741                 cerr << string_compose ("%1%7 %2 @ %7 render %5 @ %6 %3 items out of %4\n",
742                                         _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area, _position, this,
743                                         whatami());
744         }
745 #endif
746
747         ++render_depth;
748
749         for (std::vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
750
751                 if (!(*i)->visible ()) {
752 #ifdef CANVAS_DEBUG
753                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
754                                 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
755                         }
756 #endif
757                         continue;
758                 }
759
760                 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
761
762                 if (!item_bbox) {
763 #ifdef CANVAS_DEBUG
764                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
765                                 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
766                         }
767 #endif
768                         continue;
769                 }
770
771                 Rect item = (*i)->item_to_window (item_bbox.get(), false);
772                 boost::optional<Rect> d = item.intersection (area);
773
774                 if (d) {
775                         Rect draw = d.get();
776                         if (draw.width() && draw.height()) {
777 #ifdef CANVAS_DEBUG
778                                 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
779                                         if (dynamic_cast<Container*>(*i) == 0) {
780                                                 cerr << _canvas->render_indent() << "render "
781                                                      << ' '
782                                                      << (*i)
783                                                      << ' '
784                                                      << (*i)->whatami()
785                                                      << ' '
786                                                      << (*i)->name
787                                                      << " item "
788                                                      << item_bbox.get()
789                                                      << " window = "
790                                                      << item
791                                                      << " intersect = "
792                                                      << draw
793                                                      << " @ "
794                                                      << _position
795                                                      << endl;
796                                         }
797                                 }
798 #endif
799
800                                 (*i)->render (area, context);
801                                 ++render_count;
802                         }
803
804                 } else {
805
806 #ifdef CANVAS_DEBUG
807                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
808                                 cerr << string_compose ("%1skip render of %2 %3, no intersection between %4 and %5\n", _canvas->render_indent(), (*i)->whatami(),
809                                                         (*i)->name, item, area);
810                         }
811 #endif
812
813                 }
814         }
815
816         --render_depth;
817 }
818
819 void
820 Item::add_child_bounding_boxes() const
821 {
822         boost::optional<Rect> self;
823         Rect bbox;
824         bool have_one = false;
825
826         if (_bounding_box) {
827                 bbox = _bounding_box.get();
828                 have_one = true;
829         }
830
831         for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
832
833                 if (!(*i)->visible()) {
834                         continue;
835                 }
836
837                 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
838
839                 if (!item_bbox) {
840                         continue;
841                 }
842
843                 Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
844                 if (have_one) {
845                         bbox = bbox.extend (group_bbox);
846                 } else {
847                         bbox = group_bbox;
848                         have_one = true;
849                 }
850         }
851
852         if (!have_one) {
853                 _bounding_box = boost::optional<Rect> ();
854         } else {
855                 _bounding_box = bbox;
856         }
857 }
858
859 void
860 Item::add (Item* i)
861 {
862         /* XXX should really notify canvas about this */
863
864         _items.push_back (i);
865         i->reparent (this);
866         invalidate_lut ();
867         _bounding_box_dirty = true;
868 }
869
870 void
871 Item::remove (Item* i)
872 {
873
874         if (i->parent() != this) {
875                 return;
876         }
877
878         /* we cannot call bounding_box() here because that will iterate over
879            _items, one of which (the argument, i) may be in the middle of
880            deletion, making it impossible to call compute_bounding_box()
881            on it.
882         */
883
884         if (_bounding_box) {
885                 _pre_change_bounding_box = _bounding_box;
886         } else {
887                 _pre_change_bounding_box = Rect();
888         }
889
890         i->unparent ();
891         _items.remove (i);
892         invalidate_lut ();
893         _bounding_box_dirty = true;
894
895         end_change ();
896 }
897
898 void
899 Item::clear (bool with_delete)
900 {
901         begin_change ();
902
903         clear_items (with_delete);
904
905         invalidate_lut ();
906         _bounding_box_dirty = true;
907
908         end_change ();
909 }
910
911 void
912 Item::clear_items (bool with_delete)
913 {
914         for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
915
916                 list<Item*>::iterator tmp = i;
917                 Item *item = *i;
918
919                 ++tmp;
920
921                 /* remove from list before doing anything else, because we
922                  * don't want to find the item in _items during any activity
923                  * driven by unparent-ing or deletion.
924                  */
925
926                 _items.erase (i);
927                 item->unparent ();
928
929                 if (with_delete) {
930                         delete item;
931                 }
932
933                 i = tmp;
934         }
935 }
936
937 void
938 Item::raise_child_to_top (Item* i)
939 {
940         if (!_items.empty()) {
941                 if (_items.back() == i) {
942                         return;
943                 }
944         }
945
946         _items.remove (i);
947         _items.push_back (i);
948
949         invalidate_lut ();
950         redraw ();
951 }
952
953 void
954 Item::raise_child (Item* i, int levels)
955 {
956         list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
957         assert (j != _items.end ());
958
959         ++j;
960         _items.remove (i);
961
962         while (levels > 0 && j != _items.end ()) {
963                 ++j;
964                 --levels;
965         }
966
967         _items.insert (j, i);
968         invalidate_lut ();
969         redraw ();
970 }
971
972 void
973 Item::lower_child_to_bottom (Item* i)
974 {
975         if (!_items.empty()) {
976                 if (_items.front() == i) {
977                         return;
978                 }
979         }
980         _items.remove (i);
981         _items.push_front (i);
982         invalidate_lut ();
983         redraw ();
984 }
985
986 void
987 Item::ensure_lut () const
988 {
989         if (!_lut) {
990                 _lut = new DumbLookupTable (*this);
991         }
992 }
993
994 void
995 Item::invalidate_lut () const
996 {
997         delete _lut;
998         _lut = 0;
999 }
1000
1001 void
1002 Item::child_changed ()
1003 {
1004         invalidate_lut ();
1005         _bounding_box_dirty = true;
1006
1007         if (_parent) {
1008                 _parent->child_changed ();
1009         }
1010 }
1011
1012 void
1013 Item::add_items_at_point (Duple const point, vector<Item const *>& items) const
1014 {
1015         boost::optional<Rect> const bbox = bounding_box ();
1016
1017         /* Point is in window coordinate system */
1018
1019         if (!bbox || !item_to_window (bbox.get()).contains (point)) {
1020                 return;
1021         }
1022
1023         /* recurse and add any items within our group that contain point.
1024            Our children are only considered visible if we are, and similarly
1025            only if we do not ignore events.
1026         */
1027
1028         vector<Item*> our_items;
1029
1030         if (!_items.empty() && visible() && !_ignore_events) {
1031                 ensure_lut ();
1032                 our_items = _lut->items_at_point (point);
1033         }
1034
1035         if (!our_items.empty() || covers (point)) {
1036                 /* this adds this item itself to the list of items at point */
1037                 items.push_back (this);
1038         }
1039
1040         for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
1041                 (*i)->add_items_at_point (point, items);
1042         }
1043 }
1044
1045 void
1046 Item::set_tooltip (const std::string& s)
1047 {
1048         _tooltip = s;
1049 }
1050
1051 void
1052 Item::start_tooltip_timeout ()
1053 {
1054         if (!_tooltip.empty()) {
1055                 _canvas->start_tooltip_timeout (this);
1056         }
1057 }
1058
1059 void
1060 Item::stop_tooltip_timeout ()
1061 {
1062         _canvas->stop_tooltip_timeout ();
1063 }
1064
1065 void
1066 Item::dump (ostream& o) const
1067 {
1068         boost::optional<ArdourCanvas::Rect> bb = bounding_box();
1069
1070         o << _canvas->indent() << whatami() << ' ' << this << " self-Visible ? " << self_visible() << " visible ? " << visible();
1071         o << " @ " << position();
1072
1073 #ifdef CANVAS_DEBUG
1074         if (!name.empty()) {
1075                 o << ' ' << name;
1076         }
1077 #endif
1078
1079         if (bb) {
1080                 o << endl << _canvas->indent() << "\tbbox: " << bb.get();
1081                 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
1082         } else {
1083                 o << " bbox unset";
1084         }
1085
1086         o << endl;
1087
1088         if (!_items.empty()) {
1089
1090 #ifdef CANVAS_DEBUG
1091                 o << _canvas->indent();
1092                 o << " @ " << position();
1093                 o << " Items: " << _items.size();
1094                 o << " Self-Visible ? " << self_visible();
1095                 o << " Visible ? " << visible();
1096
1097                 boost::optional<Rect> bb = bounding_box();
1098
1099                 if (bb) {
1100                         o << endl << _canvas->indent() << "  bbox: " << bb.get();
1101                         o << endl << _canvas->indent() << "  CANVAS bbox: " << item_to_canvas (bb.get());
1102                 } else {
1103                         o << "  bbox unset";
1104                 }
1105
1106                 o << endl;
1107 #endif
1108
1109                 ArdourCanvas::dump_depth++;
1110
1111                 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
1112                         o << **i;
1113                 }
1114
1115                 ArdourCanvas::dump_depth--;
1116         }
1117 }
1118
1119 ostream&
1120 ArdourCanvas::operator<< (ostream& o, const Item& i)
1121 {
1122         i.dump (o);
1123         return o;
1124 }
1125