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