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