add Item::add_front()
[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::add_front (Item* i)
872 {
873         /* XXX should really notify canvas about this */
874
875         _items.push_front (i);
876         i->reparent (this);
877         invalidate_lut ();
878         _bounding_box_dirty = true;
879 }
880
881 void
882 Item::remove (Item* i)
883 {
884
885         if (i->parent() != this) {
886                 return;
887         }
888
889         /* we cannot call bounding_box() here because that will iterate over
890            _items, one of which (the argument, i) may be in the middle of
891            deletion, making it impossible to call compute_bounding_box()
892            on it.
893         */
894
895         if (_bounding_box) {
896                 _pre_change_bounding_box = _bounding_box;
897         } else {
898                 _pre_change_bounding_box = Rect();
899         }
900
901         i->unparent ();
902         _items.remove (i);
903         invalidate_lut ();
904         _bounding_box_dirty = true;
905
906         end_change ();
907 }
908
909 void
910 Item::clear (bool with_delete)
911 {
912         begin_change ();
913
914         clear_items (with_delete);
915
916         invalidate_lut ();
917         _bounding_box_dirty = true;
918
919         end_change ();
920 }
921
922 void
923 Item::clear_items (bool with_delete)
924 {
925         for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
926
927                 list<Item*>::iterator tmp = i;
928                 Item *item = *i;
929
930                 ++tmp;
931
932                 /* remove from list before doing anything else, because we
933                  * don't want to find the item in _items during any activity
934                  * driven by unparent-ing or deletion.
935                  */
936
937                 _items.erase (i);
938                 item->unparent ();
939
940                 if (with_delete) {
941                         delete item;
942                 }
943
944                 i = tmp;
945         }
946 }
947
948 void
949 Item::raise_child_to_top (Item* i)
950 {
951         if (!_items.empty()) {
952                 if (_items.back() == i) {
953                         return;
954                 }
955         }
956
957         _items.remove (i);
958         _items.push_back (i);
959
960         invalidate_lut ();
961         redraw ();
962 }
963
964 void
965 Item::raise_child (Item* i, int levels)
966 {
967         list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
968         assert (j != _items.end ());
969
970         ++j;
971         _items.remove (i);
972
973         while (levels > 0 && j != _items.end ()) {
974                 ++j;
975                 --levels;
976         }
977
978         _items.insert (j, i);
979         invalidate_lut ();
980         redraw ();
981 }
982
983 void
984 Item::lower_child_to_bottom (Item* i)
985 {
986         if (!_items.empty()) {
987                 if (_items.front() == i) {
988                         return;
989                 }
990         }
991         _items.remove (i);
992         _items.push_front (i);
993         invalidate_lut ();
994         redraw ();
995 }
996
997 void
998 Item::ensure_lut () const
999 {
1000         if (!_lut) {
1001                 _lut = new DumbLookupTable (*this);
1002         }
1003 }
1004
1005 void
1006 Item::invalidate_lut () const
1007 {
1008         delete _lut;
1009         _lut = 0;
1010 }
1011
1012 void
1013 Item::child_changed ()
1014 {
1015         invalidate_lut ();
1016         _bounding_box_dirty = true;
1017
1018         if (_parent) {
1019                 _parent->child_changed ();
1020         }
1021 }
1022
1023 void
1024 Item::add_items_at_point (Duple const point, vector<Item const *>& items) const
1025 {
1026         boost::optional<Rect> const bbox = bounding_box ();
1027
1028         /* Point is in window coordinate system */
1029
1030         if (!bbox || !item_to_window (bbox.get()).contains (point)) {
1031                 return;
1032         }
1033
1034         /* recurse and add any items within our group that contain point.
1035            Our children are only considered visible if we are, and similarly
1036            only if we do not ignore events.
1037         */
1038
1039         vector<Item*> our_items;
1040
1041         if (!_items.empty() && visible() && !_ignore_events) {
1042                 ensure_lut ();
1043                 our_items = _lut->items_at_point (point);
1044         }
1045
1046         if (!our_items.empty() || covers (point)) {
1047                 /* this adds this item itself to the list of items at point */
1048                 items.push_back (this);
1049         }
1050
1051         for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
1052                 (*i)->add_items_at_point (point, items);
1053         }
1054 }
1055
1056 void
1057 Item::set_tooltip (const std::string& s)
1058 {
1059         _tooltip = s;
1060 }
1061
1062 void
1063 Item::start_tooltip_timeout ()
1064 {
1065         if (!_tooltip.empty()) {
1066                 _canvas->start_tooltip_timeout (this);
1067         }
1068 }
1069
1070 void
1071 Item::stop_tooltip_timeout ()
1072 {
1073         _canvas->stop_tooltip_timeout ();
1074 }
1075
1076 void
1077 Item::dump (ostream& o) const
1078 {
1079         boost::optional<ArdourCanvas::Rect> bb = bounding_box();
1080
1081         o << _canvas->indent() << whatami() << ' ' << this << " self-Visible ? " << self_visible() << " visible ? " << visible();
1082         o << " @ " << position();
1083
1084 #ifdef CANVAS_DEBUG
1085         if (!name.empty()) {
1086                 o << ' ' << name;
1087         }
1088 #endif
1089
1090         if (bb) {
1091                 o << endl << _canvas->indent() << "\tbbox: " << bb.get();
1092                 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
1093         } else {
1094                 o << " bbox unset";
1095         }
1096
1097         o << endl;
1098
1099         if (!_items.empty()) {
1100
1101 #ifdef CANVAS_DEBUG
1102                 o << _canvas->indent();
1103                 o << " @ " << position();
1104                 o << " Items: " << _items.size();
1105                 o << " Self-Visible ? " << self_visible();
1106                 o << " Visible ? " << visible();
1107
1108                 boost::optional<Rect> bb = bounding_box();
1109
1110                 if (bb) {
1111                         o << endl << _canvas->indent() << "  bbox: " << bb.get();
1112                         o << endl << _canvas->indent() << "  CANVAS bbox: " << item_to_canvas (bb.get());
1113                 } else {
1114                         o << "  bbox unset";
1115                 }
1116
1117                 o << endl;
1118 #endif
1119
1120                 ArdourCanvas::dump_depth++;
1121
1122                 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
1123                         o << **i;
1124                 }
1125
1126                 ArdourCanvas::dump_depth--;
1127         }
1128 }
1129
1130 ostream&
1131 ArdourCanvas::operator<< (ostream& o, const Item& i)
1132 {
1133         i.dump (o);
1134         return o;
1135 }
1136