/* | 
|
 * Copyright (c) 1999, 2013, 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 preferred 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 requirements 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 incremental 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 exhausted, | 
|
         * 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 | 
|
         *   this views element from which to start. | 
|
         * @return the position to start the next row | 
|
*/  | 
|
protected int layoutRow(FlowView fv, int rowIndex, int pos) {  | 
|
View row = fv.getView(rowIndex);  | 
|
float x = fv.getFlowStart(rowIndex);  | 
|
float spanLeft = fv.getFlowSpan(rowIndex);  | 
|
int end = fv.getEndOffset();  | 
|
TabExpander te = (fv instanceof TabExpander) ? (TabExpander)fv : null;  | 
|
final int flowAxis = fv.getFlowAxis();  | 
|
int breakWeight = BadBreakWeight;  | 
|
float breakX = 0f;  | 
|
float breakSpan = 0f;  | 
|
int breakIndex = -1;  | 
|
int n = 0;  | 
|
viewBuffer.clear();  | 
|
while (pos < end && spanLeft >= 0) {  | 
|
View v = createView(fv, pos, (int)spanLeft, rowIndex);  | 
|
if (v == null) {  | 
|
break;  | 
|
}  | 
|
int bw = v.getBreakWeight(flowAxis, x, spanLeft);  | 
|
if (bw >= ForcedBreakWeight) {  | 
|
View w = v.breakView(flowAxis, pos, x, spanLeft);  | 
|
if (w != null) {  | 
|
viewBuffer.add(w);  | 
|
} else if (n == 0) {  | 
|
// if the view does not break, and it is the only view  | 
|
                        // in a row, use the whole view | 
|
viewBuffer.add(v);  | 
|
}  | 
|
break;  | 
|
} else if (bw >= breakWeight && bw > BadBreakWeight) {  | 
|
breakWeight = bw;  | 
|
breakX = x;  | 
|
breakSpan = spanLeft;  | 
|
breakIndex = n;  | 
|
}  | 
|
float chunkSpan;  | 
|
if (flowAxis == X_AXIS && v instanceof TabableView) {  | 
|
chunkSpan = ((TabableView)v).getTabbedSpan(x, te);  | 
|
                } else { | 
|
chunkSpan = v.getPreferredSpan(flowAxis);  | 
|
}  | 
|
if (chunkSpan > spanLeft && breakIndex >= 0) {  | 
|
                    // row is too long, and we may break | 
|
if (breakIndex < n) {  | 
|
v = viewBuffer.get(breakIndex);  | 
|
}  | 
|
for (int i = n - 1; i >= breakIndex; i--) {  | 
|
viewBuffer.remove(i);  | 
|
}  | 
|
v = v.breakView(flowAxis, v.getStartOffset(), breakX, breakSpan);  | 
|
}  | 
|
spanLeft -= chunkSpan;  | 
|
x += chunkSpan;  | 
|
viewBuffer.add(v);  | 
|
pos = v.getEndOffset();  | 
|
n++;  | 
|
}  | 
|
View[] views = new View[viewBuffer.size()];  | 
|
viewBuffer.toArray(views);  | 
|
row.replace(0, row.getViewCount(), views);  | 
|
return (views.length > 0 ? row.getEndOffset() : pos);  | 
|
}  | 
|
        /** | 
|
         * Adjusts the given row if possible to fit within the | 
|
         * layout span.  By default this will try to find the | 
|
         * highest break weight possible nearest the end of | 
|
         * the row.  If a forced break is encountered, the | 
|
         * break will be positioned there. | 
|
         * | 
|
         * @param rowIndex the row to adjust to the current layout | 
|
         *  span. | 
|
         * @param desiredSpan the current layout span >= 0 | 
|
         * @param x the location r starts at. | 
|
*/  | 
|
protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {  | 
|
final int flowAxis = fv.getFlowAxis();  | 
|
View r = fv.getView(rowIndex);  | 
|
int n = r.getViewCount();  | 
|
int span = 0;  | 
|
int bestWeight = BadBreakWeight;  | 
|
int bestSpan = 0;  | 
|
int bestIndex = -1;  | 
|
View v;  | 
|
for (int i = 0; i < n; i++) {  | 
|
v = r.getView(i);  | 
|
int spanLeft = desiredSpan - span;  | 
|
int w = v.getBreakWeight(flowAxis, x + span, spanLeft);  | 
|
if ((w >= bestWeight) && (w > BadBreakWeight)) {  | 
|
bestWeight = w;  | 
|
bestIndex = i;  | 
|
bestSpan = span;  | 
|
if (w >= ForcedBreakWeight) {  | 
|
// it's a forced break, so there is  | 
|
                        // no point in searching further. | 
|
break;  | 
|
}  | 
|
}  | 
|
span += v.getPreferredSpan(flowAxis);  | 
|
}  | 
|
if (bestIndex < 0) {  | 
|
// there is nothing that can be broken, leave  | 
|
                // it in it's current state. | 
|
return;  | 
|
}  | 
|
            // Break the best candidate view, and patch up the row. | 
|
int spanLeft = desiredSpan - bestSpan;  | 
|
v = r.getView(bestIndex);  | 
|
v = v.breakView(flowAxis, v.getStartOffset(), x + bestSpan, spanLeft);  | 
|
View[] va = new View[1];  | 
|
va[0] = v;  | 
|
View lv = getLogicalView(fv);  | 
|
int p0 = r.getView(bestIndex).getStartOffset();  | 
|
int p1 = r.getEndOffset();  | 
|
for (int i = 0; i < lv.getViewCount(); i++) {  | 
|
View tmpView = lv.getView(i);  | 
|
if (tmpView.getEndOffset() > p1) {  | 
|
break;  | 
|
}  | 
|
if (tmpView.getStartOffset() >= p0) {  | 
|
tmpView.setParent(lv);  | 
|
}  | 
|
}  | 
|
r.replace(bestIndex, n - bestIndex, va);  | 
|
}  | 
|
void reparentViews(View pool, int startPos) {  | 
|
int n = pool.getViewIndex(startPos, Position.Bias.Forward);  | 
|
if (n >= 0) {  | 
|
for (int i = n; i < pool.getViewCount(); i++) {  | 
|
pool.getView(i).setParent(pool);  | 
|
}  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Creates a view that can be used to represent the current piece | 
|
         * of the flow.  This can be either an entire view from the | 
|
         * logical view, or a fragment of the logical view. | 
|
         * | 
|
         * @param fv the view holding the flow | 
|
         * @param startOffset the start location for the view being created | 
|
         * @param spanLeft the about of span left to fill in the row | 
|
         * @param rowIndex the row the view will be placed into | 
|
*/  | 
|
protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {  | 
|
            // Get the child view that contains the given starting position | 
|
View lv = getLogicalView(fv);  | 
|
int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);  | 
|
View v = lv.getView(childIndex);  | 
|
if (startOffset==v.getStartOffset()) {  | 
|
                // return the entire view | 
|
return v;  | 
|
}  | 
|
            // return a fragment. | 
|
v = v.createFragment(startOffset, v.getEndOffset());  | 
|
return v;  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * This class can be used to represent a logical view for | 
|
     * a flow.  It keeps the children updated to reflect the state | 
|
     * of the model, gives the logical child views access to the | 
|
     * view hierarchy, and calculates a preferred span.  It doesn't | 
|
     * do any rendering, layout, or model/view translation. | 
|
*/  | 
|
static class LogicalView extends CompositeView {  | 
|
LogicalView(Element elem) {  | 
|
super(elem);  | 
|
}  | 
|
        protected int getViewIndexAtPosition(int pos) { | 
|
Element elem = getElement();  | 
|
if (elem.isLeaf()) {  | 
|
return 0;  | 
|
}  | 
|
return super.getViewIndexAtPosition(pos);  | 
|
}  | 
|
protected void loadChildren(ViewFactory f) {  | 
|
Element elem = getElement();  | 
|
if (elem.isLeaf()) {  | 
|
View v = new LabelView(elem);  | 
|
append(v);  | 
|
            } else { | 
|
super.loadChildren(f);  | 
|
}  | 
|
}  | 
|
        /** | 
|
         * Fetches the attributes to use when rendering.  This view | 
|
         * isn't directly responsible for an element so it returns | 
|
         * the outer classes attributes. | 
|
*/  | 
|
public AttributeSet getAttributes() {  | 
|
View p = getParent();  | 
|
return (p != null) ? p.getAttributes() : null;  | 
|
}  | 
|
        /** | 
|
         * Determines the preferred span for this view along an | 
|
         * axis. | 
|
         * | 
|
         * @param axis may be either View.X_AXIS or View.Y_AXIS | 
|
         * @return   the span the view would like to be rendered into. | 
|
         *           Typically the view is told to render into the span | 
|
         *           that is returned, although there is no guarantee. | 
|
         *           The parent may choose to resize or break the view. | 
|
         * @see View#getPreferredSpan | 
|
*/  | 
|
        public float getPreferredSpan(int axis) { | 
|
float maxpref = 0;  | 
|
float pref = 0;  | 
|
int n = getViewCount();  | 
|
for (int i = 0; i < n; i++) {  | 
|
View v = getView(i);  | 
|
pref += v.getPreferredSpan(axis);  | 
|
if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) >= ForcedBreakWeight) {  | 
|
maxpref = Math.max(maxpref, pref);  | 
|
pref = 0;  | 
|
}  | 
|
}  | 
|
maxpref = Math.max(maxpref, pref);  | 
|
return maxpref;  | 
|
}  | 
|
        /** | 
|
         * Determines the minimum span for this view along an | 
|
         * axis.  The is implemented to find the minimum unbreakable | 
|
         * span. | 
|
         * | 
|
         * @param axis may be either View.X_AXIS or View.Y_AXIS | 
|
         * @return  the span the view would like to be rendered into. | 
|
         *           Typically the view is told to render into the span | 
|
         *           that is returned, although there is no guarantee. | 
|
         *           The parent may choose to resize or break the view. | 
|
         * @see View#getPreferredSpan | 
|
*/  | 
|
        public float getMinimumSpan(int axis) { | 
|
float maxmin = 0;  | 
|
float min = 0;  | 
|
boolean nowrap = false;  | 
|
int n = getViewCount();  | 
|
for (int i = 0; i < n; i++) {  | 
|
View v = getView(i);  | 
|
if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) == BadBreakWeight) {  | 
|
min += v.getPreferredSpan(axis);  | 
|
nowrap = true;  | 
|
} else if (nowrap) {  | 
|
maxmin = Math.max(min, maxmin);  | 
|
nowrap = false;  | 
|
min = 0;  | 
|
}  | 
|
if (v instanceof ComponentView) {  | 
|
maxmin = Math.max(maxmin, v.getMinimumSpan(axis));  | 
|
}  | 
|
}  | 
|
maxmin = Math.max(maxmin, min);  | 
|
return maxmin;  | 
|
}  | 
|
        /** | 
|
         * Forward the DocumentEvent to the given child view.  This | 
|
         * is implemented to reparent the child to the logical view | 
|
         * (the children may have been parented by a row in the flow | 
|
         * if they fit without breaking) and then execute the superclass | 
|
         * behavior. | 
|
         * | 
|
         * @param v the child view to forward the event to. | 
|
         * @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 #forwardUpdate | 
|
         * @since 1.3 | 
|
*/  | 
|
protected void forwardUpdateToView(View v, DocumentEvent e,  | 
|
Shape a, ViewFactory f) {  | 
|
View parent = v.getParent();  | 
|
v.setParent(this);  | 
|
super.forwardUpdateToView(v, e, a, f);  | 
|
v.setParent(parent);  | 
|
}  | 
|
        /** {@inheritDoc} */ | 
|
@Override  | 
|
protected void forwardUpdate(DocumentEvent.ElementChange ec,  | 
|
DocumentEvent e, Shape a, ViewFactory f) {  | 
|
// Update the view responsible for the changed element by invocation of  | 
|
            // super method. | 
|
super.forwardUpdate(ec, e, a, f);  | 
|
// Re-calculate the update indexes and update the views followed by  | 
|
// the changed place. Note: we update the views only when insertion or  | 
|
            // removal takes place. | 
|
DocumentEvent.EventType type = e.getType();  | 
|
if (type == DocumentEvent.EventType.INSERT ||  | 
|
type == DocumentEvent.EventType.REMOVE) {  | 
|
firstUpdateIndex = Math.min((lastUpdateIndex + 1), (getViewCount() - 1));  | 
|
lastUpdateIndex = Math.max((getViewCount() - 1), 0);  | 
|
for (int i = firstUpdateIndex; i <= lastUpdateIndex; i++) {  | 
|
View v = getView(i);  | 
|
if (v != null) {  | 
|
v.updateAfterChange();  | 
|
}  | 
|
}  | 
|
}  | 
|
}  | 
|
// The following methods don't do anything useful, they  | 
|
// simply keep the class from being abstract.  | 
|
        /** | 
|
         * Renders using the given rendering surface and area on that | 
|
         * surface.  This is implemented to do nothing, the logical | 
|
         * view is never visible. | 
|
         * | 
|
         * @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) {  | 
|
}  | 
|
        /** | 
|
         * Tests whether a point lies before the rectangle range. | 
|
         * Implemented to return false, as hit detection is not | 
|
         * performed on the logical view. | 
|
         * | 
|
         * @param x the X coordinate >= 0 | 
|
         * @param y the Y coordinate >= 0 | 
|
         * @param alloc the rectangle | 
|
         * @return true if the point is before the specified range | 
|
*/  | 
|
protected boolean isBefore(int x, int y, Rectangle alloc) {  | 
|
return false;  | 
|
}  | 
|
        /** | 
|
         * Tests whether a point lies after the rectangle range. | 
|
         * Implemented to return false, as hit detection is not | 
|
         * performed on the logical view. | 
|
         * | 
|
         * @param x the X coordinate >= 0 | 
|
         * @param y the Y coordinate >= 0 | 
|
         * @param alloc the rectangle | 
|
         * @return true if the point is after the specified range | 
|
*/  | 
|
protected boolean isAfter(int x, int y, Rectangle alloc) {  | 
|
return false;  | 
|
}  | 
|
        /** | 
|
         * Fetches the child view at the given point. | 
|
         * Implemented to return null, as hit detection is not | 
|
         * performed on the logical view. | 
|
         * | 
|
         * @param x the X coordinate >= 0 | 
|
         * @param y the Y coordinate >= 0 | 
|
         * @param alloc the parent's allocation on entry, which should | 
|
         *   be changed to the child's allocation on exit | 
|
         * @return the child view | 
|
*/  | 
|
protected View getViewAtPoint(int x, int y, Rectangle alloc) {  | 
|
return null;  | 
|
}  | 
|
        /** | 
|
         * Returns the allocation for a given child. | 
|
         * Implemented to do nothing, as the logical view doesn't | 
|
         * perform layout on the children. | 
|
         * | 
|
         * @param index the index of the child, >= 0 && < getViewCount() | 
|
         * @param a  the allocation to the interior of the box on entry, | 
|
         *   and the allocation of the child view at the index on exit. | 
|
*/  | 
|
protected void childAllocation(int index, Rectangle a) {  | 
|
}  | 
|
}  | 
|
}  |