* 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); } // 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) { } } }Lines 501–871 of 871. Previous lines
* 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); } // 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) { } } }