1420 lines · 48.8 KB · Java
RawDownload
/* * Copyright (c) 1999, 2011, 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.util.*;import java.util.List;import java.awt.*;import javax.swing.SwingUtilities;import javax.swing.event.DocumentEvent; /** * A box that does layout asynchronously.  This * is useful to keep the GUI event thread moving by * not doing any layout on it.  The layout is done * on a granularity of operations on the child views. * After each child view is accessed for some part * of layout (a potentially time consuming operation) * the remaining tasks can be abandoned or a new higher * priority task (i.e. to service a synchronous request * or a visible area) can be taken on. * <p> * While the child view is being accessed * a read lock is aquired on the associated document * so that the model is stable while being accessed. * * @author  Timothy Prinzing * @since   1.3 */public class AsyncBoxView extends View {     /**     * Construct a box view that does asynchronous layout.     *     * @param elem the element of the model to represent     * @param axis the axis to tile along.  This can be     *  either X_AXIS or Y_AXIS.     */    public AsyncBoxView(Element elem, int axis) {        super(elem);        stats = new ArrayList<ChildState>();        this.axis = axis;        locator = new ChildLocator();        flushTask = new FlushTask();        minorSpan = Short.MAX_VALUE;        estimatedMajorSpan = false;    }     /**     * Fetch the major axis (the axis the children     * are tiled along).  This will have a value of     * either X_AXIS or Y_AXIS.     */    public int getMajorAxis() {        return axis;    }     /**     * Fetch the minor axis (the axis orthoginal     * to the tiled axis).  This will have a value of     * either X_AXIS or Y_AXIS.     */    public int getMinorAxis() {        return (axis == X_AXIS) ? Y_AXIS : X_AXIS;    }     /**     * Get the top part of the margin around the view.     */    public float getTopInset() {        return topInset;    }     /**     * Set the top part of the margin around the view.     *     * @param i the value of the inset     */    public void setTopInset(float i) {        topInset = i;    }     /**     * Get the bottom part of the margin around the view.     */    public float getBottomInset() {        return bottomInset;    }     /**     * Set the bottom part of the margin around the view.     *     * @param i the value of the inset     */    public void setBottomInset(float i) {        bottomInset = i;    }     /**     * Get the left part of the margin around the view.     */    public float getLeftInset() {        return leftInset;    }     /**     * Set the left part of the margin around the view.     *     * @param i the value of the inset     */    public void setLeftInset(float i) {        leftInset = i;    }     /**     * Get the right part of the margin around the view.     */    public float getRightInset() {        return rightInset;    }     /**     * Set the right part of the margin around the view.     *     * @param i the value of the inset     */    public void setRightInset(float i) {        rightInset = i;    }     /**     * Fetch the span along an axis that is taken up by the insets.     *     * @param axis the axis to determine the total insets along,     *  either X_AXIS or Y_AXIS.     * @since 1.4     */    protected float getInsetSpan(int axis) {        float margin = (axis == X_AXIS) ?            getLeftInset() + getRightInset() : getTopInset() + getBottomInset();        return margin;    }     /**     * Set the estimatedMajorSpan property that determines if the     * major span should be treated as being estimated.  If this     * property is true, the value of setSize along the major axis     * will change the requirements along the major axis and incremental     * changes will be ignored until all of the children have been updated     * (which will cause the property to automatically be set to false).     * If the property is false the value of the majorSpan will be     * considered to be accurate and incremental changes will be     * added into the total as they are calculated.     *     * @since 1.4     */    protected void setEstimatedMajorSpan(boolean isEstimated) {        estimatedMajorSpan = isEstimated;    }     /**     * Is the major span currently estimated?     *     * @since 1.4     */    protected boolean getEstimatedMajorSpan() {        return estimatedMajorSpan;    }     /**     * Fetch the object representing the layout state of     * of the child at the given index.     *     * @param index the child index.  This should be a     *   value >= 0 and < getViewCount().     */    protected ChildState getChildState(int index) {        synchronized(stats) {            if ((index >= 0) && (index < stats.size())) {                return stats.get(index);            }            return null;        }    }     /**     * Fetch the queue to use for layout.     */    protected LayoutQueue getLayoutQueue() {        return LayoutQueue.getDefaultQueue();    }     /**     * New ChildState records are created through     * this method to allow subclasses the extend     * the ChildState records to do/hold more     */    protected ChildState createChildState(View v) {        return new ChildState(v);    }     /**     * Requirements changed along the major axis.     * This is called by the thread doing layout for     * the given ChildState object when it has completed     * fetching the child views new preferences.     * Typically this would be the layout thread, but     * might be the event thread if it is trying to update     * something immediately (such as to perform a     * model/view translation).     * <p>     * This is implemented to mark the major axis as having     * changed so that a future check to see if the requirements     * need to be published to the parent view will consider     * the major axis.  If the span along the major axis is     * not estimated, it is updated by the given delta to reflect     * the incremental change.  The delta is ignored if the     * major span is estimated.     */    protected synchronized void majorRequirementChange(ChildState cs, float delta) {        if (estimatedMajorSpan == false) {            majorSpan += delta;        }        majorChanged = true;    }     /**     * Requirements changed along the minor axis.     * This is called by the thread doing layout for     * the given ChildState object when it has completed     * fetching the child views new preferences.     * Typically this would be the layout thread, but     * might be the GUI thread if it is trying to update     * something immediately (such as to perform a     * model/view translation).     */    protected synchronized void minorRequirementChange(ChildState cs) {        minorChanged = true;    }     /**     * Publish the changes in preferences upward to the parent     * view.  This is normally called by the layout thread.     */    protected void flushRequirementChanges() {        AbstractDocument doc = (AbstractDocument) getDocument();        try {            doc.readLock();             View parent = null;            boolean horizontal = false;            boolean vertical = false;             synchronized(this) {                // perform tasks that iterate over the children while                // preventing the collection from changing.                synchronized(stats) {                    int n = getViewCount();                    if ((n > 0) && (minorChanged || estimatedMajorSpan)) {                        LayoutQueue q = getLayoutQueue();                        ChildState min = getChildState(0);                        ChildState pref = getChildState(0);                        float span = 0f;                        for (int i = 1; i < n; i++) {                            ChildState cs = getChildState(i);                            if (minorChanged) {                                if (cs.min > min.min) {                                    min = cs;                                }                                if (cs.pref > pref.pref) {                                    pref = cs;                                }                            }                            if (estimatedMajorSpan) {                                span += cs.getMajorSpan();                            }                        }                         if (minorChanged) {                            minRequest = min;                            prefRequest = pref;                        }                        if (estimatedMajorSpan) {                            majorSpan = span;                            estimatedMajorSpan = false;                            majorChanged = true;                        }                    }                }                 // message preferenceChanged                if (majorChanged || minorChanged) {                    parent = getParent();                    if (parent != null) {                        if (axis == X_AXIS) {                            horizontal = majorChanged;                            vertical = minorChanged;                        } else {                            vertical = majorChanged;                            horizontal = minorChanged;                        }                    }                    majorChanged = false;                    minorChanged = false;                }            }             // propagate a preferenceChanged, using the            // layout thread.            if (parent != null) {                parent.preferenceChanged(this, horizontal, vertical);                 // probably want to change this to be more exact.                Component c = getContainer();                if (c != null) {                    c.repaint();                }            }        } finally {            doc.readUnlock();        }    }     /**     * Calls the superclass to update the child views, and     * updates the status records for the children.  This     * is expected to be called while a write lock is held     * on the model so that interaction with the layout     * thread will not happen (i.e. the layout thread     * acquires a read lock before doing anything).     *     * @param offset the starting offset into the child views >= 0     * @param length the number of existing views to replace >= 0     * @param views the child views to insert     */    public void replace(int offset, int length, View[] views) {        synchronized(stats) {            // remove the replaced state records            for (int i = 0; i < length; i++) {                ChildState cs = stats.remove(offset);                float csSpan = cs.getMajorSpan();                 cs.getChildView().setParent(null);                if (csSpan != 0) {                    majorRequirementChange(cs, -csSpan);                }            }             // insert the state records for the new children            LayoutQueue q = getLayoutQueue();            if (views != null) {                for (int i = 0; i < views.length; i++) {                    ChildState s = createChildState(views[i]);                    stats.add(offset + i, s);                    q.addTask(s);                }            }             // notify that the size changed            q.addTask(flushTask);        }    }     /**     * Loads all of the children to initialize the view.     * This is called by the {@link #setParent setParent}     * method.  Subclasses can reimplement this to initialize     * their child views in a different manner.  The default     * implementation creates a child view for each     * child element.     * <p>     * Normally a write-lock is held on the Document while     * the children are being changed, which keeps the rendering     * and layout threads safe.  The exception to this is when     * the view is initialized to represent an existing element     * (via this method), so it is synchronized to exclude     * preferenceChanged while we are initializing.     *     * @param f the view factory     * @see #setParent     */    protected void loadChildren(ViewFactory f) {        Element e = getElement();        int n = e.getElementCount();        if (n > 0) {            View[] added = new View[n];            for (int i = 0; i < n; i++) {                added[i] = f.create(e.getElement(i));            }            replace(0, 0, added);        }    }     /**     * Fetches the child view index representing the given position in     * the model.  This is implemented to fetch the view in the case     * where there is a child view for each child element.     *     * @param pos the position >= 0     * @return  index of the view representing the given position, or     *   -1 if no view represents that position     */    protected synchronized int getViewIndexAtPosition(int pos, Position.Bias b) {        boolean isBackward = (b == Position.Bias.Backward);        pos = (isBackward) ? Math.max(0, pos - 1) : pos;        Element elem = getElement();        return elem.getElementIndex(pos);    }     /**     * Update the layout in response to receiving notification of     * change from the model.  This is implemented to note the     * change on the ChildLocator so that offsets of the children     * will be correctly computed.     *     * @param ec changes to the element this view is responsible     *  for (may be null if there were no changes).     * @param e the change information from the associated document     * @param a the current allocation of the view     * @see #insertUpdate     * @see #removeUpdate     * @see #changedUpdate     */    protected void updateLayout(DocumentEvent.ElementChange ec,                                    DocumentEvent e, Shape a) {        if (ec != null) {            // the newly inserted children don't have a valid            // offset so the child locator needs to be messaged            // that the child prior to the new children has            // changed size.            int index = Math.max(ec.getIndex() - 1, 0);            ChildState cs = getChildState(index);            locator.childChanged(cs);        }    }     // --- View methods ------------------------------------     /**     * Sets the parent of the view.     * This is reimplemented to provide the superclass     * behavior as well as calling the <code>loadChildren</code>     * method if this view does not already have children.     * The children should not be loaded in the     * constructor because the act of setting the parent     * may cause them to try to search up the hierarchy     * (to get the hosting Container for example).     * If this view has children (the view is being moved     * from one place in the view hierarchy to another),     * the <code>loadChildren</code> method will not be called.     *     * @param parent the parent of the view, null if none     */    public void setParent(View parent) {        super.setParent(parent);        if ((parent != null) && (getViewCount() == 0)) {            ViewFactory f = getViewFactory();            loadChildren(f);        }    }     /**     * Child views can call this on the parent to indicate that     * the preference has changed and should be reconsidered     * for layout.  This is reimplemented to queue new work     * on the layout thread.  This method gets messaged from     * multiple threads via the children.     *     * @param child the child view     * @param width true if the width preference has changed     * @param height true if the height preference has changed     * @see javax.swing.JComponent#revalidate     */    public synchronized void preferenceChanged(View child, boolean width, boolean height) {        if (child == null) {            getParent().preferenceChanged(this, width, height);        } else {            if (changing != null) {                View cv = changing.getChildView();

Lines 1–500 of 1420.