1188 lines · 44.3 KB · Java
RawDownload
/* * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code 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.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 in the LICENSE file that * accompanied this code). * * 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; import java.io.PrintStream;import java.util.Vector;import java.awt.*;import javax.swing.event.DocumentEvent;import javax.swing.SizeRequirements; /** * A view that arranges its children into a box shape by tiling * its children along an axis.  The box is somewhat like that * found in TeX where there is alignment of the * children, flexibility of the children is considered, etc. * This is a building block that might be useful to represent * things like a collection of lines, paragraphs, * lists, columns, pages, etc.  The axis along which the children are tiled is * considered the major axis.  The orthoginal axis is the minor axis. * <p> * Layout for each axis is handled separately by the methods * <code>layoutMajorAxis</code> and <code>layoutMinorAxis</code>. * Subclasses can change the layout algorithm by * reimplementing these methods.    These methods will be called * as necessary depending upon whether or not there is cached * layout information and the cache is considered * valid.  These methods are typically called if the given size * along the axis changes, or if <code>layoutChanged</code> is * called to force an updated layout.  The <code>layoutChanged</code> * method invalidates cached layout information, if there is any. * The requirements published to the parent view are calculated by * the methods <code>calculateMajorAxisRequirements</code> * and  <code>calculateMinorAxisRequirements</code>. * If the layout algorithm is changed, these methods will * likely need to be reimplemented. * * @author  Timothy Prinzing */public class BoxView extends CompositeView {     /**     * Constructs a <code>BoxView</code>.     *     * @param elem the element this view is responsible for     * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>     */    public BoxView(Element elem, int axis) {        super(elem);        tempRect = new Rectangle();        this.majorAxis = axis;         majorOffsets = new int[0];        majorSpans = new int[0];        majorReqValid = false;        majorAllocValid = false;        minorOffsets = new int[0];        minorSpans = new int[0];        minorReqValid = false;        minorAllocValid = false;    }     /**     * Fetches the tile axis property.  This is the axis along which     * the child views are tiled.     *     * @return the major axis of the box, either     *  <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>     *     * @since 1.3     */    public int getAxis() {        return majorAxis;    }     /**     * Sets the tile axis property.  This is the axis along which     * the child views are tiled.     *     * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>     *     * @since 1.3     */    public void setAxis(int axis) {        boolean axisChanged = (axis != majorAxis);        majorAxis = axis;        if (axisChanged) {            preferenceChanged(null, true, true);        }    }     /**     * Invalidates the layout along an axis.  This happens     * automatically if the preferences have changed for     * any of the child views.  In some cases the layout     * may need to be recalculated when the preferences     * have not changed.  The layout can be marked as     * invalid by calling this method.  The layout will     * be updated the next time the <code>setSize</code> method     * is called on this view (typically in paint).     *     * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>     *     * @since 1.3     */    public void layoutChanged(int axis) {        if (axis == majorAxis) {            majorAllocValid = false;        } else {            minorAllocValid = false;        }    }     /**     * Determines if the layout is valid along the given axis.     *     * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>     *     * @since 1.4     */    protected boolean isLayoutValid(int axis) {        if (axis == majorAxis) {            return majorAllocValid;        } else {            return minorAllocValid;        }    }     /**     * Paints a child.  By default     * that is all it does, but a subclass can use this to paint     * things relative to the child.     *     * @param g the graphics context     * @param alloc the allocated region to paint into     * @param index the child index, >= 0 && < getViewCount()     */    protected void paintChild(Graphics g, Rectangle alloc, int index) {        View child = getView(index);        child.paint(g, alloc);    }     // --- View methods ---------------------------------------------     /**     * Invalidates the layout and resizes the cache of     * requests/allocations.  The child allocations can still     * be accessed for the old layout, but the new children     * will have an offset and span of 0.     *     * @param index the starting index into the child views to insert     *   the new views; this should be a value >= 0 and <= getViewCount     * @param length the number of existing child views to remove;     *   This should be a value >= 0 and <= (getViewCount() - offset)     * @param elems the child views to add; this value can be     *   <code>null</code>to indicate no children are being added     *   (useful to remove)     */    public void replace(int index, int length, View[] elems) {        super.replace(index, length, elems);         // invalidate cache        int nInserted = (elems != null) ? elems.length : 0;        majorOffsets = updateLayoutArray(majorOffsets, index, nInserted);        majorSpans = updateLayoutArray(majorSpans, index, nInserted);        majorReqValid = false;        majorAllocValid = false;        minorOffsets = updateLayoutArray(minorOffsets, index, nInserted);        minorSpans = updateLayoutArray(minorSpans, index, nInserted);        minorReqValid = false;        minorAllocValid = false;    }     /**     * Resizes the given layout array to match the new number of     * child views.  The current number of child views are used to     * produce the new array.  The contents of the old array are     * inserted into the new array at the appropriate places so that     * the old layout information is transferred to the new array.     *     * @param oldArray the original layout array     * @param offset location where new views will be inserted     * @param nInserted the number of child views being inserted;     *          therefore the number of blank spaces to leave in the     *          new array at location <code>offset</code>     * @return the new layout array     */    int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {        int n = getViewCount();        int[] newArray = new int[n];         System.arraycopy(oldArray, 0, newArray, 0, offset);        System.arraycopy(oldArray, offset,                         newArray, offset + nInserted, n - nInserted - offset);        return newArray;    }     /**     * Forwards the given <code>DocumentEvent</code> to the child views     * that need to be notified of the change to the model.     * If a child changed its requirements and the allocation     * was valid prior to forwarding the portion of the box     * from the starting child to the end of the box will     * be repainted.     *     * @param ec changes to the element this view is responsible     *  for (may be <code>null</code> if there were no changes)     * @param e the change information from the associated document     * @param a the current allocation of the view     * @param f the factory to use to rebuild if the view has children     * @see #insertUpdate     * @see #removeUpdate     * @see #changedUpdate     * @since 1.3     */    protected void forwardUpdate(DocumentEvent.ElementChange ec,                                 DocumentEvent e, Shape a, ViewFactory f) {        boolean wasValid = isLayoutValid(majorAxis);        super.forwardUpdate(ec, e, a, f);         // determine if a repaint is needed        if (wasValid && (! isLayoutValid(majorAxis))) {            // Repaint is needed because one of the tiled children            // have changed their span along the major axis.  If there            // is a hosting component and an allocated shape we repaint.            Component c = getContainer();            if ((a != null) && (c != null)) {                int pos = e.getOffset();                int index = getViewIndexAtPosition(pos);                Rectangle alloc = getInsideAllocation(a);                if (majorAxis == X_AXIS) {                    alloc.x += majorOffsets[index];                    alloc.width -= majorOffsets[index];                } else {                    alloc.y += minorOffsets[index];                    alloc.height -= minorOffsets[index];                }                c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);            }        }    }     /**     * This is called by a child to indicate its     * preferred span has changed.  This is implemented to     * throw away cached layout information so that new     * calculations will be done the next time the children     * need an allocation.     *     * @param child the child view     * @param width true if the width preference should change     * @param height true if the height preference should change     */    public void preferenceChanged(View child, boolean width, boolean height) {        boolean majorChanged = (majorAxis == X_AXIS) ? width : height;        boolean minorChanged = (majorAxis == X_AXIS) ? height : width;        if (majorChanged) {            majorReqValid = false;            majorAllocValid = false;        }        if (minorChanged) {            minorReqValid = false;            minorAllocValid = false;        }        super.preferenceChanged(child, width, height);    }     /**     * Gets the resize weight.  A value of 0 or less is not resizable.     *     * @param axis may be either <code>View.X_AXIS</code> or     *          <code>View.Y_AXIS</code>     * @return the weight     * @exception IllegalArgumentException for an invalid axis     */    public int getResizeWeight(int axis) {        checkRequests(axis);        if (axis == majorAxis) {            if ((majorRequest.preferred != majorRequest.minimum) ||                (majorRequest.preferred != majorRequest.maximum)) {                return 1;            }        } else {            if ((minorRequest.preferred != minorRequest.minimum) ||                (minorRequest.preferred != minorRequest.maximum)) {                return 1;            }        }        return 0;    }     /**     * Sets the size of the view along an axis.  This should cause     * layout of the view along the given axis.     *     * @param axis may be either <code>View.X_AXIS</code> or     *          <code>View.Y_AXIS</code>     * @param span the span to layout to >= 0     */    void setSpanOnAxis(int axis, float span) {        if (axis == majorAxis) {            if (majorSpan != (int) span) {                majorAllocValid = false;            }            if (! majorAllocValid) {                // layout the major axis                majorSpan = (int) span;                checkRequests(majorAxis);                layoutMajorAxis(majorSpan, axis, majorOffsets, majorSpans);                majorAllocValid = true;                 // flush changes to the children                updateChildSizes();            }        } else {            if (((int) span) != minorSpan) {                minorAllocValid = false;            }            if (! minorAllocValid) {                // layout the minor axis                minorSpan = (int) span;                checkRequests(axis);                layoutMinorAxis(minorSpan, axis, minorOffsets, minorSpans);                minorAllocValid = true;                 // flush changes to the children                updateChildSizes();            }        }    }     /**     * Propagates the current allocations to the child views.     */    void updateChildSizes() {        int n = getViewCount();        if (majorAxis == X_AXIS) {            for (int i = 0; i < n; i++) {                View v = getView(i);                v.setSize((float) majorSpans[i], (float) minorSpans[i]);            }        } else {            for (int i = 0; i < n; i++) {                View v = getView(i);                v.setSize((float) minorSpans[i], (float) majorSpans[i]);            }        }    }     /**     * Returns the size of the view along an axis.  This is implemented     * to return zero.     *     * @param axis may be either <code>View.X_AXIS</code> or     *          <code>View.Y_AXIS</code>     * @return the current span of the view along the given axis, >= 0     */    float getSpanOnAxis(int axis) {        if (axis == majorAxis) {            return majorSpan;        } else {            return minorSpan;        }    }     /**     * Sets the size of the view.  This should cause     * layout of the view if the view caches any layout     * information.  This is implemented to call the     * layout method with the sizes inside of the insets.     *     * @param width the width >= 0     * @param height the height >= 0     */    public void setSize(float width, float height) {        layout(Math.max(0, (int)(width - getLeftInset() - getRightInset())),               Math.max(0, (int)(height - getTopInset() - getBottomInset())));    }     /**     * Renders the <code>BoxView</code> using the given     * rendering surface and area     * on that surface.  Only the children that intersect     * the clip bounds of the given <code>Graphics</code>     * will be rendered.     *     * @param g the rendering surface to use     * @param allocation the allocated region to render into     * @see View#paint     */    public void paint(Graphics g, Shape allocation) {        Rectangle alloc = (allocation instanceof Rectangle) ?                           (Rectangle)allocation : allocation.getBounds();        int n = getViewCount();        int x = alloc.x + getLeftInset();        int y = alloc.y + getTopInset();        Rectangle clip = g.getClipBounds();        for (int i = 0; i < n; i++) {            tempRect.x = x + getOffset(X_AXIS, i);            tempRect.y = y + getOffset(Y_AXIS, i);            tempRect.width = getSpan(X_AXIS, i);            tempRect.height = getSpan(Y_AXIS, i);            int trx0 = tempRect.x, trx1 = trx0 + tempRect.width;            int try0 = tempRect.y, try1 = try0 + tempRect.height;            int crx0 = clip.x, crx1 = crx0 + clip.width;            int cry0 = clip.y, cry1 = cry0 + clip.height;            // We should paint views that intersect with clipping region            // even if the intersection has no inside points (is a line).            // This is needed for supporting views that have zero width, like            // views that contain only combining marks.            if ((trx1 >= crx0) && (try1 >= cry0) && (crx1 >= trx0) && (cry1 >= try0)) {                paintChild(g, tempRect, i);            }        }    }     /**     * Fetches the allocation for the given child view.     * This enables finding out where various views     * are located.  This is implemented to return     * <code>null</code> if the layout is invalid,     * otherwise the superclass behavior is executed.     *     * @param index the index of the child, >= 0 && < getViewCount()     * @param a  the allocation to this view     * @return the allocation to the child; or <code>null</code>     *          if <code>a</code> is <code>null</code>;     *          or <code>null</code> if the layout is invalid     */    public Shape getChildAllocation(int index, Shape a) {        if (a != null) {            Shape ca = super.getChildAllocation(index, a);            if ((ca != null) && (! isAllocationValid())) {                // The child allocation may not have been set yet.                Rectangle r = (ca instanceof Rectangle) ?                    (Rectangle) ca : ca.getBounds();                if ((r.width == 0) && (r.height == 0)) {                    return null;                }            }            return ca;        }        return null;    }     /**     * Provides a mapping from the document model coordinate space     * to the coordinate space of the view mapped to it.  This makes     * sure the allocation is valid before calling the superclass.     *     * @param pos the position to convert >= 0     * @param a the allocated region to render into     * @return the bounding box of the given position     * @exception BadLocationException  if the given position does     *  not represent a valid location in the associated document     * @see View#modelToView     */    public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {        if (! isAllocationValid()) {            Rectangle alloc = a.getBounds();            setSize(alloc.width, alloc.height);        }        return super.modelToView(pos, a, b);    }     /**     * Provides a mapping from the view coordinate space to the logical     * coordinate space of the model.     *     * @param x   x coordinate of the view location to convert >= 0     * @param y   y coordinate of the view location to convert >= 0     * @param a the allocated region to render into     * @return the location within the model that best represents the     *  given point in the view >= 0     * @see View#viewToModel     */    public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {        if (! isAllocationValid()) {            Rectangle alloc = a.getBounds();

Lines 1–500 of 1188.