FileDocCategorySizeDatePackage
CanvasManager.javaAPI DocphoneME MR2 API (J2ME)18031Wed May 02 18:00:36 BST 2007com.sun.perseus.model

CanvasManager

public class CanvasManager extends SimpleCanvasManager

The CanvasManager class is responsible for keeping the rendering of a ModelNode tree on a RenderGraphics current.

Specifically, the CanvasManager listens to update events in a ModelNode tree and triggers repaint into the RenderGraphics when necessary.

The CanvasManager optimizes rendering of the tree while the document is in loading phase.

version
$Id: CanvasManager.java,v 1.17 2006/07/13 00:55:57 st125089 Exp $

Fields Summary
protected boolean
loading
True while the component is processing a document which is in the loading phase, i.e., between the UpdateListener's loadStarting and loadComplete calls.
protected ModelNode
progressiveNode
Progressive painting is needed when a node has started loading and has been inserted into the tree. This is only used during the loading phase of a document when doing progressive rendering. The next node to paint progressively
protected ModelNode
needLoadNode
Tracks the highest level node whose load completion is needed to proceed with progressive rendering. When loading this node completes, then the node is painted.
protected SMILSample
sampler
The associated SMILSampler, if animations are run.
protected long
smilRate
The rate for SMIL animation. The smilRate is the minimum time between SMIL samples.
Constructors Summary
public CanvasManager(com.sun.perseus.j2d.RenderGraphics rg, DocumentNode documentNode, CanvasUpdateListener canvasUpdateListener)

param
rg the RenderGraphics which this instance will keep up to date with the model changes.
param
documentNode the DocumentNode, root of the tree that this CanvasManager will draw and keep current on the RenderGraphics
param
canvasUpdateListener the CanvasUpdateListener which listens to completed updates on the associated RenderGraphics
throws
IllegalArgumentException if rg, documentNode or listener is null.


                                                                                                               
       
                           
                            
        super(rg, documentNode, canvasUpdateListener);
    
Methods Summary
protected voidfullNodePaint(ModelNode node)
Utility method invoked when a node and its children need to be painted. This is used, for example, when a node which requires full loading before rendering is finally fully loaded.

param
node the node to paint fully, i.e, including its children.

        if (node.canRenderState == 0) {
            synchronized (lock) {
                if (!canvasConsumed) {
                    try {
                        lock.wait();
                    } catch (InterruptedException ie) {
                    }
                }
                node.paint(rg);
                canvasConsumed = false;
                canvasUpdateListener.updateComplete(this);
            }
        }
    
public SMILSamplegetSampler()

return
the associated SMILSampler, if animations are run.

        return sampler;
    
booleanisNeedLoadNodeOrChild(ModelNode node)

param
node the node to test.
return
true if node is a chid of the node currently holding up progressive rendering. The caller must make sure needNodeLoad is not null before calling this utility method. If called when needLoadNode is null, the method returns true.

        ModelNode parent = node;

        while (parent != null) {
            if (parent == needLoadNode) {
                break;
            }
            parent = parent.parent;
        }

        if (parent == null) {
            return false;
        }

        return true;
    
public voidloadBegun(ModelNode node)
Invoked when the input node has started loading

param
node the ModelNode for which loading has started.

        updateCanvas();
    
public voidloadComplete(ModelNode node)
Invoked when the input node has finished loading.

param
node the node for which loading is complete.

        // System.err.println(">>>>>>>>>>>>>> loadComplete : " + node);
        if (node instanceof DocumentNode) {
            // We are finished with the loading phase.
            // Progressive rendering can stop
            loading = false;
            canvasUpdateListener.initialLoadComplete(null);

            // At this point, we are ready to start the animation loop.
            // We set the document's scheduled Runnable.

            // IMPL NOTE : We disable animations if there are no initial animations.
            // We should really only sample when there are 
            // active animations, but animations can be added by scripts, so
            // we will need a more sophisticated mechanism.
            if (documentNode.updateQueue != null 
                && 
                documentNode.timeContainerRootSupport
                .timedElementChildren.size() > 0) {
                SMILSample.DocumentWallClock clock 
                    = new SMILSample.DocumentWallClock(documentNode);
                sampler 
                    = new SMILSample(documentNode,
                                     clock);
                documentNode.updateQueue.scheduleAtFixedRate(sampler, this, 
                                                             smilRate);
                documentNode.timeContainerRootSupport.initialize();
                clock.start();
            }
        } else if (node == needLoadNode) {
            // We loaded a node fully. We can now display that
            // node and its children and proceed with progressive
            // rendering
            if (progressiveNode != null) {
                throw new Error();
            }
            progressiveNode = node;
            needLoadNode = null;
        }
        updateCanvas();
    
public voidloadStarting(DocumentNode documentNode, java.io.InputStream is)
Invoked when the document starts loading

param
documentNode the DocumentNode for which loading is starting
param
is the InputStream from which SVG content is loaded.

        loading = true;
    
public voidloadingFailed(DocumentNode documentNode, java.lang.Exception error)
Invoked when a document error happened before finishing loading.

param
documentNode the DocumentNode for which loading has failed.
param
error the exception which describes the reason why loading failed.

        loading = false;
        canvasUpdateListener.initialLoadComplete(error);
    
public voidmodifiedNode(ModelNode node)
Invoked when a node modification completed.

param
node the node which was just modified.

        if (!loading) {
            if (!needRepaint 
                &&
                (node.hasNodeRendering() 
                 || 
                 node.hasDescendants())) {
                needRepaint = true;
            }
        } else {
            // Ignore modifications on nodes which have no 
            // rendering and no descendants
            if (!node.hasNodeRendering() && !node.hasDescendants()) {
                return;
            }

            // We are doing progressive rendering. Check if
            // the modified node is the one currently suspended
            // or one of its children.
            // Modifications will be picked up when we 
            // paint the node after it has finished 
            // loading.
            if (needLoadNode != null) {
                if (node == needLoadNode) {
                    return;
                } else {
                    ModelNode parent = node.parent;
                    while (parent != null) {
                        if (parent == needLoadNode) {
                            return;
                        }
                        parent = parent.parent;
                    }
                    needRepaint = true;
                }
            } else {
                if (!needRepaint) {
                    // We modified a node which did not have node 
                    // rendering. 
                    if (progressiveNode != null && progressiveNode != node) {
                        needRepaint = true;
                    }
                    progressiveNode = node;
                }
            }
        }
    
public voidmodifyingNode(ModelNode node)
Invoked when a node is about to be modified.

param
node the node which is about to be modified

        if (!isNeedLoadNodeOrChild(node)
            &&
            ((node.hasNodeRendering() || node.hasDescendants())
             && (node.canRenderState == 0))) {
            needRepaint = true;
        }
    
public voidnodeInserted(ModelNode node)
Invoked when a node has been inserted into the tree

param
node the newly inserted node

        if (loading) {
            if (needLoadNode == null) {
                // Progressive rendering is _not_ suspended

                // If this node's parent is already loaded,
                // it means we are dealing with a node insertion
                // resulting from reference resolution. We need
                // to repaint the document in its current state.
                if (node.parent.loaded) {
                    fullPaint();
                } else {
                    // Check if this node suspends progressive
                    // rendering.
                    if (!node.getPaintNeedsLoad()) {
                        if (progressiveNode != null) {
                            needRepaint = true;
                        } else {
                            progressiveNode = node;
                        }
                    } else {
                        needLoadNode = node;
                    }
                }
            } else {
                // Progressive rendering _is_ suspended

                // We are loading a document and progressive 
                // repaint is disabled. However, the newly 
                // inserted node might be a ElementNodeProxy
                // child of a Use element which is referencing
                // content under the current needLoadNode. 
                // In that situation, we need to do a repaint
                // of the document up to, but not including
                // the needLoadNode.
                ModelNode parent = node;
                while (parent != null) {
                    if (parent == needLoadNode) {
                        // We are under the disabled node, no
                        // problem
                        break;
                    }
                    parent = parent.parent;
                }
                if (parent == null) {
                    // Re-render the document up to the current
                    // needLoadNode
                    needRepaint = true;
                }
            }
        } else {
            needRepaint = true;
        }
    
protected voidpartialPaint(ModelNode node)
Utility method to paint the input tree up to, but not including the needLoadNode. This is a recursive method which should be called with the root of the tree to be painted.

param
node the node to paint next.

        if (node == needLoadNode
            ||
            (node.canRenderState != 0)) {
            return;
        }
        
        if (node.hasNodeRendering()) {
            synchronized (lock) {
                node.paint(rg);
            }
        } else {
            ModelNode child = node.getFirstExpandedChild();
            while (child != null) {
                partialPaint(child);
                child = child.nextSibling;
            }
            
            child = node.getFirstChildNode();
            while (child != null) {
                partialPaint(child);
                child = child.nextSibling;
            }
        }
    
protected voidprogressivePaint(ModelNode node)
Utility method invoked when an incremental painting is needed on a node. This may be invoked when a node was just inserted into the tree or when a node which required full loading of its children has been completely loaded.

param
node the node to paint incrementally on the canvas

        // If this node already has children, we need to do a fullNodePaint.
        // This happens for the <use> element when the <use> references
        // an element which appeared before in the document.
        if (node.hasDescendants()) {
            fullNodePaint(node);
        } else if (node.hasNodeRendering()
                   && (node.canRenderState == 0)) {
            synchronized (lock) {
                if (!canvasConsumed) {
                    try {
                        lock.wait();
                    } catch (InterruptedException ie) {
                    }
                }
                node.paint(rg);
                canvasConsumed = false;
                canvasUpdateListener.updateComplete(this);
            }
        }
    
public voidtextInserted(ModelNode node)
Invoked when a string has been appended, during a load phase. This is only used when parsing a document and is used in support of progressive download, like the other loadXXX methods.

param
node the ModelNode on which text has been inserted.

    
public voidupdateCanvas()
Utility method used to update the canvas appropriately depending on what is needed. During the loading phase, while we do progressive rendering, the canvas will only redraw nodes in the progressiveNodes list, unless a repaint has been requested. Important Note: this method should only be called from the update thread, i.e., the thread that also manages the model node tree.

        if (!loading) {
            if (needRepaint) {
                if (canvasConsumed) {
                    fullPaint();
                    needRepaint = false;
                } else {
                    // There is a request to update the canvas
                    // (likely after a Runnable was invoked),
                    // but the last update was not consumed.
                    // If there is a Runnable in the RunnableQueue,
                    // we just skip this rendering update. Otherwise,
                    // schedule a fake Runnable to force a later repaint.
                    if (documentNode.getUpdateQueue().getSize() == 0) {
                        documentNode.getUpdateQueue()
                            .preemptLater(new Runnable() {
                                    public void run() {
                                    }
                                }, this);
                    }
                }
            } 
        } else {
            if (needRepaint) {
                // A full repaint was requested. If there is no
                // suspended node, just do a full repaint.
                // Otherwise, do a partial paint
                if (needLoadNode == null) {
                    fullPaint();
                } else {
                    partialPaint(documentNode);
                    canvasUpdateListener.updateComplete(this);
                }
            } else if (progressiveNode != null) {
                progressivePaint(progressiveNode);
            }
            needRepaint = false;
            progressiveNode = null;
        }