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