871 lines · 32.1 KB · Java
RawDownload
/* * Copyright (c) 1999, 2008, 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.awt.*;import java.util.Vector;import javax.swing.event.*;import javax.swing.SizeRequirements; /** * A View that tries to flow it's children into some * partially constrained space.  This can be used to * build things like paragraphs, pages, etc.  The * flow is made up of the following pieces of functionality. * <ul> * <li>A logical set of child views, which as used as a * layout pool from which a physical view is formed. * <li>A strategy for translating the logical view to * a physical (flowed) view. * <li>Constraints for the strategy to work against. * <li>A physical structure, that represents the flow. * The children of this view are where the pieces of * of the logical views are placed to create the flow. * </ul> * * @author  Timothy Prinzing * @see     View * @since 1.3 */public abstract class FlowView extends BoxView {     /**     * Constructs a FlowView for the given element.     *     * @param elem the element that this view is responsible for     * @param axis may be either View.X_AXIS or View.Y_AXIS     */    public FlowView(Element elem, int axis) {        super(elem, axis);        layoutSpan = Integer.MAX_VALUE;        strategy = new FlowStrategy();    }     /**     * Fetches the axis along which views should be     * flowed.  By default, this will be the axis     * orthogonal to the axis along which the flow     * rows are tiled (the axis of the default flow     * rows themselves).  This is typically used     * by the <code>FlowStrategy</code>.     */    public int getFlowAxis() {        if (getAxis() == Y_AXIS) {            return X_AXIS;        }        return Y_AXIS;    }     /**     * Fetch the constraining span to flow against for     * the given child index.  This is called by the     * FlowStrategy while it is updating the flow.     * A flow can be shaped by providing different values     * for the row constraints.  By default, the entire     * span inside of the insets along the flow axis     * is returned.     *     * @param index the index of the row being updated.     *   This should be a value >= 0 and < getViewCount().     * @see #getFlowStart     */    public int getFlowSpan(int index) {        return layoutSpan;    }     /**     * Fetch the location along the flow axis that the     * flow span will start at.  This is called by the     * FlowStrategy while it is updating the flow.     * A flow can be shaped by providing different values     * for the row constraints.      * @param index the index of the row being updated.     *   This should be a value >= 0 and < getViewCount().     * @see #getFlowSpan     */    public int getFlowStart(int index) {        return 0;    }     /**     * Create a View that should be used to hold a     * a rows worth of children in a flow.  This is     * called by the FlowStrategy when new children     * are added or removed (i.e. rows are added or     * removed) in the process of updating the flow.     */    protected abstract View createRow();     // ---- BoxView methods -------------------------------------     /**     * Loads all of the children to initialize the view.     * This is called by the <code>setParent</code> method.     * This is reimplemented to not load any children directly     * (as they are created in the process of formatting).     * If the layoutPool variable is null, an instance of     * LogicalView is created to represent the logical view     * that is used in the process of formatting.     *     * @param f the view factory     */    protected void loadChildren(ViewFactory f) {        if (layoutPool == null) {            layoutPool = new LogicalView(getElement());        }        layoutPool.setParent(this);         // This synthetic insertUpdate call gives the strategy a chance        // to initialize.        strategy.insertUpdate(this, null, null);    }     /**     * Fetches the child view index representing the given position in     * the model.     *     * @param pos the position >= 0     * @return  index of the view representing the given position, or     *   -1 if no view represents that position     */    protected int getViewIndexAtPosition(int pos) {        if (pos >= getStartOffset() && (pos < getEndOffset())) {            for (int counter = 0; counter < getViewCount(); counter++) {                View v = getView(counter);                if(pos >= v.getStartOffset() &&                   pos < v.getEndOffset()) {                    return counter;                }            }        }        return -1;    }     /**     * Lays out the children.  If the span along the flow     * axis has changed, layout is marked as invalid which     * which will cause the superclass behavior to recalculate     * the layout along the box axis.  The FlowStrategy.layout     * method will be called to rebuild the flow rows as     * appropriate.  If the height of this view changes     * (determined by the perferred size along the box axis),     * a preferenceChanged is called.  Following all of that,     * the normal box layout of the superclass is performed.     *     * @param width  the width to lay out against >= 0.  This is     *   the width inside of the inset area.     * @param height the height to lay out against >= 0 This     *   is the height inside of the inset area.     */    protected void layout(int width, int height) {        final int faxis = getFlowAxis();        int newSpan;        if (faxis == X_AXIS) {            newSpan = width;        } else {            newSpan = height;        }        if (layoutSpan != newSpan) {            layoutChanged(faxis);            layoutChanged(getAxis());            layoutSpan = newSpan;        }         // repair the flow if necessary        if (! isLayoutValid(faxis)) {            final int heightAxis = getAxis();            int oldFlowHeight = (heightAxis == X_AXIS)? getWidth() : getHeight();            strategy.layout(this);            int newFlowHeight = (int) getPreferredSpan(heightAxis);            if (oldFlowHeight != newFlowHeight) {                View p = getParent();                if (p != null) {                    p.preferenceChanged(this, (heightAxis == X_AXIS), (heightAxis == Y_AXIS));                }                 // PENDING(shannonh)                // Temporary fix for 4250847                // Can be removed when TraversalContext is added                Component host = getContainer();                if (host != null) {                    //nb idk 12/12/2001 host should not be equal to null. We need to add assertion here                    host.repaint();                }            }        }         super.layout(width, height);    }     /**     * Calculate equirements along the minor axis.  This     * is implemented to forward the request to the logical     * view by calling getMinimumSpan, getPreferredSpan, and     * getMaximumSpan on it.     */    protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {        if (r == null) {            r = new SizeRequirements();        }        float pref = layoutPool.getPreferredSpan(axis);        float min = layoutPool.getMinimumSpan(axis);        // Don't include insets, Box.getXXXSpan will include them.        r.minimum = (int)min;        r.preferred = Math.max(r.minimum, (int) pref);        r.maximum = Integer.MAX_VALUE;        r.alignment = 0.5f;        return r;    }     // ---- View methods ----------------------------------------------------     /**     * Gives notification that something was inserted into the document     * in a location that this view is responsible for.     *     * @param changes 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 View#insertUpdate     */    public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {        layoutPool.insertUpdate(changes, a, f);        strategy.insertUpdate(this, changes, getInsideAllocation(a));    }     /**     * Gives notification that something was removed from the document     * in a location that this view is responsible for.     *     * @param changes 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 View#removeUpdate     */    public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {        layoutPool.removeUpdate(changes, a, f);        strategy.removeUpdate(this, changes, getInsideAllocation(a));    }     /**     * Gives notification from the document that attributes were changed     * in a location that this view is responsible for.     *     * @param changes 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 View#changedUpdate     */    public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {        layoutPool.changedUpdate(changes, a, f);        strategy.changedUpdate(this, changes, getInsideAllocation(a));    }     /** {@inheritDoc} */    public void setParent(View parent) {        super.setParent(parent);        if (parent == null                && layoutPool != null ) {            layoutPool.setParent(null);        }    }     // --- variables -----------------------------------------------     /**     * Default constraint against which the flow is     * created against.     */    protected int layoutSpan;     /**     * These are the views that represent the child elements     * of the element this view represents (The logical view     * to translate to a physical view).  These are not     * directly children of this view.  These are either     * placed into the rows directly or used for the purpose     * of breaking into smaller chunks, to form the physical     * view.     */    protected View layoutPool;     /**     * The behavior for keeping the flow updated.  By     * default this is a singleton shared by all instances     * of FlowView (FlowStrategy is stateless).  Subclasses     * can create an alternative strategy, which might keep     * state.     */    protected FlowStrategy strategy;     /**     * Strategy for maintaining the physical form     * of the flow.  The default implementation is     * completely stateless, and recalculates the     * entire flow if the layout is invalid on the     * given FlowView.  Alternative strategies can     * be implemented by subclassing, and might     * perform incrementatal repair to the layout     * or alternative breaking behavior.     * @since 1.3     */    public static class FlowStrategy {        Position damageStart = null;        Vector<View> viewBuffer;         void addDamage(FlowView fv, int offset) {            if (offset >= fv.getStartOffset() && offset < fv.getEndOffset()) {                if (damageStart == null || offset < damageStart.getOffset()) {                    try {                        damageStart = fv.getDocument().createPosition(offset);                    } catch (BadLocationException e) {                        // shouldn't happen since offset is inside view bounds                        assert(false);                    }                }            }        }         void unsetDamage() {            damageStart = null;        }         /**         * Gives notification that something was inserted into the document         * in a location that the given flow view is responsible for.  The         * strategy should update the appropriate changed region (which         * depends upon the strategy used for repair).         *         * @param e the change information from the associated document         * @param alloc the current allocation of the view inside of the insets.         *   This value will be null if the view has not yet been displayed.         * @see View#insertUpdate         */        public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {            // FlowView.loadChildren() makes a synthetic call into this,            // passing null as e            if (e != null) {                addDamage(fv, e.getOffset());            }             if (alloc != null) {                Component host = fv.getContainer();                if (host != null) {                    host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);                }            } else {                fv.preferenceChanged(null, true, true);            }        }         /**         * Gives notification that something was removed from the document         * in a location that the given flow view is responsible for.         *         * @param e the change information from the associated document         * @param alloc the current allocation of the view inside of the insets.         * @see View#removeUpdate         */        public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {            addDamage(fv, e.getOffset());            if (alloc != null) {                Component host = fv.getContainer();                if (host != null) {                    host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);                }            } else {                fv.preferenceChanged(null, true, true);            }        }         /**         * Gives notification from the document that attributes were changed         * in a location that this view is responsible for.         *         * @param fv     the <code>FlowView</code> containing the changes         * @param e      the <code>DocumentEvent</code> describing the changes         *               done to the Document         * @param alloc  Bounds of the View         * @see View#changedUpdate         */        public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {            addDamage(fv, e.getOffset());            if (alloc != null) {                Component host = fv.getContainer();                if (host != null) {                    host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);                }            } else {                fv.preferenceChanged(null, true, true);            }        }         /**         * This method gives flow strategies access to the logical         * view of the FlowView.         */        protected View getLogicalView(FlowView fv) {            return fv.layoutPool;        }         /**         * Update the flow on the given FlowView.  By default, this causes         * all of the rows (child views) to be rebuilt to match the given         * constraints for each row.  This is called by a FlowView.layout         * to update the child views in the flow.         *         * @param fv the view to reflow         */        public void layout(FlowView fv) {            View pool = getLogicalView(fv);            int rowIndex, p0;            int p1 = fv.getEndOffset();             if (fv.majorAllocValid) {                if (damageStart == null) {                    return;                }                // In some cases there's no view at position damageStart, so                // step back and search again. See 6452106 for details.                int offset = damageStart.getOffset();                while ((rowIndex = fv.getViewIndexAtPosition(offset)) < 0) {                    offset--;                }                if (rowIndex > 0) {                    rowIndex--;                }                p0 = fv.getView(rowIndex).getStartOffset();            } else {                rowIndex = 0;                p0 = fv.getStartOffset();            }            reparentViews(pool, p0);             viewBuffer = new Vector<View>(10, 10);            int rowCount = fv.getViewCount();            while (p0 < p1) {                View row;                if (rowIndex >= rowCount) {                    row = fv.createRow();                    fv.append(row);                } else {                    row = fv.getView(rowIndex);                }                p0 = layoutRow(fv, rowIndex, p0);                rowIndex++;            }            viewBuffer = null;             if (rowIndex < rowCount) {                fv.replace(rowIndex, rowCount - rowIndex, null);            }            unsetDamage();        }         /**         * Creates a row of views that will fit within the         * layout span of the row.  This is called by the layout method.         * This is implemented to fill the row by repeatedly calling         * the createView method until the available span has been         * exhausted, a forced break was encountered, or the createView         * method returned null.  If the remaining span was exhaused,         * the adjustRow method will be called to perform adjustments         * to the row to try and make it fit into the given span.         *         * @param rowIndex the index of the row to fill in with views.  The         *   row is assumed to be empty on entry.         * @param pos  The current position in the children of

Lines 1–500 of 871.