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