The attached patch fixes a lot of issues with styled text in Swing and
make it more usable with respect to performance and rendering. Try
BeanShell for example.


2006-02-09  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/text/BoxView.java
        (myAxis): Made field private.
        (xLayoutValid): Replaced by layoutValid array.
        (yLayoutValid): Replaced by layoutValid array.
        (layoutValid): New field.
        (spansX): Replaced by spans array.
        (spansY): Replaced by spans array.
        (spans): New field.
        (offsetsX): Replaced by offsets array.
        (offsetsY): Replaced by offsets array.
        (offsets): New field.
        (requirements): New field.
        (BoxView): Initialize new fields.
        (layoutChanged): Rewritten to use the layoutValid array.
        (isLayoutValid): Rewritten to use the layoutValid array.
        (replace): Use the new arrays.
        (getPreferredSpan): Rewritten to call calculateXXXRequirements
        instead of baselineRequirements.
        (baselineRequirements): Rewritten to calculate baseline requirements.
        (baselineLayout): Rewritten to calculate baseline layout.
        (childAllocation): Use new arrays.
        (layout): Rewritten. Only update the layout if necessary.
        (layoutMajorAxis): Directly set layoutValid.
        (layoutMinorAxis): Directly set layoutValid. Use cached size
        requirements.
        (getWidth): Use new span array.
        (getHeight): Likewise.
        (setSize): Rewritten to simply call layout().
        (validateLayout): Removed unneeded method.
        (getSpan): Use new arrays.
        (getOffset): Use new arrays.
        (getAlignment): Use cached requirements if possible.
        (preferenceChanged): Use new arrays.
        * javax/swing/text/FlowView.java
        (FlowStrategy.insertUpdate): Do nothing here.
        (FlowStrategy.removeUpdate): Do nothing here.
        (FlowStrategy.changedUpdate): Do nothing here.
        (FlowStrategy.layoutRow): Rewritten.
        (FlowStrategy.createView): Rewritten.
        (FlowStrategy.adjustRow): New method.
        (LogicalView.getViewIndex): Fixed condition for finding child
        view.
        (layoutDirty): New field indicating the state of the layout.
        (FlowView): Initialize new field.
        (loadChildren): Set parent on logical view so that preferenceChanges
        get propagated upwards.
        (layout): Rewritten to match the specs.
        (insertUpdate): Set layout to dirty.
        (removeUpdate): Set layout to dirty.
        (changedUpdate): Set layout to dirty.
        * javax/swing/text/GlyphView.java
        (getBreakWeight): Rewritten to use the Utilities class. Commented
        out though because that is broken.
        (insertUpdate): Call preferenceChanged on this object instead of
        parent.
        * javax/swing/text/ParagraphView.java
        (Row.loadChildren): Overridden to be a noop to prevent initial
        creation of child views. This is carried out by the flow layout.
        * javax/swing/text/View.java
        (getPreferredSpan): Added API docs.
        (getResizeWeight): Added API docs.
        (getMaximumSpan): Added API docs. Rewritten to only have one exit
        point.
        (getMinimumSpan): Added API docs. Rewritten to return 0 when
        resizable instead of Integer.MAX_VALUE.
        (getAlignment): Added API docs.
        (replace): Added API docs.
        (forwardUpdate): Rewritten to only notify child views that need to
        be notified.

/Roman
Index: javax/swing/text/BoxView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/BoxView.java,v
retrieving revision 1.12
diff -u -r1.12 BoxView.java
--- javax/swing/text/BoxView.java	17 Nov 2005 20:56:38 -0000	1.12
+++ javax/swing/text/BoxView.java	9 Feb 2006 14:27:58 -0000
@@ -58,49 +58,32 @@
   /**
    * The axis along which this <code>BoxView</code> is laid out.
    */
-  int myAxis;
+  private int myAxis;
 
   /**
-   * Indicates wether the layout in X_AXIS is valid.
+   * Indicates if the layout is valid along X_AXIS or Y_AXIS.
    */
-  boolean xLayoutValid;
+  private boolean[] layoutValid = new boolean[2];
 
   /**
-   * Indicates whether the layout in Y_AXIS is valid.
+   * The spans along the X_AXIS and Y_AXIS.
    */
-  boolean yLayoutValid;
+  private int[][] spans = new int[2][];
 
   /**
-   * The spans in X direction of the children.
+   * The offsets of the children along the X_AXIS and Y_AXIS.
    */
-  int[] spansX;
+  private int[][] offsets = new int[2][];
 
   /**
-   * The spans in Y direction of the children.
+   * The size requirements along the X_AXIS and Y_AXIS.
    */
-  int[] spansY;
+  private SizeRequirements[] requirements = new SizeRequirements[2];
 
   /**
-   * The offsets of the children in X direction relative to this BoxView's
-   * inner bounds.
+   * The current span along X_AXIS or Y_AXIS.
    */
-  int[] offsetsX;
-
-  /**
-   * The offsets of the children in Y direction relative to this BoxView's
-   * inner bounds.
-   */
-  int[] offsetsY;
-
-  /**
-   * The current width.
-   */
-  int width;
-
-  /**
-   * The current height.
-   */
-  int height;
+  private int[] span = new int[2];
 
   /**
    * Creates a new <code>BoxView</code> for the given
@@ -114,17 +97,18 @@
   {
     super(element);
     myAxis = axis;
-    xLayoutValid = false;
-    yLayoutValid = false;
+    layoutValid[0] = false;
+    layoutValid[1] = false;
+    span[0] = 0;
+    span[1] = 0;
+    requirements[0] = new SizeRequirements();
+    requirements[1] = new SizeRequirements();
 
     // Initialize the cache arrays.
-    spansX = new int[0];
-    spansY = new int[0];
-    offsetsX = new int[0];
-    offsetsY = new int[0];
-
-    width = 0;
-    height = 0;
+    spans[0] = new int[0];
+    spans[1] = new int[0];
+    offsets[0] = new int[0];
+    offsets[1] = new int[0];
   }
 
   /**
@@ -166,17 +150,9 @@
    */
   public void layoutChanged(int axis)
   {
-    switch (axis)
-      {
-      case X_AXIS:
-        xLayoutValid = false;
-        break;
-      case Y_AXIS:
-        yLayoutValid = false;
-        break;
-      default:
-        throw new IllegalArgumentException("Invalid axis parameter.");
-      }
+    if (axis != X_AXIS && axis != Y_AXIS)
+      throw new IllegalArgumentException("Invalid axis parameter.");
+    layoutValid[axis] = false;
   }
 
   /**
@@ -193,19 +169,9 @@
    */
   protected boolean isLayoutValid(int axis)
   {
-    boolean valid = false;
-    switch (axis)
-      {
-      case X_AXIS:
-        valid = xLayoutValid;
-        break;
-      case Y_AXIS:
-        valid = yLayoutValid;
-        break;
-      default:
-        throw new IllegalArgumentException("Invalid axis parameter.");
-      }
-    return valid;
+    if (axis != X_AXIS && axis != Y_AXIS)
+      throw new IllegalArgumentException("Invalid axis parameter.");
+    return layoutValid[axis];
   }
 
   /**
@@ -242,40 +208,44 @@
    */
   public void replace(int offset, int length, View[] views)
   {
+    int numViews = 0;
+    if (views != null)
+      numViews = views.length;
+
     // Resize and copy data for cache arrays.
     // The spansX cache.
     int oldSize = getViewCount();
 
-    int[] newSpansX = new int[oldSize - length + views.length];
-    System.arraycopy(spansX, 0, newSpansX, 0, offset);
-    System.arraycopy(spansX, offset + length, newSpansX,
-                     offset + views.length,
+    int[] newSpansX = new int[oldSize - length + numViews];
+    System.arraycopy(spans[X_AXIS], 0, newSpansX, 0, offset);
+    System.arraycopy(spans[X_AXIS], offset + length, newSpansX,
+                     offset + numViews,
                      oldSize - (offset + length));
-    spansX = newSpansX;
+    spans[X_AXIS] = newSpansX;
 
     // The spansY cache.
-    int[] newSpansY = new int[oldSize - length + views.length];
-    System.arraycopy(spansY, 0, newSpansY, 0, offset);
-    System.arraycopy(spansY, offset + length, newSpansY,
-                     offset + views.length,
+    int[] newSpansY = new int[oldSize - length + numViews];
+    System.arraycopy(spans[Y_AXIS], 0, newSpansY, 0, offset);
+    System.arraycopy(spans[Y_AXIS], offset + length, newSpansY,
+                     offset + numViews,
                      oldSize - (offset + length));
-    spansY = newSpansY;
+    spans[Y_AXIS] = newSpansY;
 
     // The offsetsX cache.
-    int[] newOffsetsX = new int[oldSize - length + views.length];
-    System.arraycopy(offsetsX, 0, newOffsetsX, 0, offset);
-    System.arraycopy(offsetsX, offset + length, newOffsetsX,
-                     offset + views.length,
+    int[] newOffsetsX = new int[oldSize - length + numViews];
+    System.arraycopy(offsets[X_AXIS], 0, newOffsetsX, 0, offset);
+    System.arraycopy(offsets[X_AXIS], offset + length, newOffsetsX,
+                     offset + numViews,
                      oldSize - (offset + length));
-    offsetsX = newOffsetsX;
+    offsets[X_AXIS] = newOffsetsX;
 
     // The offsetsY cache.
-    int[] newOffsetsY = new int[oldSize - length + views.length];
-    System.arraycopy(offsetsY, 0, newOffsetsY, 0, offset);
-    System.arraycopy(offsetsY, offset + length, newOffsetsY,
-                     offset + views.length,
+    int[] newOffsetsY = new int[oldSize - length + numViews];
+    System.arraycopy(offsets[Y_AXIS], 0, newOffsetsY, 0, offset);
+    System.arraycopy(offsets[Y_AXIS], offset + length, newOffsetsY,
+                     offset + numViews,
                      oldSize - (offset + length));
-    offsetsY = newOffsetsY;
+    offsets[Y_AXIS] = newOffsetsY;
 
     // Actually perform the replace.
     super.replace(offset, length, views);
@@ -323,9 +293,16 @@
    */
   public float getPreferredSpan(int axis)
   {
-    SizeRequirements sr = new SizeRequirements();
-    int pref = baselineRequirements(axis, sr).preferred;
-    return (float) pref;
+    if (!isLayoutValid(axis))
+      {
+        if (axis == myAxis)
+          requirements[axis] = calculateMajorAxisRequirements(axis,
+                                                           requirements[axis]);
+        else
+          requirements[axis] = calculateMinorAxisRequirements(axis,
+                                                           requirements[axis]);
+      }
+    return requirements[axis].preferred;
   }
 
   public float getMaximumSpan(int axis)
@@ -337,8 +314,9 @@
   }
 
   /**
-   * Calculates the size requirements for this <code>BoxView</code> along
-   * the specified axis.
+   * This method is obsolete and no longer in use. It is replaced by
+   * [EMAIL PROTECTED] #calculateMajorAxisRequirements(int, SizeRequirements)} and
+   * [EMAIL PROTECTED] #calculateMinorAxisRequirements(int, SizeRequirements)}.
    *
    * @param axis the axis that is examined
    * @param sr the <code>SizeRequirements</code> object to hold the result,
@@ -350,12 +328,8 @@
   protected SizeRequirements baselineRequirements(int axis,
                                                   SizeRequirements sr)
   {
-    SizeRequirements result;
-    if (axis == myAxis)
-      result = calculateMajorAxisRequirements(axis, sr);
-    else
-      result = calculateMinorAxisRequirements(axis, sr);
-    return result;
+    SizeRequirements[] childReqs = getChildRequirements(axis);
+    return SizeRequirements.getAlignedSizeRequirements(childReqs);
   }
 
   /**
@@ -370,10 +344,12 @@
   protected void baselineLayout(int span, int axis, int[] offsets,
                                 int[] spans)
   {
-    if (axis == myAxis)
-      layoutMajorAxis(span, axis, offsets, spans);
-    else
-      layoutMinorAxis(span, axis, offsets, spans);
+    SizeRequirements[] childReqs = getChildRequirements(axis);
+    // Calculate the spans and offsets using the SizeRequirements uility
+    // methods.
+    SizeRequirements.calculateAlignedPositions(span, requirements[axis],
+                                               childReqs, offsets, spans);
+    layoutValid[axis] = true;
   }
 
   /**
@@ -412,6 +388,7 @@
     SizeRequirements[] childReqs = getChildRequirements(axis);
     return SizeRequirements.getAlignedSizeRequirements(childReqs);
   }
+  
 
   /**
    * Returns <code>true</code> if the specified point lies before the
@@ -511,10 +488,10 @@
     if (! isAllocationValid())
       layout(a.width, a.height);
 
-    a.x += offsetsX[index];
-    a.y += offsetsY[index];
-    a.width = spansX[index];
-    a.height = spansY[index];
+    a.x += offsets[X_AXIS][index];
+    a.y += offsets[Y_AXIS][index];
+    a.width = spans[X_AXIS][index];
+    a.height = spans[Y_AXIS][index];
   }
 
   /**
@@ -528,8 +505,35 @@
    */
   protected void layout(int width, int height)
   {
-    baselineLayout(width, X_AXIS, offsetsX, spansX);
-    baselineLayout(height, Y_AXIS, offsetsY, spansY);
+    int[] newSpan = new int[]{ width, height };
+
+    // Update major axis as appropriate.
+    if (! isLayoutValid(myAxis) || newSpan[myAxis] != span[myAxis])
+      {
+        requirements[myAxis] = calculateMajorAxisRequirements(myAxis,
+                                                         requirements[myAxis]);
+        layoutMajorAxis(newSpan[myAxis], myAxis, offsets[myAxis],
+                        spans[myAxis]);
+        span[myAxis] = newSpan[myAxis];
+      }
+
+    // Update minor axis as appropriate.
+    int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS;
+    if (! isLayoutValid(minorAxis))
+      {
+        requirements[minorAxis] = calculateMinorAxisRequirements(minorAxis,
+                                                      requirements[minorAxis]);
+        layoutMinorAxis(newSpan[minorAxis], minorAxis, offsets[minorAxis],
+                        spans[minorAxis]);
+        span[myAxis] = newSpan[myAxis];
+      }
+
+    // Update the child view's sizes.
+    int count = getViewCount();
+    for (int i = 0; i < count; ++i)
+      {
+        getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+      }
   }
 
   /**
@@ -549,7 +553,7 @@
     // methods.
     SizeRequirements.calculateTiledPositions(targetSpan, null, childReqs,
                                              offsets, spans);
-    validateLayout(axis);
+    layoutValid[axis] = true;
   }
 
   /**
@@ -567,15 +571,9 @@
     SizeRequirements[] childReqs = getChildRequirements(axis);
     // Calculate the spans and offsets using the SizeRequirements uility
     // methods.
-    // TODO: This might be an opportunity for performance optimization. Here
-    // we could use a cached instance of SizeRequirements instead of passing
-    // null to baselineRequirements. However, this would involve rewriting
-    // the baselineRequirements() method to not use the SizeRequirements
-    // utility method, since they cannot reuse a cached instance.
-    SizeRequirements total = baselineRequirements(axis, null);
-    SizeRequirements.calculateAlignedPositions(targetSpan, total, childReqs,
-                                               offsets, spans);
-    validateLayout(axis);
+    SizeRequirements.calculateAlignedPositions(targetSpan, requirements[axis],
+                                               childReqs, offsets, spans);
+    layoutValid[axis] = true;
   }
 
   /**
@@ -597,7 +595,7 @@
    */
   public int getWidth()
   {
-    return width;
+    return span[X_AXIS];
   }
 
   /**
@@ -607,7 +605,7 @@
    */
   public int getHeight()
   {
-    return height;
+    return span[Y_AXIS];
   }
 
   /**
@@ -619,31 +617,7 @@
    */
   public void setSize(float width, float height)
   {
-    if (this.width != (int) width)
-      layoutChanged(X_AXIS);
-    if (this.height != (int) height)
-      layoutChanged(Y_AXIS);
-    
-    this.width = (int) width;
-    this.height = (int) height;
-
-    Rectangle outside = new Rectangle(0, 0, this.width, this.height);
-    Rectangle inside = getInsideAllocation(outside);
-    if (!isAllocationValid())
-      layout(inside.width, inside.height);
-  }
-
-  /**
-   * Sets the layout to valid for a specific axis.
-   *
-   * @param axis the axis for which to validate the layout
-   */
-  void validateLayout(int axis)
-  {
-    if (axis == X_AXIS)
-      xLayoutValid = true;
-    if (axis == Y_AXIS)
-      yLayoutValid = true;
+    layout((int) width, (int) height);
   }
 
   /**
@@ -653,7 +627,7 @@
    * @return the size requirements of this view's children for the major
    *         axis
    */
-  SizeRequirements[] getChildRequirements(int axis)
+  private SizeRequirements[] getChildRequirements(int axis)
   {
     // Allocate SizeRequirements for each child view.
     int count = getViewCount();
@@ -682,10 +656,9 @@
    */
   protected int getSpan(int axis, int childIndex)
   {
-    if (axis == X_AXIS)
-      return spansX[childIndex];
-    else
-      return spansY[childIndex];
+    if (axis != X_AXIS && axis != Y_AXIS)
+      throw new IllegalArgumentException("Illegal axis argument");
+    return spans[axis][childIndex];
   }
 
   /**
@@ -701,10 +674,9 @@
    */
   protected int getOffset(int axis, int childIndex)
   {
-    if (axis == X_AXIS)
-      return offsetsX[childIndex];
-    else
-      return offsetsY[childIndex];
+    if (axis != X_AXIS && axis != Y_AXIS)
+      throw new IllegalArgumentException("Illegal axis argument");
+    return offsets[axis][childIndex];
   }
 
   /**
@@ -719,10 +691,17 @@
    */
   public float getAlignment(int axis)
   {
+    float align;
     if (axis == myAxis)
-      return 0.5F;
+      align = 0.5F;
     else
-      return baselineRequirements(axis, null).alignment;
+      {
+        if (! isLayoutValid(axis))
+          requirements[axis] = calculateMinorAxisRequirements(axis,
+                                                           requirements[axis]);
+        align = requirements[axis].alignment;
+      }
+    return align;
   }
   
   /**
@@ -735,9 +714,9 @@
   public void preferenceChanged (View child, boolean width, boolean height)
   {
     if (width)
-      xLayoutValid = false;
+      layoutValid[X_AXIS] = false;
     if (height)
-      yLayoutValid = false;
+      layoutValid[Y_AXIS] = false;
     super.preferenceChanged(child, width, height);
   }
   
Index: javax/swing/text/FlowView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/FlowView.java,v
retrieving revision 1.7
diff -u -r1.7 FlowView.java
--- javax/swing/text/FlowView.java	23 Nov 2005 11:59:30 -0000	1.7
+++ javax/swing/text/FlowView.java	9 Feb 2006 14:27:58 -0000
@@ -45,7 +45,6 @@
 import java.util.Iterator;
 import java.util.Vector;
 
-import javax.swing.SwingConstants;
 import javax.swing.event.DocumentEvent;
 
 /**
@@ -89,7 +88,7 @@
      */
     public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
     {
-      layout(fv);
+      // The default implementation does nothing.
     }
 
     /**
@@ -105,7 +104,7 @@
      */
     public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
     {
-      layout(fv);
+      // The default implementation does nothing.
     }
 
     /**
@@ -121,7 +120,7 @@
      */
     public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
     {
-      layout(fv);
+      // The default implementation does nothing.
     }
 
     /**
@@ -166,41 +165,55 @@
      * Lays out one row of the flow view. This is called by [EMAIL PROTECTED] #layout}
      * to fill one row with child views until the available span is exhausted.
      *
+     * The default implementation fills the row by calling
+     * [EMAIL PROTECTED] #createView(FlowView, int, int, int)} until the available space
+     * is exhausted, a forced break is encountered or there are no more views
+     * in the logical view. If the available space is exhausted,
+     * [EMAIL PROTECTED] #adjustRow(FlowView, int, int, int)} is called to fit the row
+     * into the available span.
+     *
      * @param fv the flow view for which we perform the layout
      * @param rowIndex the index of the row
-     * @param pos the start position for the row
+     * @param pos the model position for the beginning of the row
      *
      * @return the start position of the next row
      */
     protected int layoutRow(FlowView fv, int rowIndex, int pos)
     {
-      int spanLeft = fv.getFlowSpan(rowIndex);
-      if (spanLeft <= 0)
-        return -1;
-
-      int offset = pos;
       View row = fv.getView(rowIndex);
-      int flowAxis = fv.getFlowAxis();
+      int axis = fv.getFlowAxis();
+      int span = fv.getFlowSpan(rowIndex);
+      int x = fv.getFlowStart(rowIndex);
+      int offset = pos;
+      View logicalView = getLogicalView(fv);
+      // Special case when span == 0. We need to layout the row as if it had
+      // a span of Integer.MAX_VALUE.
+      if (span == 0)
+        span = Integer.MAX_VALUE;
 
-      while (spanLeft > 0)
+      while (span > 0)
         {
-          View child = createView(fv, offset, spanLeft, rowIndex);
-          if (child == null)
-            {
-              offset = -1;
-              break;
-            }
-
-          int span = (int) child.getPreferredSpan(flowAxis);
-          if (span > spanLeft)
-            {
-              offset = -1;
-              break;
-            }
-
-          row.append(child);
-          spanLeft -= span;
-          offset = child.getEndOffset();
+          if (logicalView.getViewIndex(offset, Position.Bias.Forward) == -1)
+            break;
+          View view = createView(fv, offset, span, rowIndex);
+          if (view == null)
+            break;
+          int viewSpan = (int) view.getPreferredSpan(axis);
+          row.append(view);
+          int breakWeight = view.getBreakWeight(axis, x, span);
+          if (breakWeight >= View.ForcedBreakWeight)
+            break;
+          x += viewSpan;
+          span -= viewSpan;
+          offset += (view.getEndOffset() - view.getStartOffset());
+        }
+      if (span < 0)
+        {
+          int flowStart = fv.getFlowStart(axis);
+          int flowSpan = fv.getFlowSpan(axis);
+          adjustRow(fv, rowIndex, flowSpan, flowStart);
+          int rowViewCount = row.getViewCount();
+          offset = row.getView(rowViewCount - 1).getEndOffset();
         }
       return offset;
     }
@@ -212,36 +225,89 @@
      * available span and can be broken down) or <code>null</code> (if it does
      * not fit in the available span and also cannot be broken down).
      *
+     * The default implementation fetches the logical view at the specified
+     * <code>startOffset</code>. If that view has a different startOffset than
+     * specified in the argument, a fragment is created using
+     * [EMAIL PROTECTED] View#createFragment(int, int)} that has the correct startOffset
+     * and the logical view's endOffset.
+     *
      * @param fv the flow view
-     * @param offset the start offset for the view to be created
+     * @param startOffset the start offset for the view to be created
      * @param spanLeft the available span
      * @param rowIndex the index of the row
      *
      * @return a view to fill the row with, or <code>null</code> if there
      *         is no view or view fragment that fits in the available span
      */
-    protected View createView(FlowView fv, int offset, int spanLeft,
+    protected View createView(FlowView fv, int startOffset, int spanLeft,
                               int rowIndex)
     {
-      // Find the logical element for the given offset.
-      View logicalView = getLogicalView(fv);
+       View logicalView = getLogicalView(fv);
+       // FIXME: Handle the bias thing correctly.
+       int index = logicalView.getViewIndex(startOffset, Position.Bias.Forward);
+       View retVal = null;
+       if (index >= 0)
+         {
+           retVal = logicalView.getView(index);
+           if (retVal.getStartOffset() != startOffset)
+             retVal = retVal.createFragment(startOffset, retVal.getEndOffset());
+         }
+       return retVal;
+    }
 
-      int viewIndex = logicalView.getViewIndex(offset, Position.Bias.Forward);
-      if (viewIndex == -1)
-        return null;
-
-      View child = logicalView.getView(viewIndex);
-      int flowAxis = fv.getFlowAxis();
-      int span = (int) child.getPreferredSpan(flowAxis);
-
-      if (span <= spanLeft)
-        return child;
-      else if (child.getBreakWeight(flowAxis, offset, spanLeft)
-               > BadBreakWeight)
-        // FIXME: What to do with the pos parameter here?
-        return child.breakView(flowAxis, offset, 0, spanLeft);
-      else
-        return null;
+    /**
+     * Tries to adjust the specified row to fit within the desired span. The
+     * default implementation iterates through the children of the specified
+     * row to find the view that has the highest break weight and - if there
+     * is more than one view with such a break weight - which is nearest to
+     * the end of the row. If there is such a view that has a break weight >
+     * [EMAIL PROTECTED] View#BadBreakWeight}, this view is broken using the
+     * [EMAIL PROTECTED] View#breakView(int, int, float, float)} method and this view and
+     * all views after the now broken view are replaced by the broken view.
+     *
+     * @param fv the flow view
+     * @param rowIndex the index of the row to be adjusted
+     * @param desiredSpan the layout span
+     * @param x the X location at which the row starts
+     */
+    protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
+      // Determine the last view that has the highest break weight.
+      int axis = fv.getFlowAxis();
+      View row = fv.getView(rowIndex);
+      int count = row.getViewCount();
+      int breakIndex = -1;
+      int maxBreakWeight = View.BadBreakWeight;
+      int breakX = x;
+      int breakSpan = desiredSpan;
+      int currentX = x;
+      int currentSpan = desiredSpan;
+      for (int i = 0; i < count; ++i)
+        {
+          View view = row.getView(i);
+          int weight = view.getBreakWeight(axis, currentX, currentSpan);
+          if (weight >= maxBreakWeight)
+            {
+              breakIndex = i;
+              breakX = currentX;
+              breakSpan = currentSpan;
+              maxBreakWeight = weight;
+            }
+          int size = (int) view.getPreferredSpan(axis);
+          currentX += size;
+          currentSpan -= size;
+        }
+
+      // If there is a potential break location found, break the row at
+      // this location.
+      if (breakIndex > -1)
+        {
+          View toBeBroken = row.getView(breakIndex);
+          View brokenView = toBeBroken.breakView(axis,
+                                                 toBeBroken.getStartOffset(),
+                                                 breakX, breakSpan);
+          row.replace(breakIndex, count - breakIndex,
+                      new View[]{brokenView});
+        }
     }
   }
 
@@ -342,8 +408,7 @@
       for (Iterator it = children.iterator(); it.hasNext(); i++)
         {
           View child = (View) it.next();
-          if (child.getStartOffset() >= pos
-              && child.getEndOffset() < pos)
+          if (pos >= child.getStartOffset() && pos < child.getEndOffset())
             {
               index = i;
               break;
@@ -424,6 +489,11 @@
   protected FlowStrategy strategy;
 
   /**
+   * Indicates if the flow should be rebuild during the next layout.
+   */
+  private boolean layoutDirty;
+
+  /**
    * Creates a new <code>FlowView</code> for the given
    * <code>Element</code> and <code>axis</code>.
    *
@@ -436,6 +506,7 @@
   {
     super(element, axis);
     strategy = sharedStrategy;
+    layoutDirty = true;
   }
 
   /**
@@ -511,7 +582,7 @@
     if (layoutPool == null)
       {
         layoutPool = new LogicalView(getElement());
-
+        layoutPool.setParent(this);
         Element el = getElement();
         int count = el.getElementCount();
         for (int i = 0; i < count; ++i)
@@ -534,27 +605,32 @@
    */
   protected void layout(int width, int height)
   {
-    boolean rebuild = false;
-
     int flowAxis = getFlowAxis();
     if (flowAxis == X_AXIS)
       {
-        rebuild = !(width == layoutSpan);
-        layoutSpan = width;
+        if (layoutSpan != width)
+          {
+            layoutChanged(Y_AXIS);
+            layoutSpan = width;
+          }
       }
     else
       {
-        rebuild = !(height == layoutSpan);
-        layoutSpan = height;
+        if (layoutSpan != height)
+          {
+            layoutChanged(X_AXIS);
+            layoutSpan = height;
+          }
       }
 
-    if (rebuild)
-      strategy.layout(this);
+    if (layoutDirty)
+      {
+        strategy.layout(this);
+        layoutDirty = false;
+      }
 
-    // TODO: If the span along the box axis has changed in the process of
-    // relayouting the rows (that is, if rows have been added or removed),
-    // call preferenceChanged in order to throw away cached layout information
-    // of the surrounding BoxView.
+    if (getPreferredSpan(getAxis()) != height)
+      preferenceChanged(this, false, true);
 
     super.layout(width, height);
   }
@@ -574,6 +650,7 @@
     // be updated accordingly.
     layoutPool.insertUpdate(changes, a, vf);
     strategy.insertUpdate(this, changes, getInsideAllocation(a));
+    layoutDirty = true;
   }
 
   /**
@@ -588,6 +665,7 @@
   public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
   {
     strategy.removeUpdate(this, changes, getInsideAllocation(a));
+    layoutDirty = true;
   }
 
   /**
@@ -602,6 +680,7 @@
   public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
   {
     strategy.changedUpdate(this, changes, getInsideAllocation(a));
+    layoutDirty = true;
   }
 
   /**
Index: javax/swing/text/GlyphView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/GlyphView.java,v
retrieving revision 1.15
diff -u -r1.15 GlyphView.java
--- javax/swing/text/GlyphView.java	7 Feb 2006 15:54:03 -0000	1.15
+++ javax/swing/text/GlyphView.java	9 Feb 2006 14:27:58 -0000
@@ -567,7 +567,7 @@
                                tabEx, 0.F);
       }
     else
-      span = painter.getHeight(this); 
+      span = painter.getHeight(this);
     return span;
   }
 
@@ -936,23 +936,24 @@
       weight = super.getBreakWeight(axis, pos, len);
     else
       {
-        // Determine the model locations at pos and pos + len.
-        int spanX = (int) getPreferredSpan(X_AXIS);
-        int spanY = (int) getPreferredSpan(Y_AXIS);
-        Rectangle dummyAlloc = new Rectangle(0, 0, spanX, spanY);
-        Position.Bias[] biasRet = new Position.Bias[1];
-        int offset1 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet);
-        int offset2 = viewToModel(pos, spanY / 2, dummyAlloc, biasRet);
-        Segment txt = getText(offset1, offset2);
-        BreakIterator lineBreaker = BreakIterator.getLineInstance();
-        lineBreaker.setText(txt);
-        int breakLoc = lineBreaker.previous();
-        if (breakLoc == offset1)
-          weight = View.BadBreakWeight;
-        else if(breakLoc ==  BreakIterator.DONE)
-          weight = View.GoodBreakWeight;
-        else
-          weight = View.ExcellentBreakWeight;
+        // FIXME: Commented out because the Utilities.getBreakLocation method
+        // is still buggy. The GoodBreakWeight is a reasonable workaround for
+        // now.
+//        int startOffset = getStartOffset();
+//        int endOffset = getEndOffset() - 1;
+//        Segment s = getText(startOffset, endOffset);
+//        Container c = getContainer();
+//        FontMetrics fm = c.getFontMetrics(c.getFont());
+//        int x0 = (int) pos;
+//        int x = (int) (pos + len);
+//        int breakLoc = Utilities.getBreakLocation(s, fm, x0, x,
+//                                                  getTabExpander(),
+//                                                  startOffset);
+//        if (breakLoc == startOffset || breakLoc == endOffset)
+//          weight = GoodBreakWeight;
+//        else
+//          weight = ExcellentBreakWeight;
+        weight = GoodBreakWeight;
       }
     return weight;
   }
@@ -975,8 +976,8 @@
   /**
    * Receives notification that some text has been inserted within the
    * text fragment that this view is responsible for. This calls
-   * [EMAIL PROTECTED] View#preferenceChanged(View, boolean, boolean)} on the parent for
-   * width.
+   * [EMAIL PROTECTED] View#preferenceChanged(View, boolean, boolean)} for the
+   * direction in which the glyphs are rendered.
    *
    * @param e the document event describing the change; not used here
    * @param a the view allocation on screen; not used here
@@ -984,7 +985,7 @@
    */
   public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
   {
-    getParent().preferenceChanged(this, true, false);
+    preferenceChanged(this, true, false);
   }
 
   /**
Index: javax/swing/text/ParagraphView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/ParagraphView.java,v
retrieving revision 1.4
diff -u -r1.4 ParagraphView.java
--- javax/swing/text/ParagraphView.java	17 Nov 2005 20:31:51 -0000	1.4
+++ javax/swing/text/ParagraphView.java	9 Feb 2006 14:27:58 -0000
@@ -63,11 +63,16 @@
     {
       super(el, X_AXIS);
     }
+
     public float getAlignment(int axis)
     {
       // FIXME: This is very likely not 100% correct. Work this out.
       return 0.0F;
     }
+    protected void loadChildren(ViewFactory vf)
+    {
+      // Do nothing here. The children are added while layouting.
+    }
   }
 
   /**
Index: javax/swing/text/View.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/View.java,v
retrieving revision 1.26
diff -u -r1.26 View.java
--- javax/swing/text/View.java	7 Feb 2006 13:54:48 -0000	1.26
+++ javax/swing/text/View.java	9 Feb 2006 14:27:58 -0000
@@ -100,27 +100,65 @@
     return elt;
   }
 
+  /**
+   * Returns the preferred span along the specified axis. Normally the view is
+   * rendered with the span returned here if that is possible.
+   *
+   * @param axis the axis
+   *
+   * @return the preferred span along the specified axis
+   */
   public abstract float getPreferredSpan(int axis);
 
+  /**
+   * Returns the resize weight of this view. A value of <code>0</code> or less
+   * means this view is not resizeable. Positive values make the view
+   * resizeable. The default implementation returns <code>0</code>
+   * unconditionally.
+   *
+   * @param axis the axis
+   *
+   * @return the resizability of this view along the specified axis
+   */
   public int getResizeWeight(int axis)
   {
     return 0;
   }
 
+  /**
+   * Returns the maximum span along the specified axis. The default
+   * implementation will forward to
+   * [EMAIL PROTECTED] #getPreferredSpan(int)} unless [EMAIL PROTECTED] #getResizeWeight(int)}
+   * returns a value > 0, in which case this returns [EMAIL PROTECTED] Integer#MIN_VALUE}.
+   *
+   * @param axis the axis
+   *
+   * @return the maximum span along the specified axis
+   */
   public float getMaximumSpan(int axis)
   {
+    float max = Integer.MAX_VALUE;
     if (getResizeWeight(axis) <= 0)
-      return getPreferredSpan(axis);
-
-    return Integer.MAX_VALUE;
+      max = getPreferredSpan(axis);
+    return max;
   }
 
+  /**
+   * Returns the minimum span along the specified axis. The default
+   * implementation will forward to
+   * [EMAIL PROTECTED] #getPreferredSpan(int)} unless [EMAIL PROTECTED] #getResizeWeight(int)}
+   * returns a value > 0, in which case this returns <code>0</code>.
+   *
+   * @param axis the axis
+   *
+   * @return the minimum span along the specified axis
+   */
   public float getMinimumSpan(int axis)
   {
+    float min = 0;
     if (getResizeWeight(axis) <= 0)
-      return getPreferredSpan(axis);
-
-    return Integer.MAX_VALUE;
+      min = getPreferredSpan(axis);
+    return min;
   }
   
   public void setSize(float width, float height)
@@ -128,6 +166,20 @@
     // The default implementation does nothing.
   }
   
+  /**
+   * Returns the alignment of this view along the baseline of the parent view.
+   * An alignment of <code>0.0</code> will align this view with the left edge
+   * along the baseline, an alignment of <code>0.5</code> will align it
+   * centered to the baseline, an alignment of <code>1.0</code> will align
+   * the right edge along the baseline.
+   *
+   * The default implementation returns 0.5 unconditionally.
+   *
+   * @param axis the axis
+   *
+   * @return the alignment of this view along the parents baseline for the
+   *         specified axis
+   */
   public float getAlignment(int axis)
   {
     return 0.5f;
@@ -159,6 +211,15 @@
     return parent != null ? parent.getViewFactory() : null;
   }
 
+  /**
+   * Replaces a couple of child views with new child views. If
+   * <code>length == 0</code> then this is a simple insertion, if
+   * <code>views == null</code> this only removes some child views.
+   *
+   * @param offset the offset at which to replace
+   * @param length the number of child views to be removed
+   * @param views the new views to be inserted, may be <code>null</code>
+   */
   public void replace(int offset, int length, View[] views)
   {
     // Default implementation does nothing.
@@ -407,25 +468,32 @@
                                DocumentEvent ev, Shape shape, ViewFactory vf)
   {
     int count = getViewCount();
-    int index = -1;
-    int addLength = -1;
-    if (ec != null)
+    if (count > 0)
       {
-        index = ec.getIndex();
-        addLength = ec.getChildrenAdded().length;
-      }
+        int startOffset = ev.getOffset();
+        int endOffset = startOffset + ev.getLength();
+        // FIXME: What about this bias stuff?
+        int startIndex = getViewIndex(startOffset, Position.Bias.Forward);
+        int endIndex = getViewIndex(endOffset, Position.Bias.Forward);
+        int index = -1;
+        int addLength = -1;
+        if (ec != null)
+          {
+            index = ec.getIndex();
+            addLength = ec.getChildrenAdded().length;
+          }
 
-    for (int i = 0; i < count; i++)
-      {
-        // Skip newly added child views.
-        if (index >= 0 && index == i && addLength > 0)
+        if (startIndex >= 0 && endIndex >= 0)
           {
-            // We add addLengt - 1 here because the for loop does add 1 more.
-            i += addLength - 1;
-            continue;
+            for (int i = startIndex; i <= endIndex; i++)
+              {
+                // Skip newly added child views.
+                if (index >= 0 && i >= index && i < (index+addLength))
+                  continue;
+                View child = getView(i);
+                forwardUpdateToView(child, ev, shape, vf);
+              }
           }
-        View child = getView(i);
-        forwardUpdateToView(child, ev, shape, vf);
       }
   }
 

Reply via email to