1420 lines · 48.8 KB · Java
RawDownload
        /**         * Fetches the child view index at the given point.         * This is called by the various View methods that         * need to calculate which child to forward a message         * to.  This should be called by a block synchronized         * on this object, and would typically be followed         * with one or more calls to getChildAllocation that         * should also be in the synchronized block.         *         * @param x the X coordinate >= 0         * @param y the Y coordinate >= 0         * @param a the allocation to the View         * @return the nearest child index         */        public int getViewIndexAtPoint(float x, float y, Shape a) {            setAllocation(a);            float targetOffset = (axis == X_AXIS) ? x - lastAlloc.x : y - lastAlloc.y;            int index = getViewIndexAtVisualOffset(targetOffset);            return index;        }         /**         * Fetch the allocation to use for a child view.         * <em>This does not update the offsets in the ChildState         * records.</em>         */        protected Shape getChildAllocation(int index) {            ChildState cs = getChildState(index);            if (! cs.isLayoutValid()) {                cs.run();            }            if (axis == X_AXIS) {                childAlloc.x = lastAlloc.x + (int) cs.getMajorOffset();                childAlloc.y = lastAlloc.y + (int) cs.getMinorOffset();                childAlloc.width = (int) cs.getMajorSpan();                childAlloc.height = (int) cs.getMinorSpan();            } else {                childAlloc.y = lastAlloc.y + (int) cs.getMajorOffset();                childAlloc.x = lastAlloc.x + (int) cs.getMinorOffset();                childAlloc.height = (int) cs.getMajorSpan();                childAlloc.width = (int) cs.getMinorSpan();            }            childAlloc.x += (int)getLeftInset();            childAlloc.y += (int)getRightInset();            return childAlloc;        }         /**         * Copy the currently allocated shape into the Rectangle         * used to store the current allocation.  This would be         * a floating point rectangle in a Java2D-specific implmentation.         */        protected void setAllocation(Shape a) {            if (a instanceof Rectangle) {                lastAlloc.setBounds((Rectangle) a);            } else {                lastAlloc.setBounds(a.getBounds());            }            setSize(lastAlloc.width, lastAlloc.height);        }         /**         * Locate the view responsible for an offset into the box         * along the major axis.  Make sure that offsets are set         * on the ChildState objects up to the given target span         * past the desired offset.         *         * @return   index of the view representing the given visual         *   location (targetOffset), or -1 if no view represents         *   that location         */        protected int getViewIndexAtVisualOffset(float targetOffset) {            int n = getViewCount();            if (n > 0) {                boolean lastValid = (lastValidOffset != null);                 if (lastValidOffset == null) {                    lastValidOffset = getChildState(0);                }                if (targetOffset > majorSpan) {                    // should only get here on the first time display.                    if (!lastValid) {                        return 0;                    }                    int pos = lastValidOffset.getChildView().getStartOffset();                    int index = getViewIndex(pos, Position.Bias.Forward);                    return index;                } else if (targetOffset > lastValidOffset.getMajorOffset()) {                    // roll offset calculations forward                    return updateChildOffsets(targetOffset);                } else {                    // no changes prior to the needed offset                    // this should be a binary search                    float offs = 0f;                    for (int i = 0; i < n; i++) {                        ChildState cs = getChildState(i);                        float nextOffs = offs + cs.getMajorSpan();                        if (targetOffset < nextOffs) {                            return i;                        }                        offs = nextOffs;                    }                }            }            return n - 1;        }         /**         * Move the location of the last offset calculation forward         * to the desired offset.         */        int updateChildOffsets(float targetOffset) {            int n = getViewCount();            int targetIndex = n - 1;            int pos = lastValidOffset.getChildView().getStartOffset();            int startIndex = getViewIndex(pos, Position.Bias.Forward);            float start = lastValidOffset.getMajorOffset();            float lastOffset = start;            for (int i = startIndex; i < n; i++) {                ChildState cs = getChildState(i);                cs.setMajorOffset(lastOffset);                lastOffset += cs.getMajorSpan();                if (targetOffset < lastOffset) {                    targetIndex = i;                    lastValidOffset = cs;                    break;                }            }             return targetIndex;        }         /**         * Move the location of the last offset calculation forward         * to the desired index.         */        void updateChildOffsetsToIndex(int index) {            int pos = lastValidOffset.getChildView().getStartOffset();            int startIndex = getViewIndex(pos, Position.Bias.Forward);            float lastOffset = lastValidOffset.getMajorOffset();            for (int i = startIndex; i <= index; i++) {                ChildState cs = getChildState(i);                cs.setMajorOffset(lastOffset);                lastOffset += cs.getMajorSpan();            }        }         boolean intersectsClip(Shape childAlloc, Rectangle clip) {            Rectangle cs = (childAlloc instanceof Rectangle) ?                (Rectangle) childAlloc : childAlloc.getBounds();            if (cs.intersects(clip)) {                // Make sure that lastAlloc also contains childAlloc,                // this will be false if haven't yet flushed changes.                return lastAlloc.intersects(cs);            }            return false;        }         /**         * The location of the last offset calculation         * that is valid.         */        protected ChildState lastValidOffset;         /**         * The last seen allocation (for repainting when changes         * are flushed upward).         */        protected Rectangle lastAlloc;         /**         * A shape to use for the child allocation to avoid         * creating a lot of garbage.         */        protected Rectangle childAlloc;    }     /**     * A record representing the layout state of a     * child view.  It is runnable as a task on another     * thread.  All access to the child view that is     * based upon a read-lock on the model should synchronize     * on this object (i.e. The layout thread and the GUI     * thread can both have a read lock on the model at the     * same time and are not protected from each other).     * Access to a child view hierarchy is serialized via     * synchronization on the ChildState instance.     * @since 1.3     */    public class ChildState implements Runnable {         /**         * Construct a child status.  This needs to start         * out as fairly large so we don't falsely begin with         * the idea that all of the children are visible.         * @since 1.4         */        public ChildState(View v) {            child = v;            minorValid = false;            majorValid = false;            childSizeValid = false;            child.setParent(AsyncBoxView.this);        }         /**         * Fetch the child view this record represents         */        public View getChildView() {            return child;        }         /**         * Update the child state.  This should be         * called by the thread that desires to spend         * time updating the child state (intended to         * be the layout thread).         * <p>         * This aquires a read lock on the associated         * document for the duration of the update to         * ensure the model is not changed while it is         * operating.  The first thing to do would be         * to see if any work actually needs to be done.         * The following could have conceivably happened         * while the state was waiting to be updated:         * <ol>         * <li>The child may have been removed from the         * view hierarchy.         * <li>The child may have been updated by a         * higher priority operation (i.e. the child         * may have become visible).         * </ol>         */        public void run () {            AbstractDocument doc = (AbstractDocument) getDocument();            try {                doc.readLock();                if (minorValid && majorValid && childSizeValid) {                    // nothing to do                    return;                }                if (child.getParent() == AsyncBoxView.this) {                    // this may overwrite anothers threads cached                    // value for actively changing... but that just                    // means it won't use the cache if there is an                    // overwrite.                    synchronized(AsyncBoxView.this) {                        changing = this;                    }                    updateChild();                    synchronized(AsyncBoxView.this) {                        changing = null;                    }                     // setting the child size on the minor axis                    // may have caused it to change it's preference                    // along the major axis.                    updateChild();                }            } finally {                doc.readUnlock();            }        }         void updateChild() {            boolean minorUpdated = false;            synchronized(this) {                if (! minorValid) {                    int minorAxis = getMinorAxis();                    min = child.getMinimumSpan(minorAxis);                    pref = child.getPreferredSpan(minorAxis);                    max = child.getMaximumSpan(minorAxis);                    minorValid = true;                    minorUpdated = true;                }            }            if (minorUpdated) {                minorRequirementChange(this);            }             boolean majorUpdated = false;            float delta = 0.0f;            synchronized(this) {                if (! majorValid) {                    float old = span;                    span = child.getPreferredSpan(axis);                    delta = span - old;                    majorValid = true;                    majorUpdated = true;                }            }            if (majorUpdated) {                majorRequirementChange(this, delta);                locator.childChanged(this);            }             synchronized(this) {                if (! childSizeValid) {                    float w;                    float h;                    if (axis == X_AXIS) {                        w = span;                        h = getMinorSpan();                    } else {                        w = getMinorSpan();                        h = span;                    }                    childSizeValid = true;                    child.setSize(w, h);                }            }         }         /**         * What is the span along the minor axis.         */        public float getMinorSpan() {            if (max < minorSpan) {                return max;            }            // make it the target width, or as small as it can get.            return Math.max(min, minorSpan);        }         /**         * What is the offset along the minor axis         */        public float getMinorOffset() {            if (max < minorSpan) {                // can't make the child this wide, align it                float align = child.getAlignment(getMinorAxis());                return ((minorSpan - max) * align);            }            return 0f;        }         /**         * What is the span along the major axis.         */        public float getMajorSpan() {            return span;        }         /**         * Get the offset along the major axis         */        public float getMajorOffset() {            return offset;        }         /**         * This method should only be called by the ChildLocator,         * it is simply a convenient place to hold the cached         * location.         */        public void setMajorOffset(float offs) {            offset = offs;        }         /**         * Mark preferences changed for this child.         *         * @param width true if the width preference has changed         * @param height true if the height preference has changed         * @see javax.swing.JComponent#revalidate         */        public void preferenceChanged(boolean width, boolean height) {            if (axis == X_AXIS) {                if (width) {                    majorValid = false;                }                if (height) {                    minorValid = false;                }            } else {                if (width) {                    minorValid = false;                }                if (height) {                    majorValid = false;                }            }            childSizeValid = false;        }         /**         * Has the child view been laid out.         */        public boolean isLayoutValid() {            return (minorValid && majorValid && childSizeValid);        }         // minor axis        private float min;        private float pref;        private float max;        private boolean minorValid;         // major axis        private float span;        private float offset;        private boolean majorValid;         private View child;        private boolean childSizeValid;    }     /**     * Task to flush requirement changes upward     */    class FlushTask implements Runnable {         public void run() {            flushRequirementChanges();        }     } }

Lines 1001–1420 of 1420. Previous lines