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