remove all xml++.h inclusion by canvas implementations
[ardour.git] / libs / canvas / group.cc
1 #include <iostream>
2 #include <cairomm/context.h>
3
4 #include "pbd/stacktrace.h"
5 #include "pbd/compose.h"
6
7 #include "canvas/group.h"
8 #include "canvas/types.h"
9 #include "canvas/debug.h"
10 #include "canvas/item.h"
11 #include "canvas/canvas.h"
12
13 using namespace std;
14 using namespace ArdourCanvas;
15
16 int Group::default_items_per_cell = 64;
17
18
19 Group::Group (Canvas* canvas)
20         : Item (canvas)
21         , _lut (0)
22 {
23         
24 }
25
26 Group::Group (Group* parent)
27         : Item (parent)
28         , _lut (0)
29 {
30         
31 }
32
33 Group::Group (Group* parent, Duple position)
34         : Item (parent, position)
35         , _lut (0)
36 {
37         
38 }
39
40 Group::~Group ()
41 {
42         for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ++i) {
43                 (*i)->unparent ();
44         }
45
46         _items.clear ();
47 }
48
49 /** @param area Area to draw in this group's coordinates.
50  *  @param context Context, set up with its origin at this group's position.
51  */
52 void
53 Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
54 {
55         ensure_lut ();
56         vector<Item*> items = _lut->get (area);
57
58         ++render_depth;
59                 
60 #ifdef CANVAS_DEBUG
61         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
62                 cerr << string_compose ("%1GROUP %2 render %3 items out of %4\n", 
63                                         _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size());
64         }
65 #endif
66
67         for (vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
68
69                 if (!(*i)->visible ()) {
70 #ifdef CANVAS_DEBUG
71                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
72                                 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
73                         }
74 #endif
75                         continue;
76                 }
77                 
78                 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
79
80                 if (!item_bbox) {
81 #ifdef CANVAS_DEBUG
82                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
83                                 cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
84                         }
85 #endif
86                         continue;
87                 }
88
89                 /* convert the render area to our child's coordinates */
90                 Rect const item_area = (*i)->parent_to_item (area);
91
92                 /* intersect the child's render area with the child's bounding box */
93                 boost::optional<Rect> r = item_bbox.get().intersection (item_area);
94
95                 if (r) {
96                         /* render the intersection */
97                         context->save ();
98                         context->translate ((*i)->position().x, (*i)->position().y);
99 #ifdef CANVAS_DEBUG
100                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
101                                 cerr << string_compose ("%1render %2 %3\n", _canvas->render_indent(), (*i)->whatami(),
102                                                         (*i)->name);
103                         }
104 #endif
105                         (*i)->render (r.get(), context);
106                         ++render_count;
107                         context->restore ();
108                 } else {
109 #ifdef CANVAS_DEBUG
110                         if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
111                                 cerr << string_compose ("%1skip render of %2 %3, no intersection\n", _canvas->render_indent(), (*i)->whatami(),
112                                                         (*i)->name);
113                         }
114 #endif
115                 }
116         }
117
118         --render_depth;
119 }
120
121 void
122 Group::compute_bounding_box () const
123 {
124         Rect bbox;
125         bool have_one = false;
126
127         for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
128                 boost::optional<Rect> item_bbox = (*i)->bounding_box ();
129                 if (!item_bbox) {
130                         continue;
131                 }
132
133                 Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
134                 if (have_one) {
135                         bbox = bbox.extend (group_bbox);
136                 } else {
137                         bbox = group_bbox;
138                         have_one = true;
139                 }
140         }
141
142         if (!have_one) {
143                 _bounding_box = boost::optional<Rect> ();
144         } else {
145                 _bounding_box = bbox;
146         }
147
148         _bounding_box_dirty = false;
149 }
150
151 void
152 Group::add (Item* i)
153 {
154         _items.push_back (i);
155         invalidate_lut ();
156         _bounding_box_dirty = true;
157         
158         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: group add\n");
159 }
160
161 void
162 Group::remove (Item* i)
163 {
164         _items.remove (i);
165         invalidate_lut ();
166         _bounding_box_dirty = true;
167         
168         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: group remove\n");
169 }
170
171 void
172 Group::raise_child_to_top (Item* i)
173 {
174         _items.remove (i);
175         _items.push_back (i);
176         invalidate_lut ();
177 }
178
179 void
180 Group::raise_child (Item* i, int levels)
181 {
182         list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
183         assert (j != _items.end ());
184
185         ++j;
186         _items.remove (i);
187
188         while (levels > 0 && j != _items.end ()) {
189                 ++j;
190                 --levels;
191         }
192
193         _items.insert (j, i);
194         invalidate_lut ();
195 }
196
197 void
198 Group::lower_child_to_bottom (Item* i)
199 {
200         _items.remove (i);
201         _items.push_front (i);
202         invalidate_lut ();
203 }
204
205 void
206 Group::ensure_lut () const
207 {
208         if (!_lut) {
209                 _lut = new DumbLookupTable (*this);
210         }
211 }
212
213 void
214 Group::invalidate_lut () const
215 {
216         delete _lut;
217         _lut = 0;
218 }
219
220 void
221 Group::child_changed ()
222 {
223         invalidate_lut ();
224         _bounding_box_dirty = true;
225
226         if (_parent) {
227                 _parent->child_changed ();
228         }
229 }
230
231 void
232 Group::add_items_at_point (Duple const point, vector<Item const *>& items) const
233 {
234         boost::optional<Rect> const bbox = bounding_box ();
235
236         if (!bbox || !bbox.get().contains (point)) {
237                 return;
238         }
239
240         Item::add_items_at_point (point, items);
241         
242         ensure_lut ();
243         
244         vector<Item*> our_items = _lut->items_at_point (point);
245         for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
246                 (*i)->add_items_at_point (point - (*i)->position(), items);
247         }
248 }
249
250 void
251 Group::dump (ostream& o) const
252 {
253         o << _canvas->indent();
254         o << "Group " << this << " [" << name << ']';
255         o << " @ " << position();
256         o << " Items: " << _items.size();
257         o << " Visible ? " << _visible;
258
259         boost::optional<Rect> bb = bounding_box();
260
261         if (bb) {
262                 o << endl << _canvas->indent() << "  bbox: " << bb.get();
263                 o << endl << _canvas->indent() << "  CANVAS bbox: " << item_to_canvas (bb.get());
264         } else {
265                 o << "  bbox unset";
266         }
267
268         o << endl;
269
270         ArdourCanvas::dump_depth++;
271
272         for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
273                 o << **i;
274         }
275
276         ArdourCanvas::dump_depth--;
277 }