fix merge with master
[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 ArdourCanvas::Rect
131 Item::canvas_to_item (ArdourCanvas::Rect const & d) const
132 {
133         Item const * i = this;
134         Duple offset;
135
136         while (i) {
137                 offset = offset.translate (-(i->position()));
138                 i = i->parent();
139         }
140
141         return d.translate (offset);
142 }
143
144 void
145 Item::item_to_canvas (Coord& x, Coord& y) const
146 {
147         Duple d = item_to_canvas (Duple (x, y));
148                 
149         x = d.x;
150         y = d.y;
151 }
152
153 void
154 Item::canvas_to_item (Coord& x, Coord& y) const
155 {
156         Duple d = canvas_to_item (Duple (x, y));
157
158         x = d.x;
159         y = d.y;
160 }
161
162 Duple
163 Item::item_to_window (ArdourCanvas::Duple const & d) const
164 {
165         return _canvas->canvas_to_window (item_to_canvas (d));
166 }
167
168 Duple
169 Item::window_to_item (ArdourCanvas::Duple const & d) const
170 {
171         return _canvas->window_to_canvas (canvas_to_item (d));
172 }
173
174 ArdourCanvas::Rect
175 Item::item_to_window (ArdourCanvas::Rect const & r) const
176 {
177         return _canvas->canvas_to_window (item_to_canvas (r));
178 }
179
180 /** Set the position of this item in the parent's coordinates */
181 void
182 Item::set_position (Duple p)
183 {
184         boost::optional<ArdourCanvas::Rect> bbox = bounding_box ();
185         boost::optional<ArdourCanvas::Rect> pre_change_parent_bounding_box;
186
187         if (bbox) {
188                 /* see the comment in Canvas::item_moved() to understand
189                  * why we use the parent's bounding box here.
190                  */
191                 pre_change_parent_bounding_box = item_to_parent (bbox.get());
192         }
193         
194         _position = p;
195
196         _canvas->item_moved (this, pre_change_parent_bounding_box);
197
198         if (_parent) {
199                 _parent->child_changed ();
200         }
201 }
202
203 void
204 Item::set_x_position (Coord x)
205 {
206         set_position (Duple (x, _position.y));
207 }
208
209 void
210 Item::set_y_position (Coord y)
211 {
212         set_position (Duple (_position.x, y));
213 }
214
215 void
216 Item::raise_to_top ()
217 {
218         assert (_parent);
219         _parent->raise_child_to_top (this);
220 }
221
222 void
223 Item::raise (int levels)
224 {
225         assert (_parent);
226         _parent->raise_child (this, levels);
227 }
228
229 void
230 Item::lower_to_bottom ()
231 {
232         assert (_parent);
233         _parent->lower_child_to_bottom (this);
234 }
235
236 void
237 Item::hide ()
238 {
239         _visible = false;
240         _canvas->item_shown_or_hidden (this);
241 }
242
243 void
244 Item::show ()
245 {
246         _visible = true;
247         _canvas->item_shown_or_hidden (this);
248 }
249
250 Duple
251 Item::item_to_parent (Duple const & d) const
252 {
253         return d.translate (_position);
254 }
255
256 Duple
257 Item::parent_to_item (Duple const & d) const
258 {
259         return d.translate (- _position);
260 }
261
262 ArdourCanvas::Rect
263 Item::parent_to_item (ArdourCanvas::Rect const & d) const
264 {
265         return d.translate (- _position);
266 }
267
268 void
269 Item::unparent ()
270 {
271         _parent = 0;
272 }
273
274 void
275 Item::reparent (Group* new_parent)
276 {
277         assert (_canvas == _parent->canvas());
278
279         if (_parent) {
280                 _parent->remove (this);
281         }
282
283         assert (new_parent);
284
285         _parent = new_parent;
286         _canvas = _parent->canvas ();
287         _parent->add (this);
288 }
289
290 bool
291 Item::common_ancestor_within (uint32_t limit, const Item& other) const
292 {
293         uint32_t d1 = depth();
294         uint32_t d2 = other.depth();
295         const Item* i1 = this;
296         const Item* i2 = &other;
297         
298         /* move towards root until we are at the same level
299            for both items
300         */
301
302         while (d1 != d2) {
303                 if (d1 > d2) {
304                         i1 = i1->parent();
305                         d1--;
306                         limit--;
307                 } else {
308                         i2 = i2->parent();
309                         d2--;
310                         limit--;
311                 }
312                 if (limit == 0) {
313                         return false;
314                 }
315         }
316
317         /* now see if there is a common parent */
318
319         while (i1 != i2) {
320                 if (i1) {
321                         i1 = i1->parent();
322                 }
323                 if (i2) {
324                         i2 = i2->parent ();
325                 }
326
327                 limit--;
328                 if (limit == 0) {
329                         return false;
330                 }
331         }
332         
333         return true;
334 }
335
336 const Item*
337 Item::closest_ancestor_with (const Item& other) const
338 {
339         uint32_t d1 = depth();
340         uint32_t d2 = other.depth();
341         const Item* i1 = this;
342         const Item* i2 = &other;
343
344         /* move towards root until we are at the same level
345            for both items
346         */
347
348         while (d1 != d2) {
349                 if (d1 > d2) {
350                         i1 = i1->parent();
351                         d1--;
352                 } else {
353                         i2 = i2->parent();
354                         d2--;
355                 }
356         }
357
358         /* now see if there is a common parent */
359
360         while (i1 != i2) {
361                 if (i1) {
362                         i1 = i1->parent();
363                 }
364                 if (i2) {
365                         i2 = i2->parent ();
366                 }
367         }
368         
369         return i1;
370 }
371
372 bool
373 Item::is_descendant_of (const Item& candidate) const
374 {
375         Item const * i = _parent;
376
377         while (i) {
378                 if (i == &candidate) {
379                         return true;
380                 }
381                 i = i->parent();
382         }
383
384         return false;
385 }
386
387 void
388 Item::grab_focus ()
389 {
390         /* XXX */
391 }
392
393 /** @return Bounding box in this item's coordinates */
394 boost::optional<ArdourCanvas::Rect>
395 Item::bounding_box () const
396 {
397         if (_bounding_box_dirty) {
398                 compute_bounding_box ();
399         }
400
401         assert (!_bounding_box_dirty);
402         return _bounding_box;
403 }
404
405 Coord
406 Item::height () const 
407 {
408         boost::optional<ArdourCanvas::Rect> bb  = bounding_box();
409
410         if (bb) {
411                 return bb->height ();
412         }
413         return 0;
414 }
415
416 Coord
417 Item::width () const 
418 {
419         boost::optional<ArdourCanvas::Rect> bb = bounding_box().get();
420
421         if (bb) {
422                 return bb->width ();
423         }
424
425         return 0;
426 }
427
428 void
429 Item::redraw () const
430 {
431         if (_visible && _bounding_box && _canvas) {
432                 _canvas->request_redraw (item_to_canvas (_bounding_box.get()));
433         }
434 }       
435
436 void
437 Item::begin_change ()
438 {
439         _pre_change_bounding_box = bounding_box ();
440 }
441
442 void
443 Item::end_change ()
444 {
445         _canvas->item_changed (this, _pre_change_bounding_box);
446         
447         if (_parent) {
448                 _parent->child_changed ();
449         }
450 }
451
452 void
453 Item::begin_visual_change ()
454 {
455 }
456
457 void
458 Item::end_visual_change ()
459 {
460         _canvas->item_visual_property_changed (this);
461 }
462
463 void
464 Item::move (Duple movement)
465 {
466         set_position (position() + movement);
467 }
468
469 void
470 Item::grab ()
471 {
472         assert (_canvas);
473         _canvas->grab (this);
474 }
475
476 void
477 Item::ungrab ()
478 {
479         assert (_canvas);
480         _canvas->ungrab ();
481 }
482
483 void
484 Item::set_data (string const & key, void* data)
485 {
486         _data[key] = data;
487 }
488
489 void *
490 Item::get_data (string const & key) const
491 {
492         map<string, void*>::const_iterator i = _data.find (key);
493         if (i == _data.end ()) {
494                 return 0;
495         }
496         
497         return i->second;
498 }
499
500 void
501 Item::set_ignore_events (bool ignore)
502 {
503         _ignore_events = ignore;
504 }
505
506 void
507 Item::dump (ostream& o) const
508 {
509         boost::optional<ArdourCanvas::Rect> bb = bounding_box();
510
511         o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
512         o << " @ " << position();
513         
514 #ifdef CANVAS_DEBUG
515         if (!name.empty()) {
516                 o << ' ' << name;
517         }
518 #endif
519
520         if (bb) {
521                 o << endl << _canvas->indent() << "\tbbox: " << bb.get();
522                 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
523         } else {
524                 o << " bbox unset";
525         }
526
527         o << endl;
528 }
529
530 std::string
531 Item::whatami () const 
532 {
533         std::string type = demangle (typeid (*this).name());
534         return type.substr (type.find_last_of (':') + 1);
535 }
536
537 uint32_t
538 Item::depth () const
539 {
540         Item* i = _parent;
541         int d = 0;
542         while (i) {
543                 ++d;
544                 i = i->parent();
545         }
546         return d;
547 }
548
549 bool
550 Item::covers (Duple const & point) const
551 {
552         Duple p = canvas_to_item (point);
553
554         if (_bounding_box_dirty) {
555                 compute_bounding_box ();
556         }
557
558         boost::optional<Rect> r = bounding_box();
559
560         if (!r) {
561                 return false;
562         }
563
564         return r.get().contains (p);
565 }
566
567 ostream&
568 ArdourCanvas::operator<< (ostream& o, const Item& i)
569 {
570         i.dump (o);
571         return o;
572 }
573