Remove LocaleGuards from SVAModifier and HSV classes
[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         ArdourCanvas::Rect bbox = bounding_box ();
263         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);
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, bool already_added)
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         if (!already_added) {
433                 _parent->add (this);
434         }
435 }
436
437 void
438 Item::find_scroll_parent ()
439 {
440         Item const * i = this;
441         ScrollGroup const * last_scroll_group = 0;
442
443         /* Don't allow a scroll group to find itself as its own scroll parent
444          */
445
446         i = i->parent ();
447
448         while (i) {
449                 ScrollGroup const * sg = dynamic_cast<ScrollGroup const *> (i);
450                 if (sg) {
451                         last_scroll_group = sg;
452                 }
453                 i = i->parent();
454         }
455
456         _scroll_parent = const_cast<ScrollGroup*> (last_scroll_group);
457 }
458
459 bool
460 Item::common_ancestor_within (uint32_t limit, const Item& other) const
461 {
462         uint32_t d1 = depth();
463         uint32_t d2 = other.depth();
464         const Item* i1 = this;
465         const Item* i2 = &other;
466
467         /* move towards root until we are at the same level
468            for both items
469         */
470
471         while (d1 != d2) {
472                 if (d1 > d2) {
473                         if (!i1) {
474                                 return false;
475                         }
476                         i1 = i1->parent();
477                         d1--;
478                         limit--;
479                 } else {
480                         if (!i2) {
481                                 return false;
482                         }
483                         i2 = i2->parent();
484                         d2--;
485                         limit--;
486                 }
487                 if (limit == 0) {
488                         return false;
489                 }
490         }
491
492         /* now see if there is a common parent */
493
494         while (i1 != i2) {
495                 if (i1) {
496                         i1 = i1->parent();
497                 }
498                 if (i2) {
499                         i2 = i2->parent ();
500                 }
501
502                 limit--;
503                 if (limit == 0) {
504                         return false;
505                 }
506         }
507
508         return true;
509 }
510
511 const Item*
512 Item::closest_ancestor_with (const Item& other) const
513 {
514         uint32_t d1 = depth();
515         uint32_t d2 = other.depth();
516         const Item* i1 = this;
517         const Item* i2 = &other;
518
519         /* move towards root until we are at the same level
520            for both items
521         */
522
523         while (d1 != d2) {
524                 if (d1 > d2) {
525                         if (!i1) {
526                                 return 0;
527                         }
528                         i1 = i1->parent();
529                         d1--;
530                 } else {
531                         if (!i2) {
532                                 return 0;
533                         }
534                         i2 = i2->parent();
535                         d2--;
536                 }
537         }
538
539         /* now see if there is a common parent */
540
541         while (i1 != i2) {
542                 if (i1) {
543                         i1 = i1->parent();
544                 }
545                 if (i2) {
546                         i2 = i2->parent ();
547                 }
548         }
549
550         return i1;
551 }
552
553 bool
554 Item::is_descendant_of (const Item& candidate) const
555 {
556         Item const * i = _parent;
557
558         while (i) {
559                 if (i == &candidate) {
560                         return true;
561                 }
562                 i = i->parent();
563         }
564
565         return false;
566 }
567
568 void
569 Item::grab_focus ()
570 {
571         /* XXX */
572 }
573
574 void
575 Item::size_allocate (Rect const & r)
576 {
577         _allocation = r;
578 }
579
580 /** @return Bounding box in this item's coordinates */
581 ArdourCanvas::Rect
582 Item::bounding_box (bool for_own_purposes) const
583 {
584         if (_bounding_box_dirty) {
585                 compute_bounding_box ();
586                 assert (!_bounding_box_dirty);
587                 add_child_bounding_boxes ();
588         }
589
590         if (!for_own_purposes) {
591                 if (_allocation) {
592                         return _allocation;
593                 }
594         }
595
596         return _bounding_box;
597 }
598
599 Coord
600 Item::height () const
601 {
602         ArdourCanvas::Rect bb  = bounding_box();
603
604         if (bb) {
605                 return bb.height ();
606         }
607         return 0;
608 }
609
610 Coord
611 Item::width () const
612 {
613         ArdourCanvas::Rect bb = bounding_box();
614
615         if (bb) {
616                 return bb.width ();
617         }
618
619         return 0;
620 }
621
622 void
623 Item::redraw () const
624 {
625         if (visible() && _bounding_box && _canvas) {
626                 _canvas->request_redraw (item_to_window (_bounding_box));
627         }
628 }
629
630 void
631 Item::begin_change ()
632 {
633         _pre_change_bounding_box = bounding_box ();
634 }
635
636 void
637 Item::end_change ()
638 {
639         if (visible()) {
640                 _canvas->item_changed (this, _pre_change_bounding_box);
641
642                 if (_parent) {
643                         _parent->child_changed ();
644                 }
645         }
646 }
647
648 void
649 Item::begin_visual_change ()
650 {
651 }
652
653 void
654 Item::end_visual_change ()
655 {
656         if (visible()) {
657                 _canvas->item_visual_property_changed (this);
658         }
659 }
660
661 void
662 Item::move (Duple movement)
663 {
664         set_position (position() + movement);
665 }
666
667 void
668 Item::grab ()
669 {
670         assert (_canvas);
671         _canvas->grab (this);
672 }
673
674 void
675 Item::ungrab ()
676 {
677         assert (_canvas);
678         _canvas->ungrab ();
679 }
680
681 void
682 Item::set_data (string const & key, void* data)
683 {
684         _data[key] = data;
685 }
686
687 void *
688 Item::get_data (string const & key) const
689 {
690         map<string, void*>::const_iterator i = _data.find (key);
691         if (i == _data.end ()) {
692                 return 0;
693         }
694
695         return i->second;
696 }
697
698 void
699 Item::set_ignore_events (bool ignore)
700 {
701         _ignore_events = ignore;
702 }
703
704 std::string
705 Item::whatami () const
706 {
707         std::string type = demangle (typeid (*this).name());
708         return type.substr (type.find_last_of (':') + 1);
709 }
710
711 uint32_t
712 Item::depth () const
713 {
714         Item* i = _parent;
715         int d = 0;
716         while (i) {
717                 ++d;
718                 i = i->parent();
719         }
720         return d;
721 }
722
723 bool
724 Item::covers (Duple const & point) const
725 {
726         Duple p = window_to_item (point);
727
728         if (_bounding_box_dirty) {
729                 compute_bounding_box ();
730         }
731
732         Rect r = bounding_box();
733
734         if (!r) {
735                 return false;
736         }
737
738         return r.contains (p);
739 }
740
741 /* nesting/grouping API */
742
743 void
744 Item::render_children (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
745 {
746         if (_items.empty()) {
747                 return;
748         }
749
750         ensure_lut ();
751         std::vector<Item*> items = _lut->get (area);
752
753 #ifdef CANVAS_DEBUG
754         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
755                 cerr << string_compose ("%1%7 %2 @ %7 render %5 @ %6 %3 items out of %4\n",
756                                         _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area, _position, this,
757                                         whatami());
758         }
759 #endif
760
761         ++render_depth;
762
763         for (std::vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
764
765                 if (!(*i)->visible ()) {
766 #ifdef CANVAS_DEBUG
767                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
768                                 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
769                         }
770 #endif
771                         continue;
772                 }
773
774                 Rect item_bbox = (*i)->bounding_box ();
775
776                 if (!item_bbox) {
777 #ifdef CANVAS_DEBUG
778                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
779                                 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
780                         }
781 #endif
782                         continue;
783                 }
784
785                 Rect item = (*i)->item_to_window (item_bbox, false);
786                 Rect d = item.intersection (area);
787
788                 if (d) {
789                         Rect draw = d;
790                         if (draw.width() && draw.height()) {
791 #ifdef CANVAS_DEBUG
792                                 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
793                                         if (dynamic_cast<Container*>(*i) == 0) {
794                                                 cerr << _canvas->render_indent() << "render "
795                                                      << ' '
796                                                      << (*i)
797                                                      << ' '
798                                                      << (*i)->whatami()
799                                                      << ' '
800                                                      << (*i)->name
801                                                      << " item "
802                                                      << item_bbox
803                                                      << " window = "
804                                                      << item
805                                                      << " intersect = "
806                                                      << draw
807                                                      << " @ "
808                                                      << _position
809                                                      << endl;
810                                         }
811                                 }
812 #endif
813
814                                 (*i)->render (area, context);
815                                 ++render_count;
816                         }
817
818                 } else {
819
820 #ifdef CANVAS_DEBUG
821                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
822                                 cerr << string_compose ("%1skip render of %2 %3, no intersection between %4 and %5\n", _canvas->render_indent(), (*i)->whatami(),
823                                                         (*i)->name, item, area);
824                         }
825 #endif
826
827                 }
828         }
829
830         --render_depth;
831 }
832
833 void
834 Item::add_child_bounding_boxes (bool include_hidden) const
835 {
836         Rect self;
837         Rect bbox;
838         bool have_one = false;
839
840         if (_bounding_box) {
841                 bbox = _bounding_box;
842                 have_one = true;
843         }
844
845         for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
846
847                 if (!(*i)->visible() && !include_hidden) {
848                         continue;
849                 }
850
851                 Rect item_bbox = (*i)->bounding_box ();
852
853                 if (!item_bbox) {
854                         continue;
855                 }
856
857                 Rect group_bbox = (*i)->item_to_parent (item_bbox);
858                 if (have_one) {
859                         bbox = bbox.extend (group_bbox);
860                 } else {
861                         bbox = group_bbox;
862                         have_one = true;
863                 }
864         }
865
866         if (!have_one) {
867                 _bounding_box = Rect ();
868         } else {
869                 _bounding_box = bbox;
870         }
871 }
872
873 void
874 Item::add (Item* i)
875 {
876         /* XXX should really notify canvas about this */
877
878         _items.push_back (i);
879         i->reparent (this, true);
880         invalidate_lut ();
881         _bounding_box_dirty = true;
882 }
883
884 void
885 Item::add_front (Item* i)
886 {
887         /* XXX should really notify canvas about this */
888
889         _items.push_front (i);
890         i->reparent (this, true);
891         invalidate_lut ();
892         _bounding_box_dirty = true;
893 }
894
895 void
896 Item::remove (Item* i)
897 {
898
899         if (i->parent() != this) {
900                 return;
901         }
902
903         /* we cannot call bounding_box() here because that will iterate over
904            _items, one of which (the argument, i) may be in the middle of
905            deletion, making it impossible to call compute_bounding_box()
906            on it.
907         */
908
909         if (_bounding_box) {
910                 _pre_change_bounding_box = _bounding_box;
911         } else {
912                 _pre_change_bounding_box = Rect();
913         }
914
915         i->unparent ();
916         _items.remove (i);
917         invalidate_lut ();
918         _bounding_box_dirty = true;
919
920         end_change ();
921 }
922
923 void
924 Item::clear (bool with_delete)
925 {
926         begin_change ();
927
928         clear_items (with_delete);
929
930         invalidate_lut ();
931         _bounding_box_dirty = true;
932
933         end_change ();
934 }
935
936 void
937 Item::clear_items (bool with_delete)
938 {
939         for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
940
941                 list<Item*>::iterator tmp = i;
942                 Item *item = *i;
943
944                 ++tmp;
945
946                 /* remove from list before doing anything else, because we
947                  * don't want to find the item in _items during any activity
948                  * driven by unparent-ing or deletion.
949                  */
950
951                 _items.erase (i);
952                 item->unparent ();
953
954                 if (with_delete) {
955                         delete item;
956                 }
957
958                 i = tmp;
959         }
960 }
961
962 void
963 Item::raise_child_to_top (Item* i)
964 {
965         if (!_items.empty()) {
966                 if (_items.back() == i) {
967                         return;
968                 }
969         }
970
971         _items.remove (i);
972         _items.push_back (i);
973
974         invalidate_lut ();
975         redraw ();
976 }
977
978 void
979 Item::raise_child (Item* i, int levels)
980 {
981         list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
982         assert (j != _items.end ());
983
984         ++j;
985         _items.remove (i);
986
987         while (levels > 0 && j != _items.end ()) {
988                 ++j;
989                 --levels;
990         }
991
992         _items.insert (j, i);
993         invalidate_lut ();
994         redraw ();
995 }
996
997 void
998 Item::lower_child_to_bottom (Item* i)
999 {
1000         if (!_items.empty()) {
1001                 if (_items.front() == i) {
1002                         return;
1003                 }
1004         }
1005         _items.remove (i);
1006         _items.push_front (i);
1007         invalidate_lut ();
1008         redraw ();
1009 }
1010
1011 void
1012 Item::ensure_lut () const
1013 {
1014         if (!_lut) {
1015                 _lut = new DumbLookupTable (*this);
1016         }
1017 }
1018
1019 void
1020 Item::invalidate_lut () const
1021 {
1022         delete _lut;
1023         _lut = 0;
1024 }
1025
1026 void
1027 Item::child_changed ()
1028 {
1029         invalidate_lut ();
1030         _bounding_box_dirty = true;
1031
1032         if (_parent) {
1033                 _parent->child_changed ();
1034         }
1035 }
1036
1037 void
1038 Item::add_items_at_point (Duple const point, vector<Item const *>& items) const
1039 {
1040         Rect const bbox = bounding_box ();
1041
1042         /* Point is in window coordinate system */
1043
1044         if (!bbox || !item_to_window (bbox).contains (point)) {
1045                 return;
1046         }
1047
1048         /* recurse and add any items within our group that contain point.
1049            Our children are only considered visible if we are, and similarly
1050            only if we do not ignore events.
1051         */
1052
1053         vector<Item*> our_items;
1054
1055         if (!_items.empty() && visible() && !_ignore_events) {
1056                 ensure_lut ();
1057                 our_items = _lut->items_at_point (point);
1058         }
1059
1060         if (!our_items.empty() || covers (point)) {
1061                 /* this adds this item itself to the list of items at point */
1062                 items.push_back (this);
1063         }
1064
1065         for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
1066                 (*i)->add_items_at_point (point, items);
1067         }
1068 }
1069
1070 void
1071 Item::set_tooltip (const std::string& s)
1072 {
1073         _tooltip = s;
1074 }
1075
1076 void
1077 Item::start_tooltip_timeout ()
1078 {
1079         if (!_tooltip.empty()) {
1080                 _canvas->start_tooltip_timeout (this);
1081         }
1082 }
1083
1084 void
1085 Item::stop_tooltip_timeout ()
1086 {
1087         _canvas->stop_tooltip_timeout ();
1088 }
1089
1090 void
1091 Item::dump (ostream& o) const
1092 {
1093         ArdourCanvas::Rect bb = bounding_box();
1094
1095         o << _canvas->indent() << whatami() << ' ' << this << " self-Visible ? " << self_visible() << " visible ? " << visible();
1096         o << " @ " << position();
1097
1098 #ifdef CANVAS_DEBUG
1099         if (!name.empty()) {
1100                 o << ' ' << name;
1101         }
1102 #endif
1103
1104         if (bb) {
1105                 o << endl << _canvas->indent() << "\tbbox: " << bb;
1106                 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb);
1107         } else {
1108                 o << " bbox unset";
1109         }
1110
1111         o << endl;
1112
1113         if (!_items.empty()) {
1114
1115 #ifdef CANVAS_DEBUG
1116                 o << _canvas->indent();
1117                 o << " @ " << position();
1118                 o << " Items: " << _items.size();
1119                 o << " Self-Visible ? " << self_visible();
1120                 o << " Visible ? " << visible();
1121
1122                 Rect bb = bounding_box();
1123
1124                 if (bb) {
1125                         o << endl << _canvas->indent() << "  bbox: " << bb;
1126                         o << endl << _canvas->indent() << "  CANVAS bbox: " << item_to_canvas (bb);
1127                 } else {
1128                         o << "  bbox unset";
1129                 }
1130
1131                 o << endl;
1132 #endif
1133
1134                 ArdourCanvas::dump_depth++;
1135
1136                 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
1137                         o << **i;
1138                 }
1139
1140                 ArdourCanvas::dump_depth--;
1141         }
1142 }
1143
1144 ostream&
1145 ArdourCanvas::operator<< (ostream& o, const Item& i)
1146 {
1147         i.dump (o);
1148         return o;
1149 }
1150