1 /*
2  * Copyright (C) 2012 Carl Hetherington <carl@carlh.net>
3  * Copyright (C) 2013-2017 Paul Davis <paul@linuxaudiosystems.com>
4  * Copyright (C) 2014-2017 Robin Gareus <robin@gareus.org>
5  * Copyright (C) 2015-2017 Tim Mayberry <mojofunk@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21 
22 #include "pbd/compose.h"
23 #include "pbd/demangle.h"
24 #include "pbd/convert.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 
Item(Canvas * canvas)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 
Item(Item * parent)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 
Item(Item * parent,Duple const & p)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 
~Item()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
visible() const108 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
canvas_origin() const123 Item::canvas_origin () const
124 {
125 	return item_to_canvas (Duple (0,0));
126 }
127 
128 Duple
window_origin() const129 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
item_to_parent(ArdourCanvas::Rect const & r) const143 Item::item_to_parent (ArdourCanvas::Rect const & r) const
144 {
145 	return r.translate (_position);
146 }
147 
148 Duple
scroll_offset() const149 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
position_offset() const158 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
item_to_canvas(ArdourCanvas::Rect const & r) const172 Item::item_to_canvas (ArdourCanvas::Rect const & r) const
173 {
174 	return r.translate (position_offset());
175 }
176 
177 ArdourCanvas::Duple
item_to_canvas(ArdourCanvas::Duple const & d) const178 Item::item_to_canvas (ArdourCanvas::Duple const & d) const
179 {
180 	return d.translate (position_offset());
181 }
182 
183 ArdourCanvas::Duple
canvas_to_item(ArdourCanvas::Duple const & r) const184 Item::canvas_to_item (ArdourCanvas::Duple const & r) const
185 {
186 	return r.translate (-position_offset());
187 }
188 
189 ArdourCanvas::Rect
canvas_to_item(ArdourCanvas::Rect const & r) const190 Item::canvas_to_item (ArdourCanvas::Rect const & r) const
191 {
192 	return r.translate (-position_offset());
193 }
194 
195 void
item_to_canvas(Coord & x,Coord & y) const196 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
canvas_to_item(Coord & x,Coord & y) const205 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
item_to_window(ArdourCanvas::Duple const & d,bool rounded) const215 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
window_to_item(ArdourCanvas::Duple const & d) const228 Item::window_to_item (ArdourCanvas::Duple const & d) const
229 {
230 	return canvas_to_item (d.translate (scroll_offset()));
231 }
232 
233 ArdourCanvas::Rect
item_to_window(ArdourCanvas::Rect const & r,bool rounded) const234 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
window_to_item(ArdourCanvas::Rect const & r) const249 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
set_position(Duple p)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
set_x_position(Coord x)289 Item::set_x_position (Coord x)
290 {
291 	set_position (Duple (x, _position.y));
292 }
293 
294 void
set_y_position(Coord y)295 Item::set_y_position (Coord y)
296 {
297 	set_position (Duple (_position.x, y));
298 }
299 
300 void
raise_to_top()301 Item::raise_to_top ()
302 {
303 	if (_parent) {
304 		_parent->raise_child_to_top (this);
305 	}
306 }
307 
308 void
raise(int levels)309 Item::raise (int levels)
310 {
311 	if (_parent) {
312 		_parent->raise_child (this, levels);
313 	}
314 }
315 
316 void
lower_to_bottom()317 Item::lower_to_bottom ()
318 {
319 	if (_parent) {
320 		_parent->lower_child_to_bottom (this);
321 	}
322 }
323 
324 void
hide()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
show()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
propagate_show_hide()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
item_to_parent(Duple const & d) const388 Item::item_to_parent (Duple const & d) const
389 {
390 	return d.translate (_position);
391 }
392 
393 Duple
parent_to_item(Duple const & d) const394 Item::parent_to_item (Duple const & d) const
395 {
396 	return d.translate (- _position);
397 }
398 
399 ArdourCanvas::Rect
parent_to_item(ArdourCanvas::Rect const & d) const400 Item::parent_to_item (ArdourCanvas::Rect const & d) const
401 {
402 	return d.translate (- _position);
403 }
404 
405 void
unparent()406 Item::unparent ()
407 {
408 	_parent = 0;
409 	_scroll_parent = 0;
410 }
411 
412 void
reparent(Item * new_parent,bool already_added)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
find_scroll_parent()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
common_ancestor_within(uint32_t limit,const Item & other) const460 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*
closest_ancestor_with(const Item & other) const512 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
is_descendant_of(const Item & candidate) const554 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
grab_focus()569 Item::grab_focus ()
570 {
571 	/* XXX */
572 }
573 
574 void
size_allocate(Rect const & r)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
bounding_box(bool for_own_purposes) const582 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
height() const600 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
width() const611 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
redraw() const623 Item::redraw () const
624 {
625 	if (visible() && _bounding_box && _canvas) {
626 		_canvas->request_redraw (item_to_window (_bounding_box));
627 	}
628 }
629 
630 void
begin_change()631 Item::begin_change ()
632 {
633 	_pre_change_bounding_box = bounding_box ();
634 }
635 
636 void
end_change()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
begin_visual_change()649 Item::begin_visual_change ()
650 {
651 }
652 
653 void
end_visual_change()654 Item::end_visual_change ()
655 {
656 	if (visible()) {
657 		_canvas->item_visual_property_changed (this);
658 	}
659 }
660 
661 void
move(Duple movement)662 Item::move (Duple movement)
663 {
664 	set_position (position() + movement);
665 }
666 
667 void
grab()668 Item::grab ()
669 {
670 	assert (_canvas);
671 	_canvas->grab (this);
672 }
673 
674 void
ungrab()675 Item::ungrab ()
676 {
677 	assert (_canvas);
678 	_canvas->ungrab ();
679 }
680 
681 void
set_data(string const & key,void * data)682 Item::set_data (string const & key, void* data)
683 {
684 	_data[key] = data;
685 }
686 
687 void *
get_data(string const & key) const688 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
set_ignore_events(bool ignore)699 Item::set_ignore_events (bool ignore)
700 {
701 	_ignore_events = ignore;
702 }
703 
704 std::string
whatami() const705 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
depth() const712 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
covers(Duple const & point) const724 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
render_children(Rect const & area,Cairo::RefPtr<Cairo::Context> context) const744 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
prepare_for_render_children(Rect const & area) const834 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
add_child_bounding_boxes(bool include_hidden) const871 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
add(Item * i)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
add_front(Item * i)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
remove(Item * i)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
clear(bool with_delete)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
clear_items(bool with_delete)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
raise_child_to_top(Item * i)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
raise_child(Item * i,int levels)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
lower_child_to_bottom(Item * i)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
ensure_lut() const1049 Item::ensure_lut () const
1050 {
1051 	if (!_lut) {
1052 		_lut = new DumbLookupTable (*this);
1053 	}
1054 }
1055 
1056 void
invalidate_lut() const1057 Item::invalidate_lut () const
1058 {
1059 	delete _lut;
1060 	_lut = 0;
1061 }
1062 
1063 void
child_changed()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
add_items_at_point(Duple const point,vector<Item const * > & items) const1075 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
set_tooltip(const std::string & s)1108 Item::set_tooltip (const std::string& s)
1109 {
1110 	_tooltip = s;
1111 }
1112 
1113 void
start_tooltip_timeout()1114 Item::start_tooltip_timeout ()
1115 {
1116 	if (!_tooltip.empty()) {
1117 		_canvas->start_tooltip_timeout (this);
1118 	}
1119 }
1120 
1121 void
stop_tooltip_timeout()1122 Item::stop_tooltip_timeout ()
1123 {
1124 	_canvas->stop_tooltip_timeout ();
1125 }
1126 
1127 void
dump(ostream & o) const1128 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&
operator <<(ostream & o,const Item & i)1182 ArdourCanvas::operator<< (ostream& o, const Item& i)
1183 {
1184 	i.dump (o);
1185 	return o;
1186 }
1187 
1188