Only show user-presets in favorite sidebar
[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 "canvas/canvas.h"
25 #include "canvas/debug.h"
26 #include "canvas/item.h"
27 #include "canvas/scroll_group.h"
28
29 using namespace std;
30 using namespace PBD;
31 using namespace ArdourCanvas;
32
33 int Item::default_items_per_cell = 64;
34
35 Item::Item (Canvas* canvas)
36         : Fill (*this)
37         , Outline (*this)
38         ,  _canvas (canvas)
39         , _parent (0)
40         , _scroll_parent (0)
41         , _visible (true)
42         , _bounding_box_dirty (true)
43         , _lut (0)
44         , _ignore_events (false)
45 {
46         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
47 }
48
49 Item::Item (Item* parent)
50         : Fill (*this)
51         , Outline (*this)
52         ,  _canvas (parent->canvas())
53         , _parent (parent)
54         , _scroll_parent (0)
55         , _visible (true)
56         , _bounding_box_dirty (true)
57         , _lut (0)
58         , _ignore_events (false)
59 {
60         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
61
62         if (parent) {
63                 _parent->add (this);
64         }
65
66         find_scroll_parent ();
67 }
68
69 Item::Item (Item* parent, Duple const& p)
70         : Fill (*this)
71         , Outline (*this)
72         ,  _canvas (parent->canvas())
73         , _parent (parent)
74         , _scroll_parent (0)
75         , _position (p)
76         , _visible (true)
77         , _bounding_box_dirty (true)
78         , _lut (0)
79         , _ignore_events (false)
80 {
81         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
82
83         if (parent) {
84                 _parent->add (this);
85         }
86
87         find_scroll_parent ();
88
89 }
90
91 Item::~Item ()
92 {
93         if (_parent) {
94                 _parent->remove (this);
95         }
96
97         if (_canvas) {
98                 _canvas->item_going_away (this, _bounding_box);
99         }
100
101         clear_items (true);
102         delete _lut;
103 }
104
105 bool
106 Item::visible() const
107 {
108         Item const * i = this;
109
110         while (i) {
111                 if (!i->self_visible()) {
112                         return false;
113                 }
114                 i = i->parent();
115         }
116
117         return true;
118 }
119
120 Duple
121 Item::canvas_origin () const
122 {
123         return item_to_canvas (Duple (0,0));
124 }
125
126 Duple
127 Item::window_origin () const
128 {
129         /* This is slightly subtle. Our _position is in the coordinate space of
130            our parent. So to find out where that is in window coordinates, we
131            have to ask our parent.
132         */
133         if (_parent) {
134                 return _parent->item_to_window (_position);
135         } else {
136                 return _position;
137         }
138 }
139
140 ArdourCanvas::Rect
141 Item::item_to_parent (ArdourCanvas::Rect const & r) const
142 {
143         return r.translate (_position);
144 }
145
146 Duple
147 Item::scroll_offset () const
148 {
149         if (_scroll_parent) {
150                 return _scroll_parent->scroll_offset();
151         }
152         return Duple (0,0);
153 }
154
155 Duple
156 Item::position_offset() const
157 {
158         Item const * i = this;
159         Duple offset;
160
161         while (i) {
162                 offset = offset.translate (i->position());
163                 i = i->parent();
164         }
165
166         return offset;
167 }
168
169 ArdourCanvas::Rect
170 Item::item_to_canvas (ArdourCanvas::Rect const & r) const
171 {
172         return r.translate (position_offset());
173 }
174
175 ArdourCanvas::Duple
176 Item::item_to_canvas (ArdourCanvas::Duple const & d) const
177 {
178         return d.translate (position_offset());
179 }
180
181 ArdourCanvas::Duple
182 Item::canvas_to_item (ArdourCanvas::Duple const & r) const
183 {
184         return r.translate (-position_offset());
185 }
186
187 ArdourCanvas::Rect
188 Item::canvas_to_item (ArdourCanvas::Rect const & r) const
189 {
190         return r.translate (-position_offset());
191 }
192
193 void
194 Item::item_to_canvas (Coord& x, Coord& y) const
195 {
196         Duple d = item_to_canvas (Duple (x, y));
197
198         x = d.x;
199         y = d.y;
200 }
201
202 void
203 Item::canvas_to_item (Coord& x, Coord& y) const
204 {
205         Duple d = canvas_to_item (Duple (x, y));
206
207         x = d.x;
208         y = d.y;
209 }
210
211
212 Duple
213 Item::item_to_window (ArdourCanvas::Duple const & d, bool rounded) const
214 {
215         Duple ret = item_to_canvas (d).translate (-scroll_offset());
216
217         if (rounded) {
218                 ret.x = round (ret.x);
219                 ret.y = round (ret.y);
220         }
221
222         return ret;
223 }
224
225 Duple
226 Item::window_to_item (ArdourCanvas::Duple const & d) const
227 {
228         return canvas_to_item (d.translate (scroll_offset()));
229 }
230
231 ArdourCanvas::Rect
232 Item::item_to_window (ArdourCanvas::Rect const & r, bool rounded) const
233 {
234         Rect ret = item_to_canvas (r).translate (-scroll_offset());
235
236         if (rounded) {
237                 ret.x0 = round (ret.x0);
238                 ret.x1 = round (ret.x1);
239                 ret.y0 = round (ret.y0);
240                 ret.y1 = round (ret.y1);
241         }
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         ArdourCanvas::Rect bbox = bounding_box ();
261         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);
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, bool already_added)
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         if (!already_added) {
431                 _parent->add (this);
432         }
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 void
573 Item::size_allocate (Rect const & r)
574 {
575         _allocation = r;
576 }
577
578 /** @return Bounding box in this item's coordinates */
579 ArdourCanvas::Rect
580 Item::bounding_box (bool for_own_purposes) const
581 {
582         if (_bounding_box_dirty) {
583                 compute_bounding_box ();
584                 assert (!_bounding_box_dirty);
585                 add_child_bounding_boxes ();
586         }
587
588         if (!for_own_purposes) {
589                 if (_allocation) {
590                         return _allocation;
591                 }
592         }
593
594         return _bounding_box;
595 }
596
597 Coord
598 Item::height () const
599 {
600         ArdourCanvas::Rect bb  = bounding_box();
601
602         if (bb) {
603                 return bb.height ();
604         }
605         return 0;
606 }
607
608 Coord
609 Item::width () const
610 {
611         ArdourCanvas::Rect bb = bounding_box();
612
613         if (bb) {
614                 return bb.width ();
615         }
616
617         return 0;
618 }
619
620 void
621 Item::redraw () const
622 {
623         if (visible() && _bounding_box && _canvas) {
624                 _canvas->request_redraw (item_to_window (_bounding_box));
625         }
626 }
627
628 void
629 Item::begin_change ()
630 {
631         _pre_change_bounding_box = bounding_box ();
632 }
633
634 void
635 Item::end_change ()
636 {
637         if (visible()) {
638                 _canvas->item_changed (this, _pre_change_bounding_box);
639
640                 if (_parent) {
641                         _parent->child_changed ();
642                 }
643         }
644 }
645
646 void
647 Item::begin_visual_change ()
648 {
649 }
650
651 void
652 Item::end_visual_change ()
653 {
654         if (visible()) {
655                 _canvas->item_visual_property_changed (this);
656         }
657 }
658
659 void
660 Item::move (Duple movement)
661 {
662         set_position (position() + movement);
663 }
664
665 void
666 Item::grab ()
667 {
668         assert (_canvas);
669         _canvas->grab (this);
670 }
671
672 void
673 Item::ungrab ()
674 {
675         assert (_canvas);
676         _canvas->ungrab ();
677 }
678
679 void
680 Item::set_data (string const & key, void* data)
681 {
682         _data[key] = data;
683 }
684
685 void *
686 Item::get_data (string const & key) const
687 {
688         map<string, void*>::const_iterator i = _data.find (key);
689         if (i == _data.end ()) {
690                 return 0;
691         }
692
693         return i->second;
694 }
695
696 void
697 Item::set_ignore_events (bool ignore)
698 {
699         _ignore_events = ignore;
700 }
701
702 std::string
703 Item::whatami () const
704 {
705         std::string type = demangle (typeid (*this).name());
706         return type.substr (type.find_last_of (':') + 1);
707 }
708
709 uint32_t
710 Item::depth () const
711 {
712         Item* i = _parent;
713         int d = 0;
714         while (i) {
715                 ++d;
716                 i = i->parent();
717         }
718         return d;
719 }
720
721 bool
722 Item::covers (Duple const & point) const
723 {
724         Duple p = window_to_item (point);
725
726         if (_bounding_box_dirty) {
727                 compute_bounding_box ();
728         }
729
730         Rect r = bounding_box();
731
732         if (!r) {
733                 return false;
734         }
735
736         return r.contains (p);
737 }
738
739 /* nesting/grouping API */
740
741 void
742 Item::render_children (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
743 {
744         if (_items.empty()) {
745                 return;
746         }
747
748         ensure_lut ();
749         std::vector<Item*> items = _lut->get (area);
750
751 #ifdef CANVAS_DEBUG
752         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
753                 cerr << string_compose ("%1%7 %2 @ %7 render %5 @ %6 %3 items out of %4\n",
754                                         _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area, _position, this,
755                                         whatami());
756         }
757 #endif
758
759         ++render_depth;
760
761         for (std::vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
762
763                 if (!(*i)->visible ()) {
764 #ifdef CANVAS_DEBUG
765                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
766                                 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
767                         }
768 #endif
769                         continue;
770                 }
771
772                 Rect item_bbox = (*i)->bounding_box ();
773
774                 if (!item_bbox) {
775 #ifdef CANVAS_DEBUG
776                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
777                                 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
778                         }
779 #endif
780                         continue;
781                 }
782
783                 Rect item = (*i)->item_to_window (item_bbox, false);
784                 Rect d = item.intersection (area);
785
786                 if (d) {
787                         Rect draw = d;
788                         if (draw.width() && draw.height()) {
789 #ifdef CANVAS_DEBUG
790                                 if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
791                                         if (dynamic_cast<Container*>(*i) == 0) {
792                                                 cerr << _canvas->render_indent() << "render "
793                                                      << ' '
794                                                      << (*i)
795                                                      << ' '
796                                                      << (*i)->whatami()
797                                                      << ' '
798                                                      << (*i)->name
799                                                      << " item "
800                                                      << item_bbox
801                                                      << " window = "
802                                                      << item
803                                                      << " intersect = "
804                                                      << draw
805                                                      << " @ "
806                                                      << _position
807                                                      << endl;
808                                         }
809                                 }
810 #endif
811
812                                 (*i)->render (area, context);
813                                 ++render_count;
814                         }
815
816                 } else {
817
818 #ifdef CANVAS_DEBUG
819                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
820                                 cerr << string_compose ("%1skip render of %2 %3, no intersection between %4 and %5\n", _canvas->render_indent(), (*i)->whatami(),
821                                                         (*i)->name, item, area);
822                         }
823 #endif
824
825                 }
826         }
827
828         --render_depth;
829 }
830
831 void
832 Item::prepare_for_render_children (Rect const & area) const
833 {
834         if (_items.empty()) {
835                 return;
836         }
837
838         ensure_lut ();
839         std::vector<Item*> items = _lut->get (area);
840
841         for (std::vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
842
843                 if (!(*i)->visible ()) {
844                         continue;
845                 }
846
847                 Rect item_bbox = (*i)->bounding_box ();
848
849                 if (!item_bbox) {
850                         continue;
851                 }
852
853                 Rect item = (*i)->item_to_window (item_bbox, false);
854                 Rect d = item.intersection (area);
855
856                 if (d) {
857                         Rect draw = d;
858                         if (draw.width() && draw.height()) {
859                                 (*i)->prepare_for_render (area);
860                         }
861
862                 } else {
863                         // Item does not intersect with visible canvas area
864                 }
865         }
866 }
867
868 void
869 Item::add_child_bounding_boxes (bool include_hidden) const
870 {
871         Rect self;
872         Rect bbox;
873         bool have_one = false;
874
875         if (_bounding_box) {
876                 bbox = _bounding_box;
877                 have_one = true;
878         }
879
880         for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
881
882                 if (!(*i)->visible() && !include_hidden) {
883                         continue;
884                 }
885
886                 Rect item_bbox = (*i)->bounding_box ();
887
888                 if (!item_bbox) {
889                         continue;
890                 }
891
892                 Rect group_bbox = (*i)->item_to_parent (item_bbox);
893                 if (have_one) {
894                         bbox = bbox.extend (group_bbox);
895                 } else {
896                         bbox = group_bbox;
897                         have_one = true;
898                 }
899         }
900
901         if (!have_one) {
902                 _bounding_box = Rect ();
903         } else {
904                 _bounding_box = bbox;
905         }
906 }
907
908 void
909 Item::add (Item* i)
910 {
911         /* XXX should really notify canvas about this */
912
913         _items.push_back (i);
914         i->reparent (this, true);
915         invalidate_lut ();
916         _bounding_box_dirty = true;
917 }
918
919 void
920 Item::add_front (Item* i)
921 {
922         /* XXX should really notify canvas about this */
923
924         _items.push_front (i);
925         i->reparent (this, true);
926         invalidate_lut ();
927         _bounding_box_dirty = true;
928 }
929
930 void
931 Item::remove (Item* i)
932 {
933
934         if (i->parent() != this) {
935                 return;
936         }
937
938         /* we cannot call bounding_box() here because that will iterate over
939            _items, one of which (the argument, i) may be in the middle of
940            deletion, making it impossible to call compute_bounding_box()
941            on it.
942         */
943
944         if (_bounding_box) {
945                 _pre_change_bounding_box = _bounding_box;
946         } else {
947                 _pre_change_bounding_box = Rect();
948         }
949
950         i->unparent ();
951         _items.remove (i);
952         invalidate_lut ();
953         _bounding_box_dirty = true;
954
955         end_change ();
956 }
957
958 void
959 Item::clear (bool with_delete)
960 {
961         begin_change ();
962
963         clear_items (with_delete);
964
965         invalidate_lut ();
966         _bounding_box_dirty = true;
967
968         end_change ();
969 }
970
971 void
972 Item::clear_items (bool with_delete)
973 {
974         for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
975
976                 list<Item*>::iterator tmp = i;
977                 Item *item = *i;
978
979                 ++tmp;
980
981                 /* remove from list before doing anything else, because we
982                  * don't want to find the item in _items during any activity
983                  * driven by unparent-ing or deletion.
984                  */
985
986                 _items.erase (i);
987                 item->unparent ();
988
989                 if (with_delete) {
990                         delete item;
991                 }
992
993                 i = tmp;
994         }
995 }
996
997 void
998 Item::raise_child_to_top (Item* i)
999 {
1000         if (!_items.empty()) {
1001                 if (_items.back() == i) {
1002                         return;
1003                 }
1004         }
1005
1006         _items.remove (i);
1007         _items.push_back (i);
1008
1009         invalidate_lut ();
1010         redraw ();
1011 }
1012
1013 void
1014 Item::raise_child (Item* i, int levels)
1015 {
1016         list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
1017         assert (j != _items.end ());
1018
1019         ++j;
1020         _items.remove (i);
1021
1022         while (levels > 0 && j != _items.end ()) {
1023                 ++j;
1024                 --levels;
1025         }
1026
1027         _items.insert (j, i);
1028         invalidate_lut ();
1029         redraw ();
1030 }
1031
1032 void
1033 Item::lower_child_to_bottom (Item* i)
1034 {
1035         if (!_items.empty()) {
1036                 if (_items.front() == i) {
1037                         return;
1038                 }
1039         }
1040         _items.remove (i);
1041         _items.push_front (i);
1042         invalidate_lut ();
1043         redraw ();
1044 }
1045
1046 void
1047 Item::ensure_lut () const
1048 {
1049         if (!_lut) {
1050                 _lut = new DumbLookupTable (*this);
1051         }
1052 }
1053
1054 void
1055 Item::invalidate_lut () const
1056 {
1057         delete _lut;
1058         _lut = 0;
1059 }
1060
1061 void
1062 Item::child_changed ()
1063 {
1064         invalidate_lut ();
1065         _bounding_box_dirty = true;
1066
1067         if (_parent) {
1068                 _parent->child_changed ();
1069         }
1070 }
1071
1072 void
1073 Item::add_items_at_point (Duple const point, vector<Item const *>& items) const
1074 {
1075         Rect const bbox = bounding_box ();
1076
1077         /* Point is in window coordinate system */
1078
1079         if (!bbox || !item_to_window (bbox).contains (point)) {
1080                 return;
1081         }
1082
1083         /* recurse and add any items within our group that contain point.
1084            Our children are only considered visible if we are, and similarly
1085            only if we do not ignore events.
1086         */
1087
1088         vector<Item*> our_items;
1089
1090         if (!_items.empty() && visible() && !_ignore_events) {
1091                 ensure_lut ();
1092                 our_items = _lut->items_at_point (point);
1093         }
1094
1095         if (!our_items.empty() || covers (point)) {
1096                 /* this adds this item itself to the list of items at point */
1097                 items.push_back (this);
1098         }
1099
1100         for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
1101                 (*i)->add_items_at_point (point, items);
1102         }
1103 }
1104
1105 void
1106 Item::set_tooltip (const std::string& s)
1107 {
1108         _tooltip = s;
1109 }
1110
1111 void
1112 Item::start_tooltip_timeout ()
1113 {
1114         if (!_tooltip.empty()) {
1115                 _canvas->start_tooltip_timeout (this);
1116         }
1117 }
1118
1119 void
1120 Item::stop_tooltip_timeout ()
1121 {
1122         _canvas->stop_tooltip_timeout ();
1123 }
1124
1125 void
1126 Item::dump (ostream& o) const
1127 {
1128         ArdourCanvas::Rect bb = bounding_box();
1129
1130         o << _canvas->indent() << whatami() << ' ' << this << " self-Visible ? " << self_visible() << " visible ? " << visible();
1131         o << " @ " << position();
1132
1133 #ifdef CANVAS_DEBUG
1134         if (!name.empty()) {
1135                 o << ' ' << name;
1136         }
1137 #endif
1138
1139         if (bb) {
1140                 o << endl << _canvas->indent() << "\tbbox: " << bb;
1141                 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb);
1142         } else {
1143                 o << " bbox unset";
1144         }
1145
1146         o << endl;
1147
1148         if (!_items.empty()) {
1149
1150 #ifdef CANVAS_DEBUG
1151                 o << _canvas->indent();
1152                 o << " @ " << position();
1153                 o << " Items: " << _items.size();
1154                 o << " Self-Visible ? " << self_visible();
1155                 o << " Visible ? " << visible();
1156
1157                 Rect bb = bounding_box();
1158
1159                 if (bb) {
1160                         o << endl << _canvas->indent() << "  bbox: " << bb;
1161                         o << endl << _canvas->indent() << "  CANVAS bbox: " << item_to_canvas (bb);
1162                 } else {
1163                         o << "  bbox unset";
1164                 }
1165
1166                 o << endl;
1167 #endif
1168
1169                 ArdourCanvas::dump_depth++;
1170
1171                 for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
1172                         o << **i;
1173                 }
1174
1175                 ArdourCanvas::dump_depth--;
1176         }
1177 }
1178
1179 ostream&
1180 ArdourCanvas::operator<< (ostream& o, const Item& i)
1181 {
1182         i.dump (o);
1183         return o;
1184 }
1185