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