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