0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026:
0027: package com.sun.perseus.model;
0028:
0029: import com.sun.perseus.j2d.Box;
0030: import com.sun.perseus.j2d.Path;
0031: import com.sun.perseus.j2d.RenderGraphics;
0032: import com.sun.perseus.j2d.Tile;
0033: import com.sun.perseus.j2d.Transform;
0034:
0035: import org.w3c.dom.DOMException;
0036:
0037: import org.w3c.dom.svg.SVGRect;
0038: import org.w3c.dom.svg.SVGMatrix;
0039:
0040: import org.w3c.dom.events.EventListener;
0041: import org.w3c.dom.events.EventTarget;
0042:
0043: /**
0044: * The central structure manipulated in Perseus is called the <em>Model</em>. It
0045: * is used for the in-memory representation of an SVG Tiny document structure.
0046: *
0047: * <p>The <code>ModelNode</code> abstraction represents an atomic node in the
0048: * <b><code>Model</code></b>'s tree structure. </p>
0049: *
0050: * <p>A <code>ModelNode</code> may have regular children (which can be added on
0051: * <code>CompositeNode</code> implementations, for example), and expanded
0052: * children (which are automatically computed by the node, for example on
0053: * <code>Text</code> or on <code>Use</code>). In rendering order, expanded
0054: * content comes second.</p>
0055: *
0056: * <p>A <code>ModelNode</code> can have a parent, which may be null, but it
0057: * always belongs to a <code>DocumentNode</code> instance which is guaranteed to
0058: * no be null.</p>
0059: *
0060: * <p>A <code>ModelNode</code> can be painted into a
0061: * @link {com.sun.perseus.j2d.RenderGraphics RenderGraphics}.
0062: *
0063: * <p>A <code>ModelNode</code> is subjet to interactivity and its
0064: * @link {#nodeHitAt nodeHitAt} method can be used to perform hit detection.</p>
0065: *
0066: * @version $Id: ModelNode.java,v 1.24 2006/06/29 10:47:33 ln156897 Exp $
0067: */
0068: public abstract class ModelNode implements EventTarget {
0069: /**
0070: * Used in cases the parent node is null.
0071: */
0072: protected final Transform IDENTITY = new Transform(null);
0073:
0074: /**
0075: * The parent node
0076: */
0077: protected ModelNode parent;
0078:
0079: /**
0080: * The next sibling
0081: */
0082: protected ModelNode nextSibling;
0083:
0084: /**
0085: * The next sibling
0086: */
0087: protected ModelNode prevSibling;
0088:
0089: /**
0090: * Mask for the renderable bit.
0091: */
0092: public static final int CAN_RENDER_RENDERABLE_MASK = ~0x1;
0093:
0094: /**
0095: * Mask for the required features bit
0096: */
0097: public static final int CAN_RENDER_REQUIRED_FEATURES_MASK = ~(1 << 1);
0098:
0099: /**
0100: * Mask for the required extensions bit
0101: */
0102: public static final int CAN_RENDER_REQUIRED_EXTENSIONS_MASK = ~(1 << 2);
0103:
0104: /**
0105: * Mask for the required languages bit
0106: */
0107: public static final int CAN_RENDER_SYSTEM_LANGUAGE_MASK = ~(1 << 3);
0108:
0109: /**
0110: * Mask for conditions met, i.e., requiredFeatures, requiredExtensions,
0111: * and systemLanguage.
0112: */
0113: public static final int CAN_RENDER_CONDITIONS_MET_MASK = CAN_RENDER_REQUIRED_FEATURES_MASK
0114: & CAN_RENDER_REQUIRED_EXTENSIONS_MASK
0115: & CAN_RENDER_SYSTEM_LANGUAGE_MASK;
0116:
0117: /**
0118: * Mask defining which bits from the canRenderState value are copied over
0119: * to newly created proxies. See the description of the canRenderState
0120: * bits below.
0121: */
0122: public static final int CAN_RENDER_PROXY_BITS_MASK = 0x7EE;
0123: /**
0124: * Mask for the non-invertible transform bit
0125: */
0126: public static final int CAN_RENDER_NON_INVERTIBLE_TXF_MASK = ~(1 << 4);
0127:
0128: /**
0129: * Mask for the display bit
0130: */
0131: public static final int CAN_RENDER_DISPLAY_MASK = ~(1 << 5);
0132:
0133: /**
0134: * Mask for the zero-width bit
0135: */
0136: public static final int CAN_RENDER_ZERO_WIDTH_MASK = ~(1 << 6);
0137:
0138: /**
0139: * Mask for the zero-height bit
0140: */
0141: public static final int CAN_RENDER_ZERO_HEIGHT_MASK = ~(1 << 7);
0142:
0143: /**
0144: * Mask for the empty viewbox bit
0145: */
0146: public static final int CAN_RENDER_EMPTY_VIEWBOX_MASK = ~(1 << 8);
0147:
0148: /**
0149: * Mask for the empty path bit
0150: */
0151: public static final int CAN_RENDER_EMPTY_PATH_MASK = ~(1 << 9);
0152:
0153: /**
0154: * Mask for the zero font-size bit
0155: */
0156: public static final int CAN_RENDER_ZERO_FONT_SIZE_MASK = ~(1 << 10);
0157:
0158: /**
0159: * Mask for the in-document-tree bit
0160: */
0161: public static final int CAN_RENDER_IN_DOCUMENT_TREE_MASK = ~(1 << 11);
0162:
0163: /**
0164: * Mask for the paren canRenderState bit
0165: */
0166: public static final int CAN_RENDER_PARENT_STATE_MASK = ~(1 << 12);
0167:
0168: /**
0169: * Mask for the renderable bit.
0170: */
0171: public static final int CAN_RENDER_RENDERABLE_BIT = ~CAN_RENDER_RENDERABLE_MASK;
0172:
0173: /**
0174: * Mask for the required features bit
0175: */
0176: public static final int CAN_RENDER_REQUIRED_FEATURES_BIT = ~CAN_RENDER_REQUIRED_FEATURES_MASK;
0177:
0178: /**
0179: * Mask for the required extensions bit
0180: */
0181: public static final int CAN_RENDER_REQUIRED_EXTENSIONS_BIT = ~CAN_RENDER_REQUIRED_EXTENSIONS_MASK;
0182:
0183: /**
0184: * Mask for the system language bit
0185: */
0186: public static final int CAN_RENDER_SYSTEM_LANGUAGE_BIT = ~CAN_RENDER_SYSTEM_LANGUAGE_MASK;
0187:
0188: /**
0189: * Mask for the conditionsMet bits
0190: */
0191: public static final int CAN_RENDER_CONDITIONS_MET_BITS = ~CAN_RENDER_CONDITIONS_MET_MASK;
0192:
0193: /**
0194: * Mask for the non-invertible transform bit
0195: */
0196: public static final int CAN_RENDER_NON_INVERTIBLE_TXF_BIT = ~CAN_RENDER_NON_INVERTIBLE_TXF_MASK;
0197:
0198: /**
0199: * Mask for the display bit
0200: */
0201: public static final int CAN_RENDER_DISPLAY_BIT = ~CAN_RENDER_DISPLAY_MASK;
0202:
0203: /**
0204: * Mask for the zero-width bit
0205: */
0206: public static final int CAN_RENDER_ZERO_WIDTH_BIT = ~CAN_RENDER_ZERO_WIDTH_MASK;
0207:
0208: /**
0209: * Mask for the zero-height bit
0210: */
0211: public static final int CAN_RENDER_ZERO_HEIGHT_BIT = ~CAN_RENDER_ZERO_HEIGHT_MASK;
0212:
0213: /**
0214: * Mask for the empty viewbox bit
0215: */
0216: public static final int CAN_RENDER_EMPTY_VIEWBOX_BIT = ~CAN_RENDER_EMPTY_VIEWBOX_MASK;
0217:
0218: /**
0219: * Mask for the empty path bit
0220: */
0221: public static final int CAN_RENDER_EMPTY_PATH_BIT = ~CAN_RENDER_EMPTY_PATH_MASK;
0222:
0223: /**
0224: * Mask for the zero font-size bit
0225: */
0226: public static final int CAN_RENDER_ZERO_FONT_SIZE_BIT = ~CAN_RENDER_ZERO_FONT_SIZE_MASK;
0227:
0228: /**
0229: * Mask for the in document tree bit
0230: */
0231: public static final int CAN_RENDER_IN_DOCUMENT_TREE_BIT = ~CAN_RENDER_IN_DOCUMENT_TREE_MASK;
0232:
0233: /**
0234: * Mask for the parent cansRenderState bit
0235: */
0236: public static final int CAN_RENDER_PARENT_STATE_BIT = ~CAN_RENDER_PARENT_STATE_MASK;
0237:
0238: /**
0239: * Pre-computes whether or not this node can render. This is a bit-mask.
0240: *
0241: * 0 : renderable (i.e., initialized in the class constructor). All
0242: * classes.
0243: * 1 : required features. ElementNode
0244: * 2 : required extensions. ElementNode
0245: * 3 : required languages. ElementNode.
0246: * 4 : non-invertible transform. StructureNode, AbstractRenderingNode.
0247: * 5 : display. CompositeGraphicsNode.
0248: * 6 : width is zero. Ellipse, ImageNode, Rect, SVG, Viewport
0249: * 7 : height is zero. Ellipse, ImageNode, Rect, SVG, Viewport
0250: * 8 : viewBox width or height is zero or negative.
0251: * 9 : path is null or has no segments. ShapeNode.
0252: * 10 : font-size is negative or zero. Text.
0253: * 11 : node in document tree.
0254: * 12 : parent canRenderState. This 1 bit value is set if any of the bits
0255: * in the parent node's canRenderState is set.
0256: *
0257: * Only bits 1/2/3 are special in that they need to be applied from a node
0258: * to its proxies. Other bits (e.g., bit 5 (display)) do not need to be
0259: * propagated because they are set when the property changes on the node and
0260: * there is already propagation of property changes to the proxies.
0261: *
0262: * By default, ModelNodes are not renderable and are not in the document
0263: * tree.
0264: */
0265: protected int canRenderState = CAN_RENDER_RENDERABLE_BIT
0266: | CAN_RENDER_IN_DOCUMENT_TREE_BIT;
0267:
0268: /**
0269: * Used to track if this node is loaded or not. A node becomes loaded after
0270: * its <code>markLoaded</code> method is called. Note: the default is
0271: * <b>true</b> because when used through scripting, nodes are considered
0272: * fully loaded and operational. When used by the builder module or a
0273: * parser, the flag needs to be first turned off (see the
0274: * <code>setLoaded</code> method to control the progressive rendering
0275: * behavior (see the <code>CanvasManager</code> class).
0276: */
0277: protected boolean loaded = true;
0278:
0279: /**
0280: * The owner <code>DocumentNode</code>
0281: */
0282: protected DocumentNode ownerDocument;
0283:
0284: /**
0285: * By default, a node's bounding box is not accounted for. This is
0286: * overridden by children classes to specify when and under which condition
0287: * their bounding box should be accounted for.
0288: *
0289: * @return true if the node's bounding box should be accounted for.
0290: */
0291: protected boolean contributeBBox() {
0292: return false;
0293: }
0294:
0295: /**
0296: * @return the <code>UpdateListener</code> associated with the nearest
0297: * <code>DocumentNode</code> ancestor or null if there is no
0298: * <code>Viewporpt</code> ancestor
0299: */
0300: protected UpdateListener getUpdateListener() {
0301: //
0302: // It is important to _not_ use ownerDocument.getUpdateListener
0303: // because we do not report changes to elements which are not
0304: // hooked into the document tree.
0305: //
0306: // May be that should be changed in the future: we could also
0307: // always report updates to the listener and let it figure out
0308: // whether or not the reporting node is in the Document tree
0309: // or not yet.
0310: //
0311: if (parent == null) {
0312: return null;
0313: } else {
0314: return parent.getUpdateListener();
0315: }
0316: }
0317:
0318: /**
0319: * Returns the next sibling object of this object, or
0320: * <code>null</code> if this is the last sibling.
0321: *
0322: * @return the next sibling object.
0323: */
0324: public ModelNode getNextSiblingNode() {
0325: return nextSibling;
0326: }
0327:
0328: /**
0329: * Returns the previous sibling object of this object, or
0330: * <code>null</code> if this is the first child node and there
0331: * is no previous sibling.
0332: *
0333: * @return the next sibling object.
0334: */
0335: public ModelNode getPreviousSiblingNode() {
0336: return prevSibling;
0337: }
0338:
0339: /**
0340: * @return a reference to the parent <code>ModelNode</code>
0341: */
0342: public ModelNode getParent() {
0343: return parent;
0344: }
0345:
0346: /**
0347: * @param newParent this node's new parent
0348: */
0349: protected void setParent(final ModelNode newParent) {
0350: modifyingNode();
0351: setParentQuiet(newParent);
0352: nodeInserted(this );
0353: }
0354:
0355: /**
0356: * Sets this node's parent but does not generate change
0357: * events.
0358: *
0359: * @param newParent the node's new parent node.
0360: */
0361: protected void setParentQuiet(final ModelNode newParent) {
0362: if (parent == newParent) {
0363: return;
0364: }
0365:
0366: // If the new parent is not null, check whether or not
0367: // it is in the document tree. In all cases, we need
0368: // to unhook and then hook again.
0369: if (parent != null) {
0370: onUnhookedFromDocumentTree();
0371: }
0372:
0373: parent = newParent;
0374:
0375: // If the parent is not null and in the document tree,
0376: // we process that condition again.
0377: if (parent != null) {
0378: if (parent.isInDocumentTree()) {
0379: onHookedInDocumentTree();
0380: }
0381: }
0382: }
0383:
0384: /**
0385: * Utility method. Unhooks a node and all its following siblings,
0386: * quietly setting the parent and or proxy to null.
0387: *
0388: * @param node the root of the branch to unhook.
0389: */
0390: protected final void unhookQuiet(ModelNode node) {
0391: if (node != null) {
0392: if (node.prevSibling != null) {
0393: node.prevSibling.nextSibling = null;
0394: }
0395: }
0396:
0397: while (node != null) {
0398: node.setParentQuiet(null);
0399: if (node instanceof ElementNodeProxy) {
0400: ((ElementNodeProxy) node).setProxied(null);
0401: }
0402: node.unhookChildrenQuiet();
0403: node.unhookExpandedQuiet();
0404: node = node.nextSibling;
0405: }
0406: }
0407:
0408: /**
0409: * Utilitty method. Returns true if the input node is part of the
0410: * Document tree, i.e., if its top most ancestor is equal to its
0411: * ownerDocument.
0412: *
0413: * @return true if this node is a <code>DocumentNode</code> or if
0414: * one of its ancestors is a <code>DocumentNode</code>
0415: */
0416: protected boolean isInDocumentTree() {
0417: return ((canRenderState & CAN_RENDER_IN_DOCUMENT_TREE_BIT) == 0);
0418: }
0419:
0420: /**
0421: * Returns the <code>DocumentNode</code> this node is attached
0422: * to.
0423: *
0424: * @return the <code>DocumentNode</code> this node belongs to. The return
0425: * value should never be null.
0426: */
0427: public DocumentNode getOwnerDocument() {
0428: return ownerDocument;
0429: }
0430:
0431: /**
0432: * By default, this node returns true if there are no children.
0433: * <code>ModelNode</code> implementations with expanded content
0434: * should override this method.
0435: *
0436: * @return true if this node has, or may have children or expanded
0437: * children. A node may not know in advance if it has expanded
0438: * children because in some cases, the computation of expanded
0439: * content is done lazily. So this returns false if the node
0440: * does not have children and will never have expanded content.
0441: */
0442: public boolean hasDescendants() {
0443: return getFirstChildNode() != null;
0444: }
0445:
0446: /**
0447: * Paints the input node and all its siblings into the
0448: * <code>RenderGraphics</code>
0449: *
0450: * @param node the <code>ModelNode</code> to paint.
0451: * @param rg the <code>RenderGraphics</code> where the nodes should
0452: * be painted
0453: */
0454: static void paint(ModelNode node, final RenderGraphics rg) {
0455: while (node != null) {
0456: node.paint(rg);
0457: node = node.nextSibling;
0458: }
0459: }
0460:
0461: /**
0462: * @return the inherited value for the requested property.
0463: */
0464: protected final Object getInheritedPropertyState(
0465: final int propertyIndex) {
0466: if (parent == null) {
0467: return ownerDocument.getInitialPropertyState(propertyIndex);
0468: }
0469:
0470: return parent.getPropertyState(propertyIndex);
0471: }
0472:
0473: /**
0474: * @return the inherited value for the requested float property.
0475: */
0476: protected final float getInheritedFloatPropertyState(
0477: final int propertyIndex) {
0478: if (parent == null) {
0479: return ownerDocument
0480: .getInitialFloatPropertyState(propertyIndex);
0481: }
0482:
0483: return parent.getFloatPropertyState(propertyIndex);
0484: }
0485:
0486: /**
0487: * @return the inherited value for the requested packed property.
0488: */
0489: protected final int getInheritedPackedPropertyState(
0490: final int propertyIndex) {
0491: if (parent == null) {
0492: return ownerDocument
0493: .getInitialPackedPropertyState(propertyIndex);
0494: }
0495:
0496: return parent.getPackedPropertyState(propertyIndex);
0497: }
0498:
0499: /**
0500: * Returns the value for the given property.
0501: *
0502: * @return the value for the given property, null if the property is
0503: * unknown.
0504: */
0505: protected Object getPropertyState(final int propertyIndex) {
0506: return ownerDocument.getInitialPropertyState(propertyIndex);
0507: }
0508:
0509: /**
0510: * Returns the value for the given packed property.
0511: *
0512: * @return the value of the given property.
0513: */
0514: protected int getPackedPropertyState(final int propertyIndex) {
0515: return ownerDocument
0516: .getInitialPackedPropertyState(propertyIndex);
0517: }
0518:
0519: /**
0520: * Returns the value for the given float property.
0521: *
0522: * @return the value of the given property.
0523: */
0524: protected float getFloatPropertyState(final int propertyIndex) {
0525: return ownerDocument
0526: .getInitialFloatPropertyState(propertyIndex);
0527: }
0528:
0529: /**
0530: * Sets the computed value for the given property.
0531: *
0532: * @param propertyIndex the property index
0533: * @param propertyValue the computed value for the property.
0534: */
0535: protected void setPropertyState(final int propertyIndex,
0536: final Object propertyValue) {
0537: }
0538:
0539: /**
0540: * Sets the computed value for the given float property.
0541: *
0542: * @param propertyIndex the property index
0543: * @param propertyValue the computed value for the property.
0544: */
0545: protected void setFloatPropertyState(final int propertyIndex,
0546: final float propertyValue) {
0547: }
0548:
0549: /**
0550: * Sets the computed value for the given packed property.
0551: *
0552: * @param propertyIndex the property index
0553: * @param propertyValue the computed value for the property.
0554: */
0555: protected void setPackedPropertyState(final int propertyIndex,
0556: final int propertyValue) {
0557: }
0558:
0559: /**
0560: * Checks the state of the property value.
0561: *
0562: * @param propertyIndex the property index
0563: * @param propertyValue the computed value for the property.
0564: */
0565: protected boolean isPropertyState(final int propertyIndex,
0566: final Object propertyValue) {
0567: // By default, return true so that we don't bother setting an unknown
0568: // property.
0569: return true;
0570: }
0571:
0572: /**
0573: * Checks the state of the float property value.
0574: *
0575: * @param propertyIndex the property index
0576: * @param propertyValue the computed value for the property.
0577: */
0578: protected boolean isFloatPropertyState(final int propertyIndex,
0579: final float propertyValue) {
0580: // By default, return true so that we don't bother setting an unknown
0581: // property.
0582: return true;
0583: }
0584:
0585: /**
0586: * Checks the state of the packed property value.
0587: *
0588: * @param propertyIndex the property index
0589: * @param propertyValue the computed value for the property.
0590: */
0591: protected boolean isPackedPropertyState(final int propertyIndex,
0592: final int propertyValue) {
0593: // By default, return true so that we don't bother setting an unknown
0594: // property.
0595: return true;
0596: }
0597:
0598: /**
0599: * Recomputes the given property's state given the new parent property.
0600: * By default, this simply propagates to children.
0601: *
0602: * @param propertyIndex index for the property whose value is changing.
0603: * @param parentPropertyValue the value that children of this node should
0604: * now inherit.
0605: *
0606: */
0607: protected void recomputePropertyState(final int propertyIndex,
0608: final Object parentPropertyValue) {
0609: propagatePropertyState(propertyIndex, parentPropertyValue);
0610: }
0611:
0612: /**
0613: * Recomputes the given float property's state given the new parent
0614: * property. By default, this simply propagates to children.
0615: *
0616: * @param propertyIndex index for the property whose value is changing.
0617: * @param parentPropertyValue the value that children of this node should
0618: * now inherit.
0619: *
0620: */
0621: protected void recomputeFloatPropertyState(final int propertyIndex,
0622: final float parentPropertyValue) {
0623: propagateFloatPropertyState(propertyIndex, parentPropertyValue);
0624: }
0625:
0626: /**
0627: * Recomputes the given packed property's state given the new parent
0628: * property. By default, this simply propagates to children.
0629: *
0630: * @param propertyIndex index for the property whose value is changing.
0631: * @param parentPropertyValue the value that children of this node should
0632: * now inherit.
0633: *
0634: */
0635: protected void recomputePackedPropertyState(
0636: final int propertyIndex, final int parentPropertyValue) {
0637: propagatePackedPropertyState(propertyIndex, parentPropertyValue);
0638: }
0639:
0640: /**
0641: * Recomputes all inherited properties and propages the recomputation to
0642: * children.
0643: */
0644: void recomputeInheritedProperties() {
0645: ModelNode c = getFirstChildNode();
0646: while (c != null) {
0647: c.recomputeInheritedProperties();
0648: c = c.nextSibling;
0649: }
0650: }
0651:
0652: /**
0653: * Called when the canRenderState changes. If both values are zero, or
0654: * if both values are non zero, then children need to recompute their
0655: * canRenderState and propagate if need be.
0656: *
0657: * @param oldCanRenderState the old value for canRenderState.
0658: * @param newCanRenderState the new value for canRenderState
0659: */
0660: protected void propagateCanRenderState(final int oldCanRenderState,
0661: final int newCanRenderState) {
0662: if (oldCanRenderState != newCanRenderState
0663: && ((oldCanRenderState == 0) || (newCanRenderState == 0))) {
0664: if (newCanRenderState == 0) {
0665: // Clear the parent can render state bit and propagate.
0666: // Propagate to regular children.
0667: ModelNode node = getFirstChildNode();
0668: boolean nodeDisplay = false;
0669: int nodeOldState = 0;
0670: while (node != null) {
0671: nodeOldState = node.canRenderState;
0672: node.canRenderState &= CAN_RENDER_PARENT_STATE_MASK;
0673: node.propagateCanRenderState(nodeOldState,
0674: node.canRenderState);
0675: node = node.nextSibling;
0676: }
0677:
0678: // Propagate to expanded children.
0679: node = getFirstComputedExpandedChild();
0680: while (node != null) {
0681: nodeOldState = node.canRenderState;
0682: node.canRenderState &= CAN_RENDER_PARENT_STATE_MASK;
0683: node.propagateCanRenderState(nodeOldState,
0684: node.canRenderState);
0685: node = node.nextSibling;
0686: }
0687:
0688: } else {
0689: // Set the parent can render state bit and propagate.
0690: // Propagate to regular children.
0691: ModelNode node = getFirstChildNode();
0692: boolean nodeDisplay = false;
0693: int nodeOldState = 0;
0694: while (node != null) {
0695: nodeOldState = node.canRenderState;
0696: node.canRenderState |= CAN_RENDER_PARENT_STATE_BIT;
0697: node.propagateCanRenderState(nodeOldState,
0698: node.canRenderState);
0699: node = node.nextSibling;
0700: }
0701:
0702: // Propagate to expanded children.
0703: node = getFirstComputedExpandedChild();
0704: while (node != null) {
0705: nodeOldState = node.canRenderState;
0706: node.canRenderState |= CAN_RENDER_PARENT_STATE_BIT;
0707: node.propagateCanRenderState(nodeOldState,
0708: node.canRenderState);
0709: node = node.nextSibling;
0710: }
0711:
0712: }
0713: }
0714: }
0715:
0716: /**
0717: * Called when the computed value of the given property has changed.
0718: * By default, propagate both to regular content and expanded content.
0719: *
0720: * @param propertyIndex index for the property whose value has changed.
0721: * @param parentPropertyValue the value that children of this node should
0722: * now inherit.
0723: */
0724: protected void propagatePropertyState(final int propertyIndex,
0725: final Object parentPropertyValue) {
0726: // Propagate to regular children.
0727: ModelNode node = getFirstChildNode();
0728: while (node != null) {
0729: node.recomputePropertyState(propertyIndex,
0730: parentPropertyValue);
0731: node = node.nextSibling;
0732: }
0733:
0734: // Propagate to expanded children.
0735: node = getFirstExpandedChild();
0736: while (node != null) {
0737: node.recomputePropertyState(propertyIndex,
0738: parentPropertyValue);
0739: node = node.nextSibling;
0740: }
0741: }
0742:
0743: /**
0744: * Called when the computed value of the given float property has changed.
0745: * By default, propagate both to regular content and expanded content.
0746: *
0747: * @param propertyIndex index for the property whose value has changed.
0748: * @param parentPropertyValue the value that children of this node should
0749: * now inherit.
0750: */
0751: protected void propagateFloatPropertyState(final int propertyIndex,
0752: final float parentPropertyValue) {
0753: // Propagate to regular children.
0754: ModelNode node = getFirstChildNode();
0755: while (node != null) {
0756: node.recomputeFloatPropertyState(propertyIndex,
0757: parentPropertyValue);
0758: node = node.nextSibling;
0759: }
0760:
0761: // Propagate to expanded children.
0762: node = getFirstExpandedChild();
0763: while (node != null) {
0764: node.recomputeFloatPropertyState(propertyIndex,
0765: parentPropertyValue);
0766: node = node.nextSibling;
0767: }
0768: }
0769:
0770: /**
0771: * Called when the computed value of the given packed property has changed.
0772: * By default, propagate both to regular content and expanded content.
0773: *
0774: * @param propertyIndex index for the property whose value has changed.
0775: * @param parentPropertyValue the value that children of this node should
0776: * now inherit.
0777: */
0778: protected void propagatePackedPropertyState(
0779: final int propertyIndex, final int parentPropertyValue) {
0780: // Propagate to regular children.
0781: ModelNode node = getFirstChildNode();
0782: while (node != null) {
0783: node.recomputePackedPropertyState(propertyIndex,
0784: parentPropertyValue);
0785: node = node.nextSibling;
0786: }
0787:
0788: // Propagate to expanded children.
0789: node = getFirstExpandedChild();
0790: while (node != null) {
0791: node.recomputePackedPropertyState(propertyIndex,
0792: parentPropertyValue);
0793: node = node.nextSibling;
0794: }
0795: }
0796:
0797: /**
0798: * @return this node's cached transform.
0799: */
0800: public Transform getTransformState() {
0801: // By default, a ModelNode does not add any new transform, so its
0802: // cached transform state is the same as its parent node.
0803: if (parent != null) {
0804: return parent.getTransformState();
0805: }
0806:
0807: return IDENTITY;
0808: }
0809:
0810: /**
0811: * @return this node's cached inverse transform.
0812: */
0813: Transform getInverseTransformState() {
0814: // By default, a ModelNode does not add any new transform, so its
0815: // cached inverse transform state is the same as its parent node.
0816: if (parent != null) {
0817: return parent.getInverseTransformState();
0818: }
0819:
0820: return IDENTITY;
0821: }
0822:
0823: /**
0824: * Recomputes the transform cache, if one exists. This should recursively
0825: * call recomputeTransformState on children node or expanded content, if
0826: * any.
0827: *
0828: * By default, because a ModelNode has no transform and no cached transform,
0829: * this only does a pass down.
0830: */
0831: protected void recomputeTransformState() {
0832: if (parent == null) {
0833: recomputeTransformState(new Transform(null));
0834: } else {
0835: recomputeTransformState(parent.getTransformState());
0836: }
0837: }
0838:
0839: /**
0840: * Recomputes the transform cache, if one exists. This should recursively
0841: * call recomputeTransformState on children node or expanded content, if
0842: * any.
0843: *
0844: * By default, because a ModelNode has no transform and no cached transform,
0845: * this only does a pass down.
0846: *
0847: * @param parentTransform the Transform applied to this node's parent.
0848: */
0849: protected void recomputeTransformState(
0850: final Transform parentTransform) {
0851: recomputeTransformState(getTransformState(),
0852: getFirstChildNode());
0853: recomputeTransformState(getTransformState(),
0854: getFirstExpandedChild());
0855: computeCanRenderTransformBit(getTransformState());
0856: }
0857:
0858: /**
0859: * Recomputes the transform cache of the input <code>ModelNode</code>
0860: * and all its siblings. Implementation helper.
0861: *
0862: * @param parentTransform the parent transform.
0863: * @param node first node whose transform cache should be
0864: * cleared.
0865: */
0866: void recomputeTransformState(final Transform parentTransform,
0867: ModelNode node) {
0868: while (node != null) {
0869: node.recomputeTransformState(parentTransform);
0870: node = node.nextSibling;
0871: }
0872: }
0873:
0874: /**
0875: * @return true if this node is hooked to the document tree, i.e., if it top
0876: * most ancestor is the DocumentNode.
0877: */
0878: boolean inDocumentTree() {
0879: return parent != null && parent.inDocumentTree();
0880: }
0881:
0882: // JAVADOC COMMENT ELIDED
0883: public SVGMatrix getScreenCTM() {
0884: // The CTM is null if the element is not in the document tree.
0885: if (!inDocumentTree()) {
0886: return null;
0887: }
0888:
0889: return new Transform(getTransformState());
0890: }
0891:
0892: /**
0893: * Computes this node's rendering tile.
0894: *
0895: * @param tile the Tile instance whose bounds should be set.
0896: * @return the device space rendering tile.
0897: */
0898: protected void computeRenderingTile(final Tile tile) {
0899: // By default, there is no rendering and the tile is set to reflect
0900: // 'no rendering'
0901: tile.x = Integer.MIN_VALUE;
0902: tile.y = Integer.MIN_VALUE;
0903: tile.maxX = Integer.MIN_VALUE;
0904: tile.maxY = Integer.MIN_VALUE;
0905: }
0906:
0907: /**
0908: * @return the bounding box, in screen coordinate, which encompasses the
0909: * node's rendering. If this node's hasRendering method returns false, then
0910: * this method should return null. By default, this method returns null
0911: * because hasNodeRendering returns null by default.
0912: */
0913: protected Tile getRenderingTile() {
0914: return null;
0915: }
0916:
0917: /**
0918: * @return the tile which encompasses the node's last actual rendering. If
0919: * this node's hasRendering method returns false, then this method should
0920: * return null. By default, this method returns null because
0921: * hasNodeRendering returns null by default.
0922: */
0923: protected Tile getLastRenderedTile() {
0924: return null;
0925: }
0926:
0927: /**
0928: * After calling this method, getLastRenderedTile should always return null.
0929: */
0930: protected void clearLastRenderedTile() {
0931: }
0932:
0933: /**
0934: * Utility method to recycle a <code>Transform</code> instance when
0935: * possible.
0936: *
0937: * @param tx the <code>Transform</code> to use to initialize the returned
0938: * value.
0939: * @param workTx the candidate <code>Transform</code> instance which may be
0940: * re-cycled. The instance can be recycled if it is different
0941: * (i.e., a different reference) from the input base transform
0942: * <code>tx</code>.
0943: */
0944: protected final Transform recycleTransform(final Transform tx,
0945: final Transform workTx) {
0946: if (workTx != null && workTx != tx) {
0947: // We recycle workTx
0948: workTx.setTransform(tx);
0949: return workTx;
0950: } else {
0951: // System.err.println(">>>>>>> creating new Transform instance...");
0952: // We cannot use workTx because it is null or
0953: // it is the same as tx.
0954: return new Transform(tx);
0955: }
0956: }
0957:
0958: /**
0959: * Should be overridden by derived classes to apply any node specific
0960: * transform to the input transform.
0961: *
0962: * If the input transform is null and this node does not add any
0963: * transform, the return value should be null.
0964: *
0965: * If the input transform is null and this node adds a transform, the
0966: * return value should be an object equal to this node's transform,
0967: * but not be the same instance.
0968: *
0969: * If the input transform is not null and this node does not add any
0970: * transform, the return value should be the same as the input transform.
0971: *
0972: * If the input transform is not null and this node adds a transform,
0973: * the return value should be a new (different) transform.
0974: *
0975: * The second parameter, workTxf can be re-used to hold the result
0976: * only if it is different from tx. If it is referencing the same instance
0977: * as tx and a new Transform needs to be created, then it should not
0978: * be reused.
0979: * IMPL NOTE : This is a coding style to recycle <code>Transform</code>
0980: * instances in animation loops.
0981: *
0982: * @param tx the <code>Transform</code> to apply additional node
0983: * transforms to. This may be null.
0984: * @param workTx a <code>Transform</code> which can be re-used if a
0985: * new <code>Transform</code> needs to be created and workTx
0986: * is not the same instance as tx.
0987: * @return a transform with this node's transform added.
0988: */
0989: protected Transform appendTransform(final Transform tx,
0990: final Transform workTx) {
0991: // Does nothing by default. See derived classes.
0992: return tx;
0993: }
0994:
0995: /**
0996: * @param bbox the bounding box to which this node's bounding box should be
0997: * appended. That bounding box is in the target coordinate space. It
0998: * may be null, in which case this node should create a new one.
0999: * @param t the transform to apply from the node's coordinate space to the
1000: * target coordinate space. May be null for the identity
1001: * transform.
1002: * @return the node's bounding box in the target coordinate space.
1003: */
1004: Box addBBox(Box bbox, final Transform t) {
1005: return bbox;
1006: }
1007:
1008: /**
1009: * Implementation helper. Adds the input shape's bounding box to the
1010: * input bounding box rect. If the shape is null, nothing is added.
1011: *
1012: * @param path the shape whose bounds, transformed through t, should be
1013: * added to bbox.
1014: * @param t the transform to the target bounds space.
1015: * @param bbox the bounding box to which the shape's bounds should be
1016: * added.
1017: * @return a rectangle that encompasses bbox and the path's bounding box,
1018: * after transformation to the target coordinate space.
1019: */
1020: static Box addShapeBBox(Box bbox, final Path path, final Transform t) {
1021: if (path == null || path.getNumberOfSegments() == 0) {
1022: return bbox;
1023: }
1024:
1025: Box b = null;
1026: if (t == null) {
1027: b = path.getBounds();
1028: } else {
1029: b = path.getTransformedBounds(t);
1030: }
1031:
1032: return addBBox(bbox, b.getX(), b.getY(), b.getWidth(), b
1033: .getHeight());
1034: }
1035:
1036: /**
1037: * Implementation helper. Adds the input rectangle to the input bounding box
1038: * rect.
1039: *
1040: * @param bbox the rectangle to which the new box should be added.
1041: * @param x the x-axis origin of the new rect to add
1042: * @param y the y-axis origin of the new rect to add
1043: * @param width the width of the rect to add
1044: * @param height the height of the rect to add
1045: * @return a bounding box that encompasses bbox and the (x, y, width,
1046: * height) rectangle.
1047: */
1048: static Box addBBox(Box bbox, final float x, final float y,
1049: final float width, final float height) {
1050: if (bbox == null) {
1051: bbox = new Box(x, y, width, height);
1052: return bbox;
1053: }
1054:
1055: bbox.width = bbox.x + bbox.width;
1056: bbox.height = bbox.y + bbox.height;
1057:
1058: if (bbox.x > x) {
1059: bbox.x = x;
1060: }
1061:
1062: if (bbox.y > y) {
1063: bbox.y = y;
1064: }
1065:
1066: if (bbox.width < x + width) {
1067: bbox.width = x + width;
1068: }
1069:
1070: if (bbox.height < y + height) {
1071: bbox.height = y + height;
1072: }
1073:
1074: bbox.width -= bbox.x;
1075: bbox.height -= bbox.y;
1076:
1077: return bbox;
1078: }
1079:
1080: /**
1081: * Implementation helper. Computes the bounding box of the rectangle
1082: * transformed by the input matrix.
1083: *
1084: * @param bbox the bounding box to which this node's bounds should be
1085: * added.
1086: * @param x the rectangle's x-axis origin
1087: * @param y the rectangle's y-axis origin
1088: * @param width the rectangle's width
1089: * @param height the rectangle's height
1090: * @param m the matrix transforming to the target coordinate space
1091: * into which the returned rectangle should be.
1092: * @return the matrix from the input rectangle's coordinate space to
1093: * the target coordinate space.
1094: */
1095: static Box addTransformedBBox(Box bbox, final float x,
1096: final float y, final float width, final float height,
1097: final Transform m) {
1098:
1099: if (m == null) {
1100: return addBBox(bbox, x, y, width, height);
1101: }
1102:
1103: float tx = (float) (m.getComponent(0) * x + m.getComponent(2)
1104: * y + m.getComponent(4));
1105: float ty = (float) (m.getComponent(1) * x + m.getComponent(3)
1106: * y + m.getComponent(5));
1107:
1108: float dx1 = (float) (m.getComponent(0) * width);
1109: float dy1 = (float) (m.getComponent(1) * width);
1110:
1111: float dx2 = (float) (m.getComponent(2) * height);
1112: float dy2 = (float) (m.getComponent(3) * height);
1113:
1114: if (dx1 < 0) {
1115: tx += dx1;
1116: dx1 = -dx1;
1117: }
1118:
1119: if (dx2 > 0) {
1120: dx1 += dx2;
1121: } else {
1122: dx1 -= dx2;
1123: tx += dx2;
1124: }
1125:
1126: if (dy1 < 0) {
1127: ty += dy1;
1128: dy1 = -dy1;
1129: }
1130:
1131: if (dy2 > 0) {
1132: dy1 += dy2;
1133: } else {
1134: dy1 -= dy2;
1135: ty += dy2;
1136: }
1137:
1138: return addBBox(bbox, tx, ty, dx1, dy1);
1139: }
1140:
1141: /**
1142: * @param bbox the bounding box to which this node's bounding box should be
1143: * appended. That bounding box is in the target coordinate space. It
1144: * may be null, in which case this node should create a new one.
1145: * @param t the transform from the node coordinate system to the coordinate
1146: * system into which the bounds should be computed.
1147: * @return the bounding box of this node, in the target coordinate space,
1148: */
1149: Box addNodeBBox(final Box bbox, final Transform t) {
1150: return bbox;
1151: }
1152:
1153: /**
1154: * Returns the <code>ModelNode</code>, if any, hit by the
1155: * point at coordinate x/y.
1156: *
1157: * @param pt the x/y coordinate. Should never be null and be
1158: * of size two. If not, the behavior is unspecified.
1159: * The coordinates are in viewport space.
1160: * @return the <tt>ModelNode</tt> hit at the given point or null
1161: * if none was hit.
1162: */
1163: public ModelNode nodeHitAt(final float[] pt) {
1164: return null;
1165: }
1166:
1167: /**
1168: * Returns the <code>ModelNode</code>, if any, hit by the
1169: * point at coordinate x/y, in viewport space. The input
1170: * node, and all its siblings, are tested.
1171: *
1172: * @param node the first node on the set of nodes to test.
1173: * @param pt the point which is hit
1174: * @return the node hit at the given coordinate, or null if
1175: * no node was hit.
1176: */
1177: protected final ModelNode nodeHitAt(ModelNode node, final float[] pt) {
1178: ModelNode hit = null;
1179: while (node != null) {
1180: hit = node.nodeHitAt(pt);
1181: if (hit != null) {
1182: return hit;
1183: }
1184: node = node.prevSibling;
1185: }
1186: return null;
1187: }
1188:
1189: /**
1190: * The node's URI base to use to resolve URI references
1191: * If a URI base value was set on this node, then that value
1192: * is returned. Otherwise, this method returns the parent's
1193: * URI base. If there is not URI base on this node and if there
1194: * is not parent, then this method returns null.
1195: *
1196: * @return the node's URI base to use to resolve relative URI references.
1197: */
1198: protected String getURIBase() {
1199: if (parent != null) {
1200: return parent.getURIBase();
1201: }
1202: return null;
1203: }
1204:
1205: /**
1206: * Recomputes the requiredFeatures bit in the canRenderState bit mask.
1207: *
1208: * @param requiredFeatures if ConditionalProcessing.checkFeatures returns
1209: * true, the bit is cleared. Otherwise, the bit is set.
1210: */
1211: final void computeCanRenderRequiredFeaturesBit(
1212: final String[] requiredFeatures) {
1213: int oldCanRenderState = canRenderState;
1214: if (requiredFeatures == null
1215: || ConditionalProcessing
1216: .checkFeatures(requiredFeatures)) {
1217: canRenderState &= CAN_RENDER_REQUIRED_FEATURES_MASK;
1218: } else {
1219: canRenderState |= CAN_RENDER_REQUIRED_FEATURES_BIT;
1220: }
1221:
1222: propagateCanRenderState(oldCanRenderState, canRenderState);
1223: }
1224:
1225: /**
1226: * Recomputes the requiredExtensions bit in the canRenderState bit mask.
1227: *
1228: * @param requiredExtensions if ConditionalProcessing.checkExtensions
1229: * returns true, the bit is cleared. Otherwise, the bit is set.
1230: */
1231: final void computeCanRenderRequiredExtensionsBit(
1232: final String[] requiredExtensions) {
1233: int oldCanRenderState = canRenderState;
1234: if (requiredExtensions == null
1235: || ConditionalProcessing
1236: .checkExtensions(requiredExtensions)) {
1237: canRenderState &= CAN_RENDER_REQUIRED_EXTENSIONS_MASK;
1238: } else {
1239: canRenderState |= CAN_RENDER_REQUIRED_EXTENSIONS_BIT;
1240: }
1241:
1242: propagateCanRenderState(oldCanRenderState, canRenderState);
1243: }
1244:
1245: /**
1246: * Recomputes the systemLanguage bit in the canRenderState bit mask.
1247: *
1248: * @param systemLanguage if ConditionalProcessing.checkLanguage returns
1249: * true, the bit is cleared. Otherwise, the bit is set.
1250: */
1251: final void computeCanRenderSystemLanguageBit(
1252: final String[] systemLanguage) {
1253: int oldCanRenderState = canRenderState;
1254: if (systemLanguage == null
1255: || ConditionalProcessing.checkLanguage(systemLanguage)) {
1256: canRenderState &= CAN_RENDER_SYSTEM_LANGUAGE_MASK;
1257: } else {
1258: canRenderState |= CAN_RENDER_SYSTEM_LANGUAGE_BIT;
1259: }
1260:
1261: propagateCanRenderState(oldCanRenderState, canRenderState);
1262: }
1263:
1264: /**
1265: * Recomputes the non-invertible transform bit in the canRenderState
1266: * bit mask.
1267: *
1268: * @param transform the transform which drives the non-invertible
1269: * transform bit. If non invertible, the bit is set. Otherwise, the
1270: * bit is cleared.
1271: */
1272: final void computeCanRenderTransformBit(final Transform transform) {
1273: int oldCanRenderState = canRenderState;
1274: if (transform == null || transform.isInvertible()) {
1275: canRenderState &= CAN_RENDER_NON_INVERTIBLE_TXF_MASK;
1276: } else {
1277: canRenderState |= CAN_RENDER_NON_INVERTIBLE_TXF_BIT;
1278: }
1279:
1280: propagateCanRenderState(oldCanRenderState, canRenderState);
1281: }
1282:
1283: /**
1284: * Recomputes the empty viewBox bit in the canRenderState
1285: * bit mask.
1286: *
1287: * @param viewBox the viewBox value which drives the empty viewBox
1288: * bit. If null or if positive width/height, the bit is cleared.
1289: * Otherwise, the bit is set.
1290: */
1291: final void computeCanRenderEmptyViewBoxBit(final float[][] viewBox) {
1292: int oldCanRenderState = canRenderState;
1293: if (viewBox == null || (viewBox[1][0] > 0 && viewBox[2][0] > 0)) {
1294: canRenderState &= CAN_RENDER_EMPTY_VIEWBOX_MASK;
1295: } else {
1296: canRenderState |= CAN_RENDER_EMPTY_VIEWBOX_BIT;
1297: }
1298:
1299: propagateCanRenderState(oldCanRenderState, canRenderState);
1300: }
1301:
1302: /**
1303: * Recomputes the display bit in the canRenderState bit mask.
1304: *
1305: * @param display the display value. If true, the bit is cleared. If
1306: * false, the bit is set.
1307: */
1308: final void computeCanRenderDisplayBit(final boolean display) {
1309: int oldCanRenderState = canRenderState;
1310: if (display) {
1311: canRenderState &= CAN_RENDER_DISPLAY_MASK;
1312: } else {
1313: canRenderState |= CAN_RENDER_DISPLAY_BIT;
1314: }
1315:
1316: propagateCanRenderState(oldCanRenderState, canRenderState);
1317: }
1318:
1319: /**
1320: * Recomputes the zero-width bit in the canRenderState bit mask.
1321: *
1322: * @param width the new width value. If zero or negative, the bit
1323: * is set. Otherwise, the bit is cleared.
1324: */
1325: final void computeCanRenderWidthBit(final float width) {
1326: int oldCanRenderState = canRenderState;
1327: if (width > 0) {
1328: canRenderState &= CAN_RENDER_ZERO_WIDTH_MASK;
1329: } else {
1330: canRenderState |= CAN_RENDER_ZERO_WIDTH_BIT;
1331: }
1332:
1333: propagateCanRenderState(oldCanRenderState, canRenderState);
1334: }
1335:
1336: /**
1337: * Recomputes the zero-height bit in the canRenderState bit mask.
1338: *
1339: * @param height the new height value. If zero or negative, the bit
1340: * is set. Otherwise, the bit is cleared.
1341: */
1342: final void computeCanRenderHeightBit(final float height) {
1343: int oldCanRenderState = canRenderState;
1344: if (height > 0) {
1345: canRenderState &= CAN_RENDER_ZERO_HEIGHT_MASK;
1346: } else {
1347: canRenderState |= CAN_RENDER_ZERO_HEIGHT_BIT;
1348: }
1349:
1350: propagateCanRenderState(oldCanRenderState, canRenderState);
1351: }
1352:
1353: /**
1354: * Recomputes the zero-fontSize bit in the canRenderState bit mask.
1355: *
1356: * @param fontSize the new fontSize value. If zero or negative, the bit
1357: * is set. Otherwise, the bit is cleared.
1358: */
1359: final void computeCanRenderFontSizeBit(final float fontSize) {
1360: int oldCanRenderState = canRenderState;
1361: if (fontSize > 0) {
1362: canRenderState &= CAN_RENDER_ZERO_FONT_SIZE_MASK;
1363: } else {
1364: canRenderState |= CAN_RENDER_ZERO_FONT_SIZE_BIT;
1365: }
1366:
1367: propagateCanRenderState(oldCanRenderState, canRenderState);
1368: }
1369:
1370: /**
1371: * Recomputes the empty path bit in the canRenderStaet bit mask.
1372: *
1373: * @param path the value that determines the bit state. If null or
1374: * if no segments, the bit is set. Otherwise, the bit is cleared.
1375: */
1376: final void computeCanRenderEmptyPathBit(final Path path) {
1377: int oldCanRenderState = canRenderState;
1378: if (path == null || path.getNumberOfSegments() == 0) {
1379: canRenderState |= CAN_RENDER_EMPTY_PATH_BIT;
1380: } else {
1381: canRenderState &= CAN_RENDER_EMPTY_PATH_MASK;
1382: }
1383:
1384: propagateCanRenderState(oldCanRenderState, canRenderState);
1385: }
1386:
1387: /**
1388: * By default, there is no node rendering.
1389: *
1390: * @return true if the node has some rendering of its own,
1391: * i.e., rendering beyond what its children or
1392: * expanded content render.
1393: */
1394: protected boolean hasNodeRendering() {
1395: return false;
1396: }
1397:
1398: /**
1399: * Clears the text layouts, if any exist. This is typically
1400: * called when the font selection has changed and nodes such
1401: * as <code>Text</code> should recompute their layouts.
1402: * This should recursively call clearLayouts on children
1403: * node or expanded content, if any.
1404: */
1405: protected abstract void clearLayouts();
1406:
1407: /**
1408: * Clears the text layouts in the input node and all its
1409: * siblings. Implementation helper.
1410: *
1411: * @param node the first node whose text layouts should be cleared.
1412: */
1413: void clearLayouts(ModelNode node) {
1414: while (node != null) {
1415: node.clearLayouts();
1416: node = node.nextSibling;
1417: }
1418: }
1419:
1420: /**
1421: * @return true if this node is considered loaded. This is used
1422: * in support of progressive rendering.
1423: */
1424: public final boolean isLoaded() {
1425: return loaded;
1426: }
1427:
1428: /**
1429: * @param isLoaded the new loaded state
1430: */
1431: public void setLoaded(final boolean isLoaded) {
1432: loaded = isLoaded;
1433: }
1434:
1435: /**
1436: * @return true if the node needs to be fully loaded before it
1437: * can be painted
1438: */
1439: boolean getPaintNeedsLoad() {
1440: return false;
1441: }
1442:
1443: /**
1444: * Used to notify the <code>UpdateListener</code>, if any, of
1445: * an upcoming node modification
1446: *
1447: */
1448: protected void modifyingNode() {
1449: UpdateListener updateListener = getUpdateListener();
1450: if (updateListener != null) {
1451: updateListener.modifyingNode(this );
1452: }
1453: }
1454:
1455: /**
1456: * Used to notify the <code>UpdateListener</code>, if any, of
1457: * an change in a node's rendering
1458: *
1459: */
1460: protected void modifyingNodeRendering() {
1461: UpdateListener updateListener = getUpdateListener();
1462: if (updateListener != null) {
1463: updateListener.modifyingNodeRendering(this );
1464: }
1465: }
1466:
1467: /**
1468: * Used to notify the <code>UpdateListener</code>, if any, of
1469: * a completed node modification
1470: *
1471: */
1472: protected void modifiedNode() {
1473: UpdateListener updateListener = getUpdateListener();
1474: if (updateListener != null) {
1475: updateListener.modifiedNode(this );
1476: }
1477: }
1478:
1479: /**
1480: * Used to notify the <code>UpdateListener</code>, if any, of
1481: * a node insertion
1482: *
1483: * @param node the node which was just inserted
1484: */
1485: protected static void nodeInserted(final ModelNode node) {
1486: UpdateListener updateListener = node.getUpdateListener();
1487: if (updateListener != null) {
1488: updateListener.nodeInserted(node);
1489: }
1490: }
1491:
1492: // JAVADOC COMMENT ELIDED
1493: public void addEventListener(final String type,
1494: final EventListener listener, final boolean useCapture)
1495: throws DOMException {
1496: ownerDocument.eventSupport.addEventListener(this , type,
1497: useCapture ? EventSupport.CAPTURE_PHASE
1498: : EventSupport.BUBBLE_PHASE, listener);
1499:
1500: }
1501:
1502: // JAVADOC COMMENT ELIDED
1503: public void removeEventListener(final String type,
1504: final EventListener listener, final boolean useCapture)
1505: throws DOMException {
1506: ownerDocument.eventSupport.removeEventListener(this , type,
1507: useCapture ? EventSupport.CAPTURE_PHASE
1508: : EventSupport.BUBBLE_PHASE, listener);
1509: }
1510:
1511: /**
1512: * Delegates to the associated <code>EventSupport</code> class.
1513: *
1514: * @param evt the event to dispatch
1515: */
1516: public void dispatchEvent(final ModelEvent evt) {
1517: ownerDocument.eventSupport.dispatchEvent(evt);
1518: }
1519:
1520: // =========================================================================
1521: // Methods with default implementations which are typically overridden in
1522: // extension.
1523: // =========================================================================
1524:
1525: /**
1526: * To be overriddent by derived classes, such as TimedElementNode,
1527: * if they need to perform special operations when hooked into the
1528: * document tree.
1529: */
1530: void onHookedInDocumentTree() {
1531: // Clear the 'in document tree' can render bit
1532: int oldCanRenderState = canRenderState;
1533: canRenderState &= CAN_RENDER_IN_DOCUMENT_TREE_MASK;
1534:
1535: // Set the parent's canRenderState bit
1536: if (parent.canRenderState == 0) {
1537: canRenderState &= CAN_RENDER_PARENT_STATE_MASK;
1538: } else {
1539: canRenderState |= CAN_RENDER_PARENT_STATE_BIT;
1540: }
1541: propagateCanRenderState(oldCanRenderState, canRenderState);
1542:
1543: recomputeTransformState();
1544: recomputeInheritedProperties();
1545: }
1546:
1547: /**
1548: * To be overriddent by derived classes, such as TimedElementNode,
1549: * if they need to perform special operations when unhooked from the
1550: * document tree.
1551: */
1552: void onUnhookedFromDocumentTree() {
1553: // Set the 'in document tree' can render bit
1554: int oldCanRenderState = canRenderState;
1555: canRenderState |= CAN_RENDER_IN_DOCUMENT_TREE_BIT;
1556:
1557: // Clear the parent's canRenderState bit
1558: canRenderState &= CAN_RENDER_PARENT_STATE_MASK;
1559:
1560: propagateCanRenderState(oldCanRenderState, canRenderState);
1561:
1562: recomputeTransformState();
1563: recomputeInheritedProperties();
1564: }
1565:
1566: /**
1567: * Paints this node into the input <code>RenderGraphics</code>.
1568: *
1569: * @param rg the <tt>RenderGraphics</tt> where the node should paint itself
1570: */
1571: public void paint(final RenderGraphics rg) {
1572: // By default, do not paint anything.
1573: }
1574:
1575: /**
1576: * To be overridden by nodes which may have a rendering if they need
1577: * to do something specific (e.g., notify their RenderingManager) when
1578: * they have been rendered.
1579: */
1580: protected void nodeRendered() {
1581: // By default, do nothing.
1582: }
1583:
1584: // =========================================================================
1585: // Abstract method to override in extensions.
1586: // =========================================================================
1587:
1588: /**
1589: * Utility method. Unhooks the children.
1590: */
1591: protected abstract void unhookChildrenQuiet();
1592:
1593: /**
1594: * Utility method. Unhooks the expanded content.
1595: */
1596: protected abstract void unhookExpandedQuiet();
1597:
1598: /**
1599: * @return a reference to the node's first child, or null if there
1600: * are no children.
1601: */
1602: public abstract ModelNode getFirstChildNode();
1603:
1604: /**
1605: * @return a reference to the node's last child, or null if there
1606: * are no children.
1607: */
1608: public abstract ModelNode getLastChildNode();
1609:
1610: /**
1611: * Some node types (such as <code>ElementNodeProxy</code>) have
1612: * expanded children that they compute in some specific
1613: * way depending on the implementation.
1614: *
1615: * @return a reference to the node's first expanded child, or null if there
1616: * are no expanded children. This forces the computation of expanded
1617: * content if needed.
1618: */
1619: abstract ModelNode getFirstExpandedChild();
1620:
1621: /**
1622: * Some node types (such as <code>ElementNodeProxy</code>) have
1623: * expanded children that they compute in some specific
1624: * way depending on the implementation.
1625: *
1626: * @return a reference to the node's first expanded child, or null if there
1627: * are no expanded children. This forces the computation of expanded
1628: * content if needed.
1629: */
1630: abstract ModelNode getFirstComputedExpandedChild();
1631:
1632: /**
1633: * Some node types (such as <code>ElementNodeProxy</code>) have
1634: * expanded children that they compute in some specific
1635: * way depending on the implementation.
1636: *
1637: * @return a reference to the node's last expanded child, or null if there
1638: * are no expanded children. This forces the computation of expanded
1639: * content if needed.
1640: */
1641: abstract ModelNode getLastExpandedChild();
1642:
1643: }
|