| /* |
| * 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) Math.ceil(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} */ |
| @Override |
| public void preferenceChanged(View child, boolean width, boolean height) { |
| super.preferenceChanged(child, width, height); |
| if (strategy instanceof TextLayoutStrategy) { |
| ((TextLayoutStrategy) strategy).syncFRC(this); |
| } |
| } |
| |
| /** {@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) { |
| } |
| } |
| |
| |
| } |