FileDocCategorySizeDatePackage
Viewport.javaAPI DocphoneME MR2 API (J2ME)8304Wed May 02 18:00:34 BST 2007com.sun.perseus.model

Viewport.java

/*
 *
 *
 * Copyright  1990-2007 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt).
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions.
 */
package com.sun.perseus.model;

import com.sun.perseus.j2d.Transform;

/**
 * A <code>Viewport</code> describes a <code>ModelNode</code> into which 
 * rendering may happen. 
 *
 * <p>A <code>ViewportNode</code> has a width and height
 * which a child <code>SVG</code> uses to compute the viewbox to viewport
 * transform.</p>
 * 
 * <p>In addition, a <code>Viewport</code> has a user transform limited to 
 * its scale and translation components.</p>
 *
 * <p>The <code>Viewport</code> is the root of any SVG fragment hierarchy.
 * </p>
 *
 * @version $Id: Viewport.java,v 1.9 2006/06/29 10:47:36 ln156897 Exp $
 */
public abstract class Viewport extends CompositeNode implements Transformable {
    /**
     * Default width for viewports.
     */
    public static final int DEFAULT_VIEWPORT_WIDTH = 100;

    /**
     * Default height for viewports
     */
    public static final int DEFAULT_VIEWPORT_HEIGHT = 100;

    /**
     * As in the SVG 1.1 specification
     */
    public static final int ZOOM_PAN_MAGNIFY = 0;

    /**
     * As in the SVG 1.1 specification
     */
    public static final int ZOOM_PAN_DISABLE = 1;

    /**
     * As in the SVG 1.1 specification
     */
    public static final int ZOOM_PAN_UNKNOWN = 2;

    /**
     * Viewport width 
     */
    protected int width = DEFAULT_VIEWPORT_WIDTH;

    /**
     * Viewport height
     */
    protected int height = DEFAULT_VIEWPORT_HEIGHT;

    /**
     * The Transform applied to this node. 
     */
    protected Transform transform = new Transform(null);

    /**
     * The inverse of the Transform applied to this node. 
     */
    protected Transform inverseTxf = new Transform(null);


    /**
     * The zoomAndPan setting
     */
    protected int zoomAndPan = ZOOM_PAN_MAGNIFY;

    /**
     * Default constructor
     */
    public Viewport() {
    }

    /**
     * Returns the <code>ModelNode</code>, if any, hit by the
     * point at coordinate x/y. 
     * 
     * @param pt the x/y coordinate. Should never be null and be
     *        of size two. If not, the behavior is unspecified.
     *        The coordinates are in viewport space.
     * @return the <tt>ModelNode</tt> hit at the given point or null
     *         if none was hit.
     */
    public ModelNode nodeHitAt(final float[] pt) {
        // Check for a hit on children
        return nodeHitAt(getLastChildNode(), pt);
    }

    /**
     * @return The zoomAndPan setting for this viewport (read-only)
     */
    public int getZoomAndPan() {
        return zoomAndPan;
    }

    /**
     * @param newZoomAndPan the new value for the zoom and pan setting
     */
    public void setZoomAndPan(final int newZoomAndPan) {
        if (newZoomAndPan != ZOOM_PAN_MAGNIFY
            &&
            newZoomAndPan != ZOOM_PAN_DISABLE
            &&
            newZoomAndPan != ZOOM_PAN_UNKNOWN) {
            throw new IllegalArgumentException();
        }

        if (newZoomAndPan == zoomAndPan) {
            return;
        }
        modifyingNode();
        this.zoomAndPan = newZoomAndPan;
        modifiedNode();
    }

    ///////////////////// 

    /**
     * @return the viewport width
     */
    public int getWidth() {
        return this.width;
    }

    /**
     * @return the viewport height
     */
    public int getHeight() {
        return this.height;
    }

    /**
     * Sets the viewport size
     *
     * @param newWidth the new viewport width. Should be greater than 0
     * @param newHeight the new viewport height. Should be greater than 0.
     */
    public void setSize(final int newWidth, final int newHeight) {
        if (newWidth < 0 || newHeight < 0) {
            throw new IllegalArgumentException();
        }

        if (newWidth == width && newHeight == height) {
            return;
        }

        modifyingNode();
        this.width = newWidth;
        this.height = newHeight;
        recomputeTransformState(null);
        computeCanRenderWidthBit(width);
        computeCanRenderHeightBit(height);
        modifiedNode();
    }

    /**
     * @return this node's cached transform. 
     */
    public Transform getTransformState() {
        return transform;
    }

    /**
     * @return this node's cached inverse transform. 
     */
    public Transform getInverseTransformState() {
        if (((canRenderState & CAN_RENDER_NON_INVERTIBLE_TXF_BIT) == 0)) {
            if (inverseTxf == null) {
                inverseTxf = new Transform(null);
                try {
                    inverseTxf = (Transform) transform.inverse(inverseTxf);
                } catch (Exception e) {
                    // If we get an exception, then we have a real error
                    // condition, because we just checked that the
                    // transform was invertible.
                    throw new Error();
                }
            }
        } else {
        inverseTxf = null;
        }
        return inverseTxf;
    }


    /**
     * Recomputes the transform cache, if one exists. This should recursively
     * call recomputeTransformState on children node or expanded content, if
     * any.
     *
     * By default, because a ModelNode has no transform and no cached transform,
     * this only does a pass down.
     *
     * @param parentTransform the Transform applied to this node's parent.
     */
    protected void recomputeTransformState(final Transform parentTransform) {
        if (parentTransform != null) {
            throw new IllegalArgumentException();
        }

        computeCanRenderTransformBit(transform);
        inverseTxf = null;
        recomputeTransformState(transform, getFirstChildNode());
    }

    /**
     * @param newTransform The new <code>Transformable</code>'s transform.
     */
    public void setTransform(final Transform newTransform) {
        if (ElementNode.equal(newTransform, this.transform)) {
            return;
        }

        modifyingNode();
        this.transform = newTransform;
        recomputeTransformState(null);
        modifiedNode();
    }

    /**
     * @return This <code>Transformable</code>'s transform.
     */
    public Transform getTransform() {
        return transform;
    }

    /**
     * Appends this node's transform, if it is not null.
     *
     * @param tx the <code>Transform</code> to apply additional node 
     *        transforms to. This may be null.
     * @param workTx a <code>Transform</code> which can be re-used if a 
     *        new <code>Transform</code> needs to be created and workTx
     *        is not the same instance as tx.
     * @return a transform with this node's transform added.
     */
    protected Transform appendTransform(Transform tx,
                                        final Transform workTx) {
        if (transform == null) {
            return tx;
        }

        tx = recycleTransform(tx, workTx);
        tx.mMultiply(transform);
        
        return tx;
    }

    /**
     * Debug helper
     * 
     * @return a textual description of this viewport object
     */
    /*
    public String toString() {
        return "[Viewport(zoomPan=" + zoomAndPan + ", width=" 
            + width + " height=" + height 
            + " txf=" + transform
            + "]";
    }
    */

}