change Canvas heirarchy and constructors
[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/canvas.h"
27 #include "canvas/debug.h"
28 #include "canvas/group.h"
29 #include "canvas/item.h"
30 #include "canvas/scroll_group.h"
31
32 using namespace std;
33 using namespace PBD;
34 using namespace ArdourCanvas;
35
36 Item::Item (Canvas* canvas)
37         : Fill (*this)
38         , Outline (*this)
39         ,  _canvas (canvas)
40         , _parent (0)
41         , _visible (true)
42         , _bounding_box_dirty (true)
43         , _ignore_events (false)
44 {
45         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
46 }       
47
48 Item::Item (Group* parent)
49         : Fill (*this)
50         , Outline (*this)
51         ,  _canvas (parent->canvas())
52         , _parent (parent)
53         , _visible (true)
54         , _bounding_box_dirty (true)
55         , _ignore_events (false)
56 {
57         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
58
59         if (parent) {
60                 _parent->add (this);
61         }
62
63         find_scroll_parent ();
64 }       
65
66 Item::Item (Group* parent, Duple const& p)
67         : Fill (*this)
68         , Outline (*this)
69         ,  _canvas (parent->canvas())
70         , _parent (parent)
71         , _position (p)
72         , _visible (true)
73         , _bounding_box_dirty (true)
74         , _ignore_events (false)
75 {
76         DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
77
78         if (parent) {
79                 _parent->add (this);
80         }
81
82         find_scroll_parent ();
83
84 }       
85
86 Item::~Item ()
87 {
88         if (_parent) {
89                 _parent->remove (this);
90         }
91
92         if (_canvas) {
93                 _canvas->item_going_away (this, _bounding_box);
94         }
95 }
96
97 Duple
98 Item::canvas_origin () const
99 {
100         return item_to_canvas (Duple (0,0));
101 }
102
103 Duple
104 Item::window_origin () const 
105 {
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.
109         */
110         if (_parent) {
111                 return _parent->item_to_window (_position);
112         } else {
113                 return _position;
114         }
115 }
116
117 ArdourCanvas::Rect
118 Item::item_to_parent (ArdourCanvas::Rect const & r) const
119 {
120         return r.translate (_position);
121 }
122
123 Duple
124 Item::scroll_offset () const
125 {
126         if (_scroll_parent) {
127                 return _scroll_parent->scroll_offset();
128         } 
129         return Duple (0,0);
130 }
131
132 Duple
133 Item::position_offset() const
134 {
135         Item const * i = this;
136         Duple offset;
137
138         while (i) {
139                 offset = offset.translate (i->position());
140                 i = i->parent();
141         }
142
143         return offset;
144 }
145
146 ArdourCanvas::Rect
147 Item::item_to_canvas (ArdourCanvas::Rect const & r) const
148 {
149         return r.translate (position_offset());
150 }
151
152 ArdourCanvas::Duple
153 Item::item_to_canvas (ArdourCanvas::Duple const & d) const
154 {
155         return d.translate (position_offset());
156 }
157
158 ArdourCanvas::Duple
159 Item::canvas_to_item (ArdourCanvas::Duple const & r) const
160 {
161         return r.translate (-position_offset());
162 }
163
164 ArdourCanvas::Rect
165 Item::canvas_to_item (ArdourCanvas::Rect const & r) const
166 {
167         return r.translate (-position_offset());
168 }
169
170 void
171 Item::item_to_canvas (Coord& x, Coord& y) const
172 {
173         Duple d = item_to_canvas (Duple (x, y));
174                 
175         x = d.x;
176         y = d.y;
177 }
178
179 void
180 Item::canvas_to_item (Coord& x, Coord& y) const
181 {
182         Duple d = canvas_to_item (Duple (x, y));
183
184         x = d.x;
185         y = d.y;
186 }
187
188
189 Duple
190 Item::item_to_window (ArdourCanvas::Duple const & d, bool rounded) const
191 {
192         Duple ret = item_to_canvas (d).translate (-scroll_offset());
193
194         if (rounded) {
195                 ret.x = round (ret.x);
196                 ret.y = round (ret.y);
197         }
198
199         return ret;
200 }
201
202 Duple
203 Item::window_to_item (ArdourCanvas::Duple const & d) const
204 {
205         return canvas_to_item (d.translate (scroll_offset()));
206 }
207
208 ArdourCanvas::Rect
209 Item::item_to_window (ArdourCanvas::Rect const & r) const
210 {
211         Rect ret = item_to_canvas (r).translate (-scroll_offset());
212
213         ret.x0 = round (ret.x0);
214         ret.x1 = round (ret.x1);
215         ret.y0 = round (ret.y0);
216         ret.y1 = round (ret.y1);
217
218         return ret;
219 }
220
221 ArdourCanvas::Rect
222 Item::window_to_item (ArdourCanvas::Rect const & r) const
223 {
224         return canvas_to_item (r.translate (scroll_offset()));
225 }
226
227 /** Set the position of this item in the parent's coordinates */
228 void
229 Item::set_position (Duple p)
230 {
231         if (p == _position) {
232                 return;
233         }
234
235         boost::optional<ArdourCanvas::Rect> bbox = bounding_box ();
236         boost::optional<ArdourCanvas::Rect> pre_change_parent_bounding_box;
237
238         if (bbox) {
239                 /* see the comment in Canvas::item_moved() to understand
240                  * why we use the parent's bounding box here.
241                  */
242                 pre_change_parent_bounding_box = item_to_parent (bbox.get());
243         }
244         
245         _position = p;
246
247         _canvas->item_moved (this, pre_change_parent_bounding_box);
248
249         if (_parent) {
250                 _parent->child_changed ();
251         }
252 }
253
254 void
255 Item::set_x_position (Coord x)
256 {
257         set_position (Duple (x, _position.y));
258 }
259
260 void
261 Item::set_y_position (Coord y)
262 {
263         set_position (Duple (_position.x, y));
264 }
265
266 void
267 Item::raise_to_top ()
268 {
269         if (_parent) {
270                 _parent->raise_child_to_top (this);
271         }
272 }
273
274 void
275 Item::raise (int levels)
276 {
277         if (_parent) {
278                 _parent->raise_child (this, levels);
279         }
280 }
281
282 void
283 Item::lower_to_bottom ()
284 {
285         if (_parent) {
286                 _parent->lower_child_to_bottom (this);
287         }
288 }
289
290 void
291 Item::hide ()
292 {
293         if (_visible) {
294                 _visible = false;
295                 _canvas->item_shown_or_hidden (this);
296         }
297 }
298
299 void
300 Item::show ()
301 {
302         if (!_visible) {
303                 _visible = true;
304                 _canvas->item_shown_or_hidden (this);
305         }
306 }
307
308 Duple
309 Item::item_to_parent (Duple const & d) const
310 {
311         return d.translate (_position);
312 }
313
314 Duple
315 Item::parent_to_item (Duple const & d) const
316 {
317         return d.translate (- _position);
318 }
319
320 ArdourCanvas::Rect
321 Item::parent_to_item (ArdourCanvas::Rect const & d) const
322 {
323         return d.translate (- _position);
324 }
325
326 void
327 Item::unparent ()
328 {
329         _parent = 0;
330         _scroll_parent = 0;
331 }
332
333 void
334 Item::reparent (Group* new_parent)
335 {
336         if (new_parent == _parent) {
337                 return;
338         }
339
340         assert (_canvas == new_parent->canvas());
341
342         if (_parent) {
343                 _parent->remove (this);
344         }
345
346         assert (new_parent);
347
348         _parent = new_parent;
349         _canvas = _parent->canvas ();
350
351         find_scroll_parent ();
352
353         _parent->add (this);
354 }
355
356 void
357 Item::find_scroll_parent ()
358 {
359         Item const * i = this;
360         ScrollGroup const * last_scroll_group = 0;
361
362         /* Don't allow a scroll group to find itself as its own scroll parent
363          */
364
365         i = i->parent ();
366
367         while (i) {
368                 ScrollGroup const * sg = dynamic_cast<ScrollGroup const *> (i);
369                 if (sg) {
370                         last_scroll_group = sg;
371                 }
372                 i = i->parent();
373         }
374         
375         _scroll_parent = const_cast<ScrollGroup*> (last_scroll_group);
376 }
377
378 bool
379 Item::common_ancestor_within (uint32_t limit, const Item& other) const
380 {
381         uint32_t d1 = depth();
382         uint32_t d2 = other.depth();
383         const Item* i1 = this;
384         const Item* i2 = &other;
385         
386         /* move towards root until we are at the same level
387            for both items
388         */
389
390         while (d1 != d2) {
391                 if (d1 > d2) {
392                         if (!i1) {
393                                 return false;
394                         }
395                         i1 = i1->parent();
396                         d1--;
397                         limit--;
398                 } else {
399                         if (!i2) {
400                                 return false;
401                         }
402                         i2 = i2->parent();
403                         d2--;
404                         limit--;
405                 }
406                 if (limit == 0) {
407                         return false;
408                 }
409         }
410
411         /* now see if there is a common parent */
412
413         while (i1 != i2) {
414                 if (i1) {
415                         i1 = i1->parent();
416                 }
417                 if (i2) {
418                         i2 = i2->parent ();
419                 }
420
421                 limit--;
422                 if (limit == 0) {
423                         return false;
424                 }
425         }
426         
427         return true;
428 }
429
430 const Item*
431 Item::closest_ancestor_with (const Item& other) const
432 {
433         uint32_t d1 = depth();
434         uint32_t d2 = other.depth();
435         const Item* i1 = this;
436         const Item* i2 = &other;
437
438         /* move towards root until we are at the same level
439            for both items
440         */
441
442         while (d1 != d2) {
443                 if (d1 > d2) {
444                         if (!i1) {
445                                 return 0;
446                         }
447                         i1 = i1->parent();
448                         d1--;
449                 } else {
450                         if (!i2) {
451                                 return 0;
452                         }
453                         i2 = i2->parent();
454                         d2--;
455                 }
456         }
457
458         /* now see if there is a common parent */
459
460         while (i1 != i2) {
461                 if (i1) {
462                         i1 = i1->parent();
463                 }
464                 if (i2) {
465                         i2 = i2->parent ();
466                 }
467         }
468         
469         return i1;
470 }
471
472 bool
473 Item::is_descendant_of (const Item& candidate) const
474 {
475         Item const * i = _parent;
476
477         while (i) {
478                 if (i == &candidate) {
479                         return true;
480                 }
481                 i = i->parent();
482         }
483
484         return false;
485 }
486
487 void
488 Item::grab_focus ()
489 {
490         /* XXX */
491 }
492
493 /** @return Bounding box in this item's coordinates */
494 boost::optional<ArdourCanvas::Rect>
495 Item::bounding_box () const
496 {
497         if (_bounding_box_dirty) {
498                 compute_bounding_box ();
499                 assert (!_bounding_box_dirty);
500         }
501
502         return _bounding_box;
503 }
504
505 Coord
506 Item::height () const 
507 {
508         boost::optional<ArdourCanvas::Rect> bb  = bounding_box();
509
510         if (bb) {
511                 return bb->height ();
512         }
513         return 0;
514 }
515
516 Coord
517 Item::width () const 
518 {
519         boost::optional<ArdourCanvas::Rect> bb = bounding_box().get();
520
521         if (bb) {
522                 return bb->width ();
523         }
524
525         return 0;
526 }
527
528 void
529 Item::redraw () const
530 {
531         if (_visible && _bounding_box && _canvas) {
532                 _canvas->request_redraw (item_to_window (_bounding_box.get()));
533         }
534 }       
535
536 void
537 Item::begin_change ()
538 {
539         _pre_change_bounding_box = bounding_box ();
540 }
541
542 void
543 Item::end_change ()
544 {
545         _canvas->item_changed (this, _pre_change_bounding_box);
546         
547         if (_parent) {
548                 _parent->child_changed ();
549         }
550 }
551
552 void
553 Item::begin_visual_change ()
554 {
555 }
556
557 void
558 Item::end_visual_change ()
559 {
560         _canvas->item_visual_property_changed (this);
561 }
562
563 void
564 Item::move (Duple movement)
565 {
566         set_position (position() + movement);
567 }
568
569 void
570 Item::grab ()
571 {
572         assert (_canvas);
573         _canvas->grab (this);
574 }
575
576 void
577 Item::ungrab ()
578 {
579         assert (_canvas);
580         _canvas->ungrab ();
581 }
582
583 void
584 Item::set_data (string const & key, void* data)
585 {
586         _data[key] = data;
587 }
588
589 void *
590 Item::get_data (string const & key) const
591 {
592         map<string, void*>::const_iterator i = _data.find (key);
593         if (i == _data.end ()) {
594                 return 0;
595         }
596         
597         return i->second;
598 }
599
600 void
601 Item::set_ignore_events (bool ignore)
602 {
603         _ignore_events = ignore;
604 }
605
606 void
607 Item::dump (ostream& o) const
608 {
609         boost::optional<ArdourCanvas::Rect> bb = bounding_box();
610
611         o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
612         o << " @ " << position();
613         
614 #ifdef CANVAS_DEBUG
615         if (!name.empty()) {
616                 o << ' ' << name;
617         }
618 #endif
619
620         if (bb) {
621                 o << endl << _canvas->indent() << "\tbbox: " << bb.get();
622                 o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
623         } else {
624                 o << " bbox unset";
625         }
626
627         o << endl;
628 }
629
630 std::string
631 Item::whatami () const 
632 {
633         std::string type = demangle (typeid (*this).name());
634         return type.substr (type.find_last_of (':') + 1);
635 }
636
637 uint32_t
638 Item::depth () const
639 {
640         Item* i = _parent;
641         int d = 0;
642         while (i) {
643                 ++d;
644                 i = i->parent();
645         }
646         return d;
647 }
648
649 bool
650 Item::covers (Duple const & point) const
651 {
652         Duple p = window_to_item (point);
653
654         if (_bounding_box_dirty) {
655                 compute_bounding_box ();
656         }
657
658         boost::optional<Rect> r = bounding_box();
659
660         if (!r) {
661                 return false;
662         }
663
664         return r.get().contains (p);
665 }
666
667 ostream&
668 ArdourCanvas::operator<< (ostream& o, const Item& i)
669 {
670         i.dump (o);
671         return o;
672 }
673