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/group.h"
27 #include "canvas/item.h"
28 #include "canvas/canvas.h"
29 #include "canvas/debug.h"
33 using namespace ArdourCanvas;
35 Item::Item (Canvas* canvas)
38 , _scroll_sensitivity (ScrollSensitivity (0))
43 Item::Item (Group* parent)
44 : _canvas (parent->canvas ())
46 , _scroll_sensitivity (ScrollSensitivity (0))
51 Item::Item (Group* parent, Duple position)
52 : _canvas (parent->canvas())
54 , _position (position)
55 , _scroll_sensitivity (ScrollSensitivity (0))
64 _bounding_box_dirty = true;
65 _ignore_events = false;
71 DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
77 _parent->remove (this);
81 _canvas->item_going_away (this, _bounding_box);
86 Item::scroll_to (Duple const& d)
88 if (_scroll_sensitivity & ScrollsVertically) {
89 _scroll_offset.y = d.y;
92 if (_scroll_sensitivity & ScrollsHorizontally) {
93 _scroll_offset.x = d.x;
98 Item::set_scroll_sensitivity (ScrollSensitivity s)
100 _scroll_sensitivity = s;
104 Item::item_to_parent (ArdourCanvas::Rect const & r) const
106 return r.translate (_position);
110 Item::item_to_canvas (ArdourCanvas::Rect const & r) const
112 Item const * i = this;
116 offset = offset.translate (i->position());
120 return r.translate (offset);
124 Item::item_to_canvas (ArdourCanvas::Duple const & d) const
126 Item const * i = this;
130 offset = offset.translate (i->position());
134 return d.translate (offset);
138 Item::canvas_to_item (ArdourCanvas::Duple const & d) const
140 Item const * i = this;
144 offset = offset.translate (-(i->position()));
148 return d.translate (offset);
152 Item::canvas_to_item (ArdourCanvas::Rect const & d) const
154 Item const * i = this;
158 offset = offset.translate (-(i->position()));
162 return d.translate (offset);
166 Item::item_to_canvas (Coord& x, Coord& y) const
168 Duple d = item_to_canvas (Duple (x, y));
175 Item::canvas_to_item (Coord& x, Coord& y) const
177 Duple d = canvas_to_item (Duple (x, y));
184 Item::item_to_window (ArdourCanvas::Duple const & d, bool rounded) const
186 Item const * i = this;
190 offset = offset.translate (i->scroll_offset());
194 return _canvas->canvas_to_window (d.translate (offset), rounded);
198 Item::window_to_item (ArdourCanvas::Duple const & d) const
200 Item const * i = this;
204 offset = offset.translate (-i->scroll_offset());
208 return _canvas->window_to_canvas (d.translate (offset));
212 Item::item_to_window (ArdourCanvas::Rect const & r) const
214 Item const * i = this;
218 offset = offset.translate (i->scroll_offset());
222 return _canvas->canvas_to_window (item_to_canvas (r.translate (offset)));
226 Item::window_to_item (ArdourCanvas::Rect const & r) const
228 Item const * i = this;
232 offset = offset.translate (-i->scroll_offset());
236 return canvas_to_item (_canvas->window_to_canvas (r).translate (offset));
239 /** Set the position of this item in the parent's coordinates */
241 Item::set_position (Duple p)
243 if (p == _position) {
247 boost::optional<ArdourCanvas::Rect> bbox = bounding_box ();
248 boost::optional<ArdourCanvas::Rect> pre_change_parent_bounding_box;
251 /* see the comment in Canvas::item_moved() to understand
252 * why we use the parent's bounding box here.
254 pre_change_parent_bounding_box = item_to_parent (bbox.get());
259 _canvas->item_moved (this, pre_change_parent_bounding_box);
262 _parent->child_changed ();
267 Item::set_x_position (Coord x)
269 set_position (Duple (x, _position.y));
273 Item::set_y_position (Coord y)
275 set_position (Duple (_position.x, y));
279 Item::raise_to_top ()
282 _parent->raise_child_to_top (this);
286 Item::raise (int levels)
289 _parent->raise_child (this, levels);
293 Item::lower_to_bottom ()
296 _parent->lower_child_to_bottom (this);
304 _canvas->item_shown_or_hidden (this);
313 _canvas->item_shown_or_hidden (this);
318 Item::item_to_parent (Duple const & d) const
320 return d.translate (_position);
324 Item::parent_to_item (Duple const & d) const
326 return d.translate (- _position);
330 Item::parent_to_item (ArdourCanvas::Rect const & d) const
332 return d.translate (- _position);
342 Item::reparent (Group* new_parent)
344 assert (_canvas == _parent->canvas());
347 _parent->remove (this);
352 _parent = new_parent;
353 _canvas = _parent->canvas ();
358 Item::common_ancestor_within (uint32_t limit, const Item& other) const
360 uint32_t d1 = depth();
361 uint32_t d2 = other.depth();
362 const Item* i1 = this;
363 const Item* i2 = &other;
365 /* move towards root until we are at the same level
384 /* now see if there is a common parent */
404 Item::closest_ancestor_with (const Item& other) const
406 uint32_t d1 = depth();
407 uint32_t d2 = other.depth();
408 const Item* i1 = this;
409 const Item* i2 = &other;
411 /* move towards root until we are at the same level
425 /* now see if there is a common parent */
440 Item::is_descendant_of (const Item& candidate) const
442 Item const * i = _parent;
445 if (i == &candidate) {
460 /** @return Bounding box in this item's coordinates */
461 boost::optional<ArdourCanvas::Rect>
462 Item::bounding_box () const
464 if (_bounding_box_dirty) {
465 compute_bounding_box ();
466 assert (!_bounding_box_dirty);
469 return _bounding_box;
473 Item::height () const
475 boost::optional<ArdourCanvas::Rect> bb = bounding_box();
478 return bb->height ();
486 boost::optional<ArdourCanvas::Rect> bb = bounding_box().get();
496 Item::redraw () const
498 if (_visible && _bounding_box && _canvas) {
499 _canvas->request_redraw (item_to_canvas (_bounding_box.get()));
504 Item::begin_change ()
506 _pre_change_bounding_box = bounding_box ();
512 _canvas->item_changed (this, _pre_change_bounding_box);
515 _parent->child_changed ();
520 Item::begin_visual_change ()
525 Item::end_visual_change ()
527 _canvas->item_visual_property_changed (this);
531 Item::move (Duple movement)
533 set_position (position() + movement);
540 _canvas->grab (this);
551 Item::set_data (string const & key, void* data)
557 Item::get_data (string const & key) const
559 map<string, void*>::const_iterator i = _data.find (key);
560 if (i == _data.end ()) {
568 Item::set_ignore_events (bool ignore)
570 _ignore_events = ignore;
574 Item::dump (ostream& o) const
576 boost::optional<ArdourCanvas::Rect> bb = bounding_box();
578 o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
579 o << " @ " << position() << " scrolled-to " << _scroll_offset;
588 o << endl << _canvas->indent() << "\tbbox: " << bb.get();
589 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
598 Item::whatami () const
600 std::string type = demangle (typeid (*this).name());
601 return type.substr (type.find_last_of (':') + 1);
617 Item::covers (Duple const & point) const
619 Duple p = canvas_to_item (point);
621 if (_bounding_box_dirty) {
622 compute_bounding_box ();
625 boost::optional<Rect> r = bounding_box();
631 return r.get().contains (p);
635 ArdourCanvas::operator<< (ostream& o, const Item& i)