add the 2.1-bootstrap dir to MONO_PATH when running smcs
[moon.git] / src / dirty.cpp
blobbfcee8169bf55c0028ca9eec79062fd4dc317f5d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * dirty.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2007 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include <glib.h>
17 #include "canvas.h"
18 #include "uielement.h"
19 #include "panel.h"
20 #include "control.h"
21 #include "runtime.h"
22 #include "clock.h"
23 #include "dirty.h"
24 #include "list.h"
26 class DirtyNode : public List::Node {
27 public:
28 DirtyNode (UIElement *element)
30 this->element = element;
32 UIElement *element;
35 class DirtyList : public List::Node
37 public:
38 DirtyList (int level)
40 this->level = level;
41 dirty_list = new List();
44 ~DirtyList ()
46 delete dirty_list;
49 List* GetDirtyNodes ()
51 return dirty_list;
54 int GetLevel ()
56 return level;
58 private:
59 List *dirty_list;
60 int level;
63 DirtyLists::DirtyLists (bool ascending)
65 this->ascending = ascending;
66 this->lists = new List();
69 DirtyLists::~DirtyLists ()
71 delete lists;
74 DirtyList*
75 DirtyLists::GetList (int level, bool create)
77 DirtyList *dl;
79 for (dl = (DirtyList*)lists->First(); dl; dl = (DirtyList*)dl->next) {
80 if (dl->GetLevel() == level)
81 return dl;
82 else if (dl->GetLevel() > level)
83 break;
86 if (create) {
87 DirtyList *new_dl = new DirtyList (level);
88 lists->InsertBefore (new_dl, dl);
89 return new_dl;
92 return NULL;
95 void
96 DirtyLists::RemoveList (int level)
98 DirtyList *dl = (DirtyList*)GetList (level, false);
99 if (!dl)
100 return;
101 lists->Remove (dl);
104 void
105 DirtyLists::AddDirtyNode (int level, List::Node *node)
107 DirtyList *dl = (DirtyList*)GetList (level, true);
108 dl->GetDirtyNodes()->Append(node);
111 void
112 DirtyLists::RemoveDirtyNode (int level, List::Node *node)
114 DirtyList *dl = (DirtyList*)GetList (level, false);
115 if (!dl)
116 return;
117 dl->GetDirtyNodes()->Remove(node);
118 if (dl->GetDirtyNodes()->IsEmpty())
119 lists->Remove (dl);
122 List::Node*
123 DirtyLists::GetFirst ()
125 DirtyList *dl;
127 if (ascending) {
128 dl = (DirtyList*)lists->First();
130 else {
131 dl = (DirtyList*)lists->Last ();
134 if (!dl)
135 return NULL;
137 return dl->GetDirtyNodes()->First();
140 bool
141 DirtyLists::IsEmpty ()
143 return lists->IsEmpty();
146 void
147 DirtyLists::Clear (bool freeNodes)
149 lists->Clear (freeNodes);
152 void
153 Surface::AddDirtyElement (UIElement *element, DirtyType dirt)
155 // there's no point in adding an element to the dirty list if it
156 // isn't in the hierarchy. it will be added to the dirty list when
157 // it's added to the hierarchy anyway.
158 if (element->GetVisualParent() == NULL && !IsTopLevel(element))
159 return;
161 // XXX this should really be here...
162 // if (element->dirty_flags & dirt)
163 // return;
165 element->dirty_flags |= dirt;
167 //printf ("adding element %p (%s) to the dirty list\n", element, element->GetTypeName());
169 if (dirt & DownDirtyState) {
170 if (element->down_dirty_node)
171 return;
172 element->down_dirty_node = new DirtyNode (element);
174 down_dirty->AddDirtyNode (element->GetVisualLevel (), element->down_dirty_node);
177 if (dirt & UpDirtyState) {
178 if (element->up_dirty_node)
179 return;
180 element->up_dirty_node = new DirtyNode (element);
182 up_dirty->AddDirtyNode (element->GetVisualLevel (), element->up_dirty_node);
186 void
187 Surface::RemoveDirtyElement (UIElement *element)
189 if (element->up_dirty_node)
190 up_dirty->RemoveDirtyNode (element->GetVisualLevel(), element->up_dirty_node);
191 if (element->down_dirty_node)
192 down_dirty->RemoveDirtyNode (element->GetVisualLevel(), element->down_dirty_node);
194 element->down_dirty_node = NULL;
195 element->up_dirty_node = NULL;
200 ** There are 2 types of changes that need to propagate around the
201 ** tree.
203 ** 1. Those changes that needs to be propagated from parent to children
204 ** (transformation, opacity). We call these Downward Changes, and
205 ** the elements are placed in the down_dirty list.
207 ** 2. Those changes that need to be propagated from children to parent
208 ** (bounds, invalidation). We call these Upward Changes, and the
209 ** elements are placed in the up_dirty list.
212 ** Downward Changes can result in new Upward changes (when an
213 ** element's transform changes, usually its bounds change), so when
214 ** processing the dirty list we push changes down the tree before
215 ** handling the Upward Changes.
219 void
220 Surface::PropagateDirtyFlagToChildren (UIElement *el, DirtyType flags)
222 VisualTreeWalker walker = VisualTreeWalker (el);
223 while (UIElement *child = walker.Step ())
224 AddDirtyElement (child, flags);
227 void
228 Surface::ProcessDownDirtyElements ()
230 /* push down the transforms opacity, and visibility changes first */
231 while (DirtyNode *node = (DirtyNode*)down_dirty->GetFirst()) {
232 UIElement* el = (UIElement*)node->element;
234 if (el->dirty_flags & DirtyRenderVisibility) {
235 el->dirty_flags &= ~DirtyRenderVisibility;
237 el->UpdateBounds ();
239 // Since we are not included in our parents subtree when we
240 // are collapsed we need to notify our parent that things may
241 // have changed
242 if (el->GetVisualParent ())
243 el->GetVisualParent ()->UpdateBounds ();
245 el->ComputeTotalRenderVisibility ();
247 if (!el->GetRenderVisible ())
248 el->CacheInvalidateHint ();
250 AddDirtyElement (el, DirtyNewBounds);
252 PropagateDirtyFlagToChildren (el, DirtyRenderVisibility);
255 if (el->dirty_flags & DirtyHitTestVisibility) {
256 el->dirty_flags &= ~DirtyHitTestVisibility;
258 el->ComputeTotalHitTestVisibility ();
260 PropagateDirtyFlagToChildren (el, DirtyHitTestVisibility);
263 ** since we cache N's local (from N's parent to N)
264 ** transform, we need to catch if we're changing
265 ** something about that local transform and recompute
266 ** it.
268 ** DirtyLocalTransform implies DirtyTransform, since
269 ** changing N's local transform requires updating the
270 ** transform of all descendents in the subtree rooted
271 ** at N.
273 if (el->dirty_flags & DirtyLocalTransform) {
274 el->dirty_flags &= ~DirtyLocalTransform;
276 el->dirty_flags |= DirtyTransform;
278 el->ComputeLocalTransform ();
281 if (el->dirty_flags & DirtyTransform) {
282 el->dirty_flags &= ~DirtyTransform;
284 el->Invalidate ();
285 el->ComputeTransform ();
287 if (el->GetVisualParent ())
288 el->GetVisualParent ()->UpdateBounds ();
290 AddDirtyElement (el, DirtyNewBounds);
291 PropagateDirtyFlagToChildren (el, DirtyTransform);
294 if (el->dirty_flags & DirtyLocalClip) {
295 el->dirty_flags &= ~DirtyLocalClip;
296 el->dirty_flags |= DirtyClip;
298 // XXX el->ComputeLocalClip ();
301 if (el->dirty_flags & DirtyClip) {
302 el->dirty_flags &= ~DirtyTransform;
304 // XXX el->ComputeClip ();
305 // XXX el->UpdateBounds ();
307 PropagateDirtyFlagToChildren (el, DirtyClip);
310 if (el->dirty_flags & DirtyChildrenZIndices) {
311 el->dirty_flags &= ~DirtyChildrenZIndices;
312 if (!el->Is(Type::PANEL)) {
313 g_warning ("DirtyChildrenZIndices is only applicable to Panel subclasses");
315 else {
316 ((Panel*)el)->GetChildren ()->ResortByZIndex();
321 if (!(el->dirty_flags & DownDirtyState)) {
322 down_dirty->RemoveDirtyNode (el->GetVisualLevel (), el->down_dirty_node);
323 el->down_dirty_node = NULL;
327 if (!down_dirty->IsEmpty())
328 g_warning ("after down dirty pass, down dirty list is not empty");
332 ** Note that since this calls GDK invalidation functions
333 ** it's a good idea to call it with a GDK lock held (all gtk callbacks
334 ** are automatically protected except for timeouts and idle)
336 void
337 Surface::ProcessUpDirtyElements ()
339 while (DirtyNode *node = (DirtyNode*)up_dirty->GetFirst()) {
340 UIElement* el = (UIElement*)node->element;
342 // printf ("up processing element element %p (%s)\n", el, el->GetName());
343 // printf ("el->parent = %p\n", el->parent);
345 if (el->dirty_flags & DirtyBounds) {
346 // printf (" + bounds\n");
347 el->dirty_flags &= ~DirtyBounds;
349 Rect obounds = el->GetBounds ();
350 Rect osubtreebounds = el->GetSubtreeBounds ();
351 bool parent_bounds_updated = false;
353 el->ComputeBounds ();
355 // printf (" + + obounds = %f %f %f %f, nbounds = %f %f %f %f\n",
356 // obounds.x, obounds.y, obounds.w, obounds.h,
357 // el->GetBounds().x, el->GetBounds().y, el->GetBounds().w, el->GetBounds().h);
359 if (osubtreebounds != el->GetSubtreeBounds ()) {
360 if (el->GetVisualParent ()) {
361 el->GetVisualParent ()->UpdateBounds ();
362 parent_bounds_updated = true;
366 if (obounds != el->GetBounds()) {
367 if (el->GetVisualParent ()) {
368 // printf (" + + + calling UpdateBounds and Invalidate on parent\n");
369 if (!parent_bounds_updated)
370 el->GetVisualParent ()->UpdateBounds();
372 Region oregion = Region (obounds);
373 el->GetVisualParent ()->Invalidate (&oregion);
375 el->Invalidate ();
378 if (el->force_invalidate_of_new_bounds) {
379 el->force_invalidate_of_new_bounds = false;
380 // Invalidate everything including the
381 // visible area of our children.
382 el->InvalidateSubtreePaint ();
385 if (el->dirty_flags & DirtyNewBounds) {
386 el->Invalidate ();
387 el->dirty_flags &= ~DirtyNewBounds;
389 if (el->dirty_flags & DirtyInvalidate) {
390 // printf (" + invalidating %p (%s) %s, %f %f %f %f\n",
391 // el, el->GetTypeName(), el->GetName(), el->dirty_rect.x, el->dirty_rect.y, el->dirty_rect.w, el->dirty_rect.h);
393 el->dirty_flags &= ~DirtyInvalidate;
395 Region *dirty = el->dirty_region;
397 GdkRectangle *rects;
398 int count;
399 dirty->GetRectangles (&rects, &count);
400 Surface *surface = el->GetSurface ();
401 if (surface) {
402 while (count--) {
403 Rect r = Rect ((double)rects [count].x,
404 (double)rects [count].y,
405 (double)rects [count].width,
406 (double)rects [count].height);
407 //printf (" + + invalidating parent (%f,%f,%f,%f)\n",
408 // r.x,
409 // r.y,
410 // r.w,
411 // r.h);
413 surface->Invalidate (r);
415 g_free (rects);
418 delete el->dirty_region;
419 el->dirty_region = new Region ();
422 if (!(el->dirty_flags & UpDirtyState)) {
423 up_dirty->RemoveDirtyNode (el->GetVisualLevel (), el->up_dirty_node);
424 el->up_dirty_node = NULL;
428 if (!up_dirty->IsEmpty())
429 g_warning ("after up dirty pass, up dirty list is not empty");
432 void
433 Surface::UpdateLayout ()
435 if (!needs_measure && !needs_arrange)
436 return;
438 needs_measure = needs_arrange = false;
440 for (int i = 0; i < layers->GetCount (); i++) {
441 UIElement *layer = layers->GetValueAt (i)->AsUIElement ();
443 // This is a hack to make sure the elements understand the currnet
444 // size of the surface until it is moved to a proper location.
445 Size *last = LayoutInformation::GetPreviousConstraint (layer);
446 Size available = Size (active_window->GetWidth (), active_window->GetHeight ());
447 if (!last || (*last != available)) {
448 layer->InvalidateMeasure ();
449 Size size(active_window->GetWidth (), active_window->GetHeight ());
450 LayoutInformation::SetPreviousConstraint (layer, &size);
453 layer->UpdateLayout ();
457 bool
458 Surface::ProcessDirtyElements ()
460 UpdateLayout ();
461 bool dirty = down_dirty->IsEmpty() || !up_dirty->IsEmpty();
462 ProcessDownDirtyElements ();
463 ProcessUpDirtyElements ();
464 return dirty;