2 Copyright (C) 2011-2013 Paul Davis
3 Author: Carl Hetherington <cth@carlh.net>
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.
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.
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.
20 #include "pbd/compose.h"
21 #include "pbd/stacktrace.h"
22 #include "pbd/convert.h"
24 #include "ardour/utils.h"
26 #include "canvas/canvas.h"
27 #include "canvas/debug.h"
28 #include "canvas/group.h"
29 #include "canvas/item.h"
30 #include "canvas/scroll_group.h"
34 using namespace ArdourCanvas;
36 Item::Item (Canvas* canvas)
42 , _bounding_box_dirty (true)
43 , _ignore_events (false)
45 DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
48 Item::Item (Group* parent)
51 , _canvas (parent->canvas())
54 , _bounding_box_dirty (true)
55 , _ignore_events (false)
57 DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
63 find_scroll_parent ();
66 Item::Item (Group* parent, Duple const& p)
69 , _canvas (parent->canvas())
73 , _bounding_box_dirty (true)
74 , _ignore_events (false)
76 DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
82 find_scroll_parent ();
89 _parent->remove (this);
93 _canvas->item_going_away (this, _bounding_box);
98 Item::canvas_origin () const
100 return item_to_canvas (Duple (0,0));
104 Item::window_origin () const
106 /* This is slightly subtle. Our _position is in the coordinate space of
107 our parent. So to find out where that is in window coordinates, we
108 have to ask our parent.
111 return _parent->item_to_window (_position);
118 Item::item_to_parent (ArdourCanvas::Rect const & r) const
120 return r.translate (_position);
124 Item::scroll_offset () const
126 if (_scroll_parent) {
127 return _scroll_parent->scroll_offset();
133 Item::position_offset() const
135 Item const * i = this;
139 offset = offset.translate (i->position());
147 Item::item_to_canvas (ArdourCanvas::Rect const & r) const
149 return r.translate (position_offset());
153 Item::item_to_canvas (ArdourCanvas::Duple const & d) const
155 return d.translate (position_offset());
159 Item::canvas_to_item (ArdourCanvas::Duple const & r) const
161 return r.translate (-position_offset());
165 Item::canvas_to_item (ArdourCanvas::Rect const & r) const
167 return r.translate (-position_offset());
171 Item::item_to_canvas (Coord& x, Coord& y) const
173 Duple d = item_to_canvas (Duple (x, y));
180 Item::canvas_to_item (Coord& x, Coord& y) const
182 Duple d = canvas_to_item (Duple (x, y));
190 Item::item_to_window (ArdourCanvas::Duple const & d, bool rounded) const
192 Duple ret = item_to_canvas (d).translate (-scroll_offset());
195 ret.x = round (ret.x);
196 ret.y = round (ret.y);
203 Item::window_to_item (ArdourCanvas::Duple const & d) const
205 return canvas_to_item (d.translate (scroll_offset()));
209 Item::item_to_window (ArdourCanvas::Rect const & r) const
211 Rect ret = item_to_canvas (r).translate (-scroll_offset());
213 ret.x0 = round (ret.x0);
214 ret.x1 = round (ret.x1);
215 ret.y0 = round (ret.y0);
216 ret.y1 = round (ret.y1);
222 Item::window_to_item (ArdourCanvas::Rect const & r) const
224 return canvas_to_item (r.translate (scroll_offset()));
227 /** Set the position of this item in the parent's coordinates */
229 Item::set_position (Duple p)
231 if (p == _position) {
235 boost::optional<ArdourCanvas::Rect> bbox = bounding_box ();
236 boost::optional<ArdourCanvas::Rect> pre_change_parent_bounding_box;
239 /* see the comment in Canvas::item_moved() to understand
240 * why we use the parent's bounding box here.
242 pre_change_parent_bounding_box = item_to_parent (bbox.get());
247 _canvas->item_moved (this, pre_change_parent_bounding_box);
250 _parent->child_changed ();
255 Item::set_x_position (Coord x)
257 set_position (Duple (x, _position.y));
261 Item::set_y_position (Coord y)
263 set_position (Duple (_position.x, y));
267 Item::raise_to_top ()
270 _parent->raise_child_to_top (this);
275 Item::raise (int levels)
278 _parent->raise_child (this, levels);
283 Item::lower_to_bottom ()
286 _parent->lower_child_to_bottom (this);
295 _canvas->item_shown_or_hidden (this);
304 _canvas->item_shown_or_hidden (this);
309 Item::item_to_parent (Duple const & d) const
311 return d.translate (_position);
315 Item::parent_to_item (Duple const & d) const
317 return d.translate (- _position);
321 Item::parent_to_item (ArdourCanvas::Rect const & d) const
323 return d.translate (- _position);
334 Item::reparent (Group* new_parent)
336 if (new_parent == _parent) {
340 assert (_canvas == new_parent->canvas());
343 _parent->remove (this);
348 _parent = new_parent;
349 _canvas = _parent->canvas ();
351 find_scroll_parent ();
357 Item::find_scroll_parent ()
359 Item const * i = this;
360 ScrollGroup const * last_scroll_group = 0;
362 /* Don't allow a scroll group to find itself as its own scroll parent
368 ScrollGroup const * sg = dynamic_cast<ScrollGroup const *> (i);
370 last_scroll_group = sg;
375 _scroll_parent = const_cast<ScrollGroup*> (last_scroll_group);
379 Item::common_ancestor_within (uint32_t limit, const Item& other) const
381 uint32_t d1 = depth();
382 uint32_t d2 = other.depth();
383 const Item* i1 = this;
384 const Item* i2 = &other;
386 /* move towards root until we are at the same level
411 /* now see if there is a common parent */
431 Item::closest_ancestor_with (const Item& other) const
433 uint32_t d1 = depth();
434 uint32_t d2 = other.depth();
435 const Item* i1 = this;
436 const Item* i2 = &other;
438 /* move towards root until we are at the same level
458 /* now see if there is a common parent */
473 Item::is_descendant_of (const Item& candidate) const
475 Item const * i = _parent;
478 if (i == &candidate) {
493 /** @return Bounding box in this item's coordinates */
494 boost::optional<ArdourCanvas::Rect>
495 Item::bounding_box () const
497 if (_bounding_box_dirty) {
498 compute_bounding_box ();
499 assert (!_bounding_box_dirty);
502 return _bounding_box;
506 Item::height () const
508 boost::optional<ArdourCanvas::Rect> bb = bounding_box();
511 return bb->height ();
519 boost::optional<ArdourCanvas::Rect> bb = bounding_box().get();
529 Item::redraw () const
531 if (_visible && _bounding_box && _canvas) {
532 _canvas->request_redraw (item_to_window (_bounding_box.get()));
537 Item::begin_change ()
539 _pre_change_bounding_box = bounding_box ();
545 _canvas->item_changed (this, _pre_change_bounding_box);
548 _parent->child_changed ();
553 Item::begin_visual_change ()
558 Item::end_visual_change ()
560 _canvas->item_visual_property_changed (this);
564 Item::move (Duple movement)
566 set_position (position() + movement);
573 _canvas->grab (this);
584 Item::set_data (string const & key, void* data)
590 Item::get_data (string const & key) const
592 map<string, void*>::const_iterator i = _data.find (key);
593 if (i == _data.end ()) {
601 Item::set_ignore_events (bool ignore)
603 _ignore_events = ignore;
607 Item::dump (ostream& o) const
609 boost::optional<ArdourCanvas::Rect> bb = bounding_box();
611 o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
612 o << " @ " << position();
621 o << endl << _canvas->indent() << "\tbbox: " << bb.get();
622 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
631 Item::whatami () const
633 std::string type = demangle (typeid (*this).name());
634 return type.substr (type.find_last_of (':') + 1);
650 Item::covers (Duple const & point) const
652 Duple p = window_to_item (point);
654 if (_bounding_box_dirty) {
655 compute_bounding_box ();
658 boost::optional<Rect> r = bounding_box();
664 return r.get().contains (p);
668 ArdourCanvas::operator<< (ostream& o, const Item& i)