Merge branch 'master' into cairocanvas
[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/stacktrace.h"
22 #include "pbd/convert.h"
23
24 #include "ardour/utils.h"
25
26 #include "canvas/group.h"
27 #include "canvas/item.h"
28 #include "canvas/canvas.h"
29 #include "canvas/debug.h"
30
31 using namespace std;
32 using namespace PBD;
33 using namespace ArdourCanvas;
34
35 Item::Item (Canvas* canvas)
36         : _canvas (canvas)
37         , _parent (0)
38 {
39         init ();
40 }
41
42 Item::Item (Group* parent)
43         : _canvas (parent->canvas ())
44         , _parent (parent)
45 {
46         init ();
47 }
48
49 Item::Item (Group* parent, Duple position)
50         : _canvas (parent->canvas())
51         , _parent (parent)
52         , _position (position)
53 {
54         init ();
55 }
56
57 void
58 Item::init ()
59 {
60         _visible = true;
61         _bounding_box_dirty = true;
62         _ignore_events = false;
63         
64         if (_parent) {
65                 _parent->add (this);
66         }
67
68         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
69 }       
70
71 Item::~Item ()
72 {
73         if (_parent) {
74                 _parent->remove (this);
75         }
76
77         if (_canvas) {
78                 _canvas->item_going_away (this, _bounding_box);
79         }
80 }
81
82 ArdourCanvas::Rect
83 Item::item_to_parent (ArdourCanvas::Rect const & r) const
84 {
85         return r.translate (_position);
86 }
87
88 ArdourCanvas::Rect
89 Item::item_to_canvas (ArdourCanvas::Rect const & r) const
90 {
91         Item const * i = this;
92         Duple offset;
93
94         while (i) {
95                 offset = offset.translate (i->position());
96                 i = i->parent();
97         }
98
99         return r.translate (offset);
100 }
101
102 ArdourCanvas::Duple
103 Item::item_to_canvas (ArdourCanvas::Duple const & d) const
104 {
105         Item const * i = this;
106         Duple offset;
107
108         while (i) {
109                 offset = offset.translate (i->position());
110                 i = i->parent();
111         }
112
113         return d.translate (offset);
114 }
115
116 ArdourCanvas::Duple
117 Item::canvas_to_item (ArdourCanvas::Duple const & d) const
118 {
119         Item const * i = this;
120         Duple offset;
121
122         while (i) {
123                 offset = offset.translate (-(i->position()));
124                 i = i->parent();
125         }
126
127         return d.translate (offset);
128 }
129
130 void
131 Item::item_to_canvas (Coord& x, Coord& y) const
132 {
133         Duple d = item_to_canvas (Duple (x, y));
134                 
135         x = d.x;
136         y = d.y;
137 }
138
139 void
140 Item::canvas_to_item (Coord& x, Coord& y) const
141 {
142         Duple d = canvas_to_item (Duple (x, y));
143
144         x = d.x;
145         y = d.y;
146 }
147
148 /** Set the position of this item in the parent's coordinates */
149 void
150 Item::set_position (Duple p)
151 {
152         boost::optional<ArdourCanvas::Rect> bbox = bounding_box ();
153         boost::optional<ArdourCanvas::Rect> pre_change_parent_bounding_box;
154
155         if (bbox) {
156                 /* see the comment in Canvas::item_moved() to understand
157                  * why we use the parent's bounding box here.
158                  */
159                 pre_change_parent_bounding_box = item_to_parent (bbox.get());
160         }
161         
162         _position = p;
163
164         _canvas->item_moved (this, pre_change_parent_bounding_box);
165
166         if (_parent) {
167                 _parent->child_changed ();
168         }
169 }
170
171 void
172 Item::set_x_position (Coord x)
173 {
174         set_position (Duple (x, _position.y));
175 }
176
177 void
178 Item::set_y_position (Coord y)
179 {
180         set_position (Duple (_position.x, y));
181 }
182
183 void
184 Item::raise_to_top ()
185 {
186         assert (_parent);
187         _parent->raise_child_to_top (this);
188 }
189
190 void
191 Item::raise (int levels)
192 {
193         assert (_parent);
194         _parent->raise_child (this, levels);
195 }
196
197 void
198 Item::lower_to_bottom ()
199 {
200         assert (_parent);
201         _parent->lower_child_to_bottom (this);
202 }
203
204 void
205 Item::hide ()
206 {
207         _visible = false;
208         _canvas->item_shown_or_hidden (this);
209 }
210
211 void
212 Item::show ()
213 {
214         _visible = true;
215         _canvas->item_shown_or_hidden (this);
216 }
217
218 Duple
219 Item::item_to_parent (Duple const & d) const
220 {
221         return d.translate (_position);
222 }
223
224 Duple
225 Item::parent_to_item (Duple const & d) const
226 {
227         return d.translate (- _position);
228 }
229
230 ArdourCanvas::Rect
231 Item::parent_to_item (ArdourCanvas::Rect const & d) const
232 {
233         return d.translate (- _position);
234 }
235
236 void
237 Item::unparent ()
238 {
239         _parent = 0;
240 }
241
242 void
243 Item::reparent (Group* new_parent)
244 {
245         assert (_canvas == _parent->canvas());
246
247         if (_parent) {
248                 _parent->remove (this);
249         }
250
251         assert (new_parent);
252
253         _parent = new_parent;
254         _canvas = _parent->canvas ();
255         _parent->add (this);
256 }
257
258 void
259 Item::grab_focus ()
260 {
261         /* XXX */
262 }
263
264 /** @return Bounding box in this item's coordinates */
265 boost::optional<ArdourCanvas::Rect>
266 Item::bounding_box () const
267 {
268         if (_bounding_box_dirty) {
269                 compute_bounding_box ();
270         }
271
272         assert (!_bounding_box_dirty);
273         return _bounding_box;
274 }
275
276 Coord
277 Item::height () const 
278 {
279         boost::optional<ArdourCanvas::Rect> bb  = bounding_box();
280
281         if (bb) {
282                 return bb->height ();
283         }
284         return 0;
285 }
286
287 Coord
288 Item::width () const 
289 {
290         boost::optional<ArdourCanvas::Rect> bb = bounding_box().get();
291
292         if (bb) {
293                 return bb->width ();
294         }
295
296         return 0;
297 }
298
299 void
300 Item::begin_change ()
301 {
302         _pre_change_bounding_box = bounding_box ();
303 }
304
305 void
306 Item::end_change ()
307 {
308         _canvas->item_changed (this, _pre_change_bounding_box);
309         
310         if (_parent) {
311                 _parent->child_changed ();
312         }
313 }
314
315 void
316 Item::begin_visual_change ()
317 {
318 }
319
320 void
321 Item::end_visual_change ()
322 {
323         _canvas->item_visual_property_changed (this);
324 }
325
326 void
327 Item::move (Duple movement)
328 {
329         set_position (position() + movement);
330 }
331
332 void
333 Item::grab ()
334 {
335         assert (_canvas);
336         _canvas->grab (this);
337 }
338
339 void
340 Item::ungrab ()
341 {
342         assert (_canvas);
343         _canvas->ungrab ();
344 }
345
346 void
347 Item::set_data (string const & key, void* data)
348 {
349         _data[key] = data;
350 }
351
352 void *
353 Item::get_data (string const & key) const
354 {
355         map<string, void*>::const_iterator i = _data.find (key);
356         if (i == _data.end ()) {
357                 return 0;
358         }
359         
360         return i->second;
361 }
362
363 void
364 Item::set_ignore_events (bool ignore)
365 {
366         _ignore_events = ignore;
367 }
368
369 void
370 Item::dump (ostream& o) const
371 {
372         boost::optional<ArdourCanvas::Rect> bb = bounding_box();
373
374         o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
375         o << " @ " << position();
376         
377 #ifdef CANVAS_DEBUG
378         if (!name.empty()) {
379                 o << ' ' << name;
380         }
381 #endif
382
383         if (bb) {
384                 o << endl << _canvas->indent() << "\tbbox: " << bb.get();
385                 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
386         } else {
387                 o << " bbox unset";
388         }
389
390         o << endl;
391 }
392
393 std::string
394 Item::whatami () const 
395 {
396         std::string type = demangle (typeid (*this).name());
397         return type.substr (type.find_last_of (':') + 1);
398 }
399
400 ostream&
401 ArdourCanvas::operator<< (ostream& o, const Item& i)
402 {
403         i.dump (o);
404         return o;
405 }