2009-12-03 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / src / dirty.cpp
blob00571dccc503195d92949960784eea27dc846cb8
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"
25 #include "deployment.h"
27 class DirtyNode : public List::Node {
28 public:
29 DirtyNode (UIElement *element)
31 this->element = element;
33 UIElement *element;
36 class DirtyList : public List::Node
38 public:
39 DirtyList (int level)
41 this->level = level;
42 dirty_list = new List();
45 ~DirtyList ()
47 delete dirty_list;
50 List* GetDirtyNodes ()
52 return dirty_list;
55 int GetLevel ()
57 return level;
59 private:
60 List *dirty_list;
61 int level;
64 DirtyLists::DirtyLists (bool ascending)
66 this->ascending = ascending;
67 this->lists = new List();
70 DirtyLists::~DirtyLists ()
72 delete lists;
75 DirtyList*
76 DirtyLists::GetList (int level, bool create)
78 DirtyList *dl;
80 for (dl = (DirtyList*)lists->First(); dl; dl = (DirtyList*)dl->next) {
81 if (dl->GetLevel() == level)
82 return dl;
83 else if (dl->GetLevel() > level)
84 break;
87 if (create) {
88 DirtyList *new_dl = new DirtyList (level);
89 lists->InsertBefore (new_dl, dl);
90 return new_dl;
93 return NULL;
96 void
97 DirtyLists::RemoveList (int level)
99 DirtyList *dl = (DirtyList*)GetList (level, false);
100 if (!dl)
101 return;
102 lists->Remove (dl);
105 void
106 DirtyLists::AddDirtyNode (int level, List::Node *node)
108 DirtyList *dl = (DirtyList*)GetList (level, true);
109 dl->GetDirtyNodes()->Append(node);
112 void
113 DirtyLists::RemoveDirtyNode (int level, List::Node *node)
115 DirtyList *dl = (DirtyList*)GetList (level, false);
116 if (!dl)
117 return;
118 dl->GetDirtyNodes()->Remove(node);
119 if (dl->GetDirtyNodes()->IsEmpty())
120 lists->Remove (dl);
123 List::Node*
124 DirtyLists::GetFirst ()
126 DirtyList *dl;
128 if (ascending) {
129 dl = (DirtyList*)lists->First();
131 else {
132 dl = (DirtyList*)lists->Last ();
135 if (!dl)
136 return NULL;
138 return dl->GetDirtyNodes()->First();
141 bool
142 DirtyLists::IsEmpty ()
144 return lists->IsEmpty();
147 void
148 DirtyLists::Clear (bool freeNodes)
150 lists->Clear (freeNodes);
153 void
154 Surface::AddDirtyElement (UIElement *element, DirtyType dirt)
156 // there's no point in adding an element to the dirty list if it
157 // isn't in the hierarchy. it will be added to the dirty list when
158 // it's added to the hierarchy anyway.
159 if (element->GetVisualParent() == NULL && !IsTopLevel(element))
160 return;
162 // XXX this should really be here...
163 // if (element->dirty_flags & dirt)
164 // return;
166 element->dirty_flags |= dirt;
168 //printf ("adding element %p (%s) to the dirty list\n", element, element->GetTypeName());
170 if (dirt & DownDirtyState) {
171 if (element->down_dirty_node)
172 return;
173 element->down_dirty_node = new DirtyNode (element);
175 down_dirty->AddDirtyNode (element->GetVisualLevel (), element->down_dirty_node);
178 if (dirt & UpDirtyState) {
179 if (element->up_dirty_node)
180 return;
181 element->up_dirty_node = new DirtyNode (element);
183 up_dirty->AddDirtyNode (element->GetVisualLevel (), element->up_dirty_node);
187 void
188 Surface::RemoveDirtyElement (UIElement *element)
190 if (element->up_dirty_node)
191 up_dirty->RemoveDirtyNode (element->GetVisualLevel(), element->up_dirty_node);
192 if (element->down_dirty_node)
193 down_dirty->RemoveDirtyNode (element->GetVisualLevel(), element->down_dirty_node);
195 element->down_dirty_node = NULL;
196 element->up_dirty_node = NULL;
201 ** There are 2 types of changes that need to propagate around the
202 ** tree.
204 ** 1. Those changes that needs to be propagated from parent to children
205 ** (transformation, opacity). We call these Downward Changes, and
206 ** the elements are placed in the down_dirty list.
208 ** 2. Those changes that need to be propagated from children to parent
209 ** (bounds, invalidation). We call these Upward Changes, and the
210 ** elements are placed in the up_dirty list.
213 ** Downward Changes can result in new Upward changes (when an
214 ** element's transform changes, usually its bounds change), so when
215 ** processing the dirty list we push changes down the tree before
216 ** handling the Upward Changes.
220 void
221 Surface::PropagateDirtyFlagToChildren (UIElement *el, DirtyType flags)
223 VisualTreeWalker walker = VisualTreeWalker (el);
224 while (UIElement *child = walker.Step ())
225 AddDirtyElement (child, flags);
228 void
229 Surface::ProcessDownDirtyElements ()
231 /* push down the transforms opacity, and visibility changes first */
232 while (DirtyNode *node = (DirtyNode*)down_dirty->GetFirst()) {
233 UIElement* el = (UIElement*)node->element;
235 if (el->dirty_flags & DirtyRenderVisibility) {
236 el->dirty_flags &= ~DirtyRenderVisibility;
238 el->UpdateBounds ();
240 // Since we are not included in our parents subtree when we
241 // are collapsed we need to notify our parent that things may
242 // have changed
243 if (el->GetVisualParent ())
244 el->GetVisualParent ()->UpdateBounds ();
246 el->ComputeTotalRenderVisibility ();
248 if (!el->GetRenderVisible ())
249 el->CacheInvalidateHint ();
251 AddDirtyElement (el, DirtyNewBounds);
253 PropagateDirtyFlagToChildren (el, DirtyRenderVisibility);
256 if (el->dirty_flags & DirtyHitTestVisibility) {
257 el->dirty_flags &= ~DirtyHitTestVisibility;
259 el->ComputeTotalHitTestVisibility ();
261 PropagateDirtyFlagToChildren (el, DirtyHitTestVisibility);
264 ** since we cache N's local (from N's parent to N)
265 ** transform, we need to catch if we're changing
266 ** something about that local transform and recompute
267 ** it.
269 ** DirtyLocalTransform implies DirtyTransform, since
270 ** changing N's local transform requires updating the
271 ** transform of all descendents in the subtree rooted
272 ** at N.
274 if (el->dirty_flags & DirtyLocalTransform) {
275 el->dirty_flags &= ~DirtyLocalTransform;
277 el->dirty_flags |= DirtyTransform;
279 el->ComputeLocalTransform ();
282 if (el->dirty_flags & DirtyTransform) {
283 el->dirty_flags &= ~DirtyTransform;
285 el->Invalidate ();
286 el->ComputeTransform ();
288 if (el->GetVisualParent ())
289 el->GetVisualParent ()->UpdateBounds ();
291 AddDirtyElement (el, DirtyNewBounds);
292 PropagateDirtyFlagToChildren (el, DirtyTransform);
295 if (el->dirty_flags & DirtyLocalClip) {
296 el->dirty_flags &= ~DirtyLocalClip;
297 el->dirty_flags |= DirtyClip;
299 // XXX el->ComputeLocalClip ();
302 if (el->dirty_flags & DirtyClip) {
303 el->dirty_flags &= ~DirtyTransform;
305 // XXX el->ComputeClip ();
306 // XXX el->UpdateBounds ();
308 PropagateDirtyFlagToChildren (el, DirtyClip);
311 if (el->dirty_flags & DirtyChildrenZIndices) {
312 el->dirty_flags &= ~DirtyChildrenZIndices;
313 if (!el->Is(Type::PANEL)) {
314 g_warning ("DirtyChildrenZIndices is only applicable to Panel subclasses");
316 else {
317 ((Panel*)el)->GetChildren ()->ResortByZIndex();
322 if (!(el->dirty_flags & DownDirtyState)) {
323 down_dirty->RemoveDirtyNode (el->GetVisualLevel (), el->down_dirty_node);
324 el->down_dirty_node = NULL;
328 if (!down_dirty->IsEmpty())
329 g_warning ("after down dirty pass, down dirty list is not empty");
333 ** Note that since this calls GDK invalidation functions
334 ** it's a good idea to call it with a GDK lock held (all gtk callbacks
335 ** are automatically protected except for timeouts and idle)
337 void
338 Surface::ProcessUpDirtyElements ()
340 while (DirtyNode *node = (DirtyNode*)up_dirty->GetFirst()) {
341 UIElement* el = (UIElement*)node->element;
343 // printf ("up processing element element %p (%s)\n", el, el->GetName());
344 // printf ("el->parent = %p\n", el->parent);
346 if (el->dirty_flags & DirtyBounds) {
347 // printf (" + bounds\n");
348 el->dirty_flags &= ~DirtyBounds;
350 Rect obounds = el->GetBounds ();
351 Rect osubtreebounds = el->GetSubtreeBounds ();
352 bool parent_bounds_updated = false;
354 el->ComputeBounds ();
356 // printf (" + + obounds = %f %f %f %f, nbounds = %f %f %f %f\n",
357 // obounds.x, obounds.y, obounds.w, obounds.h,
358 // el->GetBounds().x, el->GetBounds().y, el->GetBounds().w, el->GetBounds().h);
360 if (osubtreebounds != el->GetSubtreeBounds ()) {
361 if (el->GetVisualParent ()) {
362 el->GetVisualParent ()->UpdateBounds ();
363 parent_bounds_updated = true;
367 if (obounds != el->GetBounds()) {
368 if (el->GetVisualParent ()) {
369 // printf (" + + + calling UpdateBounds and Invalidate on parent\n");
370 if (!parent_bounds_updated)
371 el->GetVisualParent ()->UpdateBounds();
373 Region oregion = Region (obounds);
374 el->GetVisualParent ()->Invalidate (&oregion);
376 el->Invalidate ();
379 if (el->force_invalidate_of_new_bounds) {
380 el->force_invalidate_of_new_bounds = false;
381 // Invalidate everything including the
382 // visible area of our children.
383 el->InvalidateSubtreePaint ();
386 if (el->dirty_flags & DirtyNewBounds) {
387 el->Invalidate ();
388 el->dirty_flags &= ~DirtyNewBounds;
390 if (el->dirty_flags & DirtyInvalidate) {
391 // printf (" + invalidating %p (%s) %s, %f %f %f %f\n",
392 // el, el->GetTypeName(), el->GetName(), el->dirty_rect.x, el->dirty_rect.y, el->dirty_rect.w, el->dirty_rect.h);
394 el->dirty_flags &= ~DirtyInvalidate;
396 Region *dirty = el->dirty_region;
398 GdkRectangle *rects;
399 int count;
400 dirty->GetRectangles (&rects, &count);
401 Surface *surface = el->GetDeployment ()->GetSurface ();
402 if (el->IsAttached ()) {
403 while (count--) {
404 Rect r = Rect ((double)rects [count].x,
405 (double)rects [count].y,
406 (double)rects [count].width,
407 (double)rects [count].height);
408 //printf (" + + invalidating parent (%f,%f,%f,%f)\n",
409 // r.x,
410 // r.y,
411 // r.w,
412 // r.h);
414 surface->Invalidate (r);
416 g_free (rects);
419 delete el->dirty_region;
420 el->dirty_region = new Region ();
423 if (!(el->dirty_flags & UpDirtyState)) {
424 up_dirty->RemoveDirtyNode (el->GetVisualLevel (), el->up_dirty_node);
425 el->up_dirty_node = NULL;
429 if (!up_dirty->IsEmpty())
430 g_warning ("after up dirty pass, up dirty list is not empty");
433 void
434 Surface::UpdateLayout ()
436 for (int i = 0; i < layers->GetCount (); i++) {
437 UIElement *layer = layers->GetValueAt (i)->AsUIElement ();
438 if (!layer->HasFlag (UIElement::DIRTY_MEASURE_HINT) && !layer->HasFlag (UIElement::DIRTY_ARRANGE_HINT))
439 continue;
441 // This is a hack to make sure the elements understand the currnet
442 // size of the surface until it is moved to a proper location.
443 Size *last = LayoutInformation::GetPreviousConstraint (layer);
444 Size available = Size (active_window->GetWidth (), active_window->GetHeight ());
445 if (layer->IsContainer () && (!last || (*last != available))) {
446 layer->InvalidateMeasure ();
447 LayoutInformation::SetPreviousConstraint (layer, &available);
450 layer->UpdateLayout ();
454 bool
455 Surface::ProcessDirtyElements ()
457 UpdateLayout ();
458 bool dirty = down_dirty->IsEmpty() || !up_dirty->IsEmpty();
459 ProcessDownDirtyElements ();
460 ProcessUpDirtyElements ();
461 return dirty;